diff options
author | Mary Ruthven <mruthven@chromium.org> | 2021-01-05 13:35:59 -0800 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2021-01-06 21:56:53 +0000 |
commit | 7103bc89ed2a4460a606b62b63df02e7f0ecb3df (patch) | |
tree | 0634b852aff059895a6ced19dcdb52c6570ec5c2 | |
parent | 2495bc0a9744f9733c59daeeb81c8be3c54243d8 (diff) | |
download | chrome-ec-7103bc89ed2a4460a606b62b63df02e7f0ecb3df.tar.gz |
coil: remove usbc, usb_pd, charge_manager, and tcpm
This code uses coil terms we're removing, but we don't use it in
platform/cr50. Remove the code instead of replacing the terms.
BUG=b:175244613
TEST=make buildall -j
Change-Id: Ia64e1ff4df941d2fe19e95e84dee8b743616aa88
Signed-off-by: Mary Ruthven <mruthven@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2613135
Reviewed-by: Namyoon Woo <namyoon@chromium.org>
61 files changed, 0 insertions, 36085 deletions
diff --git a/board/host/build.mk b/board/host/build.mk index 241f197342..78e4df530c 100644 --- a/board/host/build.mk +++ b/board/host/build.mk @@ -12,4 +12,3 @@ board-y=board.o board-$(HAS_TASK_CHIPSET)+=chipset.o board-$(CONFIG_BATTERY_MOCK)+=battery.o charger.o board-$(CONFIG_FANS)+=fan.o -board-$(CONFIG_USB_POWER_DELIVERY)+=usb_pd_policy.o usb_pd_config.o diff --git a/board/host/usb_pd_config.c b/board/host/usb_pd_config.c deleted file mode 100644 index 91c30d1755..0000000000 --- a/board/host/usb_pd_config.c +++ /dev/null @@ -1,35 +0,0 @@ -/* 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. - */ - -/* USB Power delivery board configuration */ - -#include "test_util.h" - -test_mockable void pd_select_polarity(int port, int polarity) -{ - /* Not implemented */ -} - -test_mockable void pd_tx_init(void) -{ - /* Not implemented */ -} - -test_mockable void pd_set_host_mode(int port, int enable) -{ - /* Not implemented */ -} - -test_mockable void pd_config_init(int port, uint8_t power_role) -{ - /* Not implemented */ -} - -test_mockable int pd_adc_read(int port, int cc) -{ - /* Not implemented */ - return 0; -} - diff --git a/board/host/usb_pd_config.h b/board/host/usb_pd_config.h deleted file mode 100644 index fb12b2ce7d..0000000000 --- a/board/host/usb_pd_config.h +++ /dev/null @@ -1,24 +0,0 @@ -/* 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. - */ - -/* USB Power delivery board configuration */ - -#ifndef __CROS_EC_USB_PD_CONFIG_H -#define __CROS_EC_USB_PD_CONFIG_H - -/* Use software CRC */ -#define CONFIG_SW_CRC - -void pd_select_polarity(int port, int polarity); - -void pd_tx_init(void); - -void pd_set_host_mode(int port, int enable); - -void pd_config_init(int port, uint8_t power_role); - -int pd_adc_read(int port, int cc); - -#endif /* __CROS_EC_USB_PD_CONFIG_H */ diff --git a/board/host/usb_pd_policy.c b/board/host/usb_pd_policy.c deleted file mode 100644 index 0dcc5942a8..0000000000 --- a/board/host/usb_pd_policy.c +++ /dev/null @@ -1,96 +0,0 @@ -/* 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 "common.h" -#include "console.h" -#include "usb_pd.h" -#include "util.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_DUAL_ROLE | PDO_FIXED_DATA_SWAP) - -const uint32_t pd_src_pdo[] = { - PDO_FIXED(5000, 900, PDO_FIXED_FLAGS), - PDO_FIXED(12000, 3000, PDO_FIXED_FLAGS), -}; -const int pd_src_pdo_cnt = ARRAY_SIZE(pd_src_pdo); - -const uint32_t pd_snk_pdo[] = { - PDO_FIXED(5000, 500, PDO_FIXED_FLAGS), - PDO_BATT(4750, 21000, 15000), - PDO_VAR(4750, 21000, 3000), -}; -const int pd_snk_pdo_cnt = ARRAY_SIZE(pd_snk_pdo); - -int pd_is_valid_input_voltage(int mv) -{ - /* Any voltage less than the max is allowed */ - return 1; -} - -void pd_transition_voltage(int idx) -{ - /* Not implemented */ -} - -int pd_set_power_supply_ready(int port) -{ - /* Not implemented */ - return EC_SUCCESS; -} - -void pd_power_supply_reset(int port) -{ - /* Not implemented */ -} - -void pd_set_input_current_limit(int port, uint32_t max_ma, - uint32_t supply_voltage) -{ - /* Not implemented */ -} - -test_mockable int pd_snk_is_vbus_provided(int port) -{ - /* Not implemented */ - return 1; -} - -int pd_board_checks(void) -{ - return EC_SUCCESS; -} - -int pd_check_power_swap(int port) -{ - /* Always allow power swap */ - return 1; -} - -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_custom_vdm(int port, int cnt, uint32_t *payload, uint32_t **rpayload) -{ - return 0; -} diff --git a/chip/host/build.mk b/chip/host/build.mk index f018c2a281..3b71e676a4 100644 --- a/chip/host/build.mk +++ b/chip/host/build.mk @@ -13,7 +13,6 @@ chip-y=clock.o flash.o gpio.o i2c.o lpc.o persistence.o reboot.o registers.o \ ifndef CONFIG_KEYBOARD_NOT_RAW chip-$(HAS_TASK_KEYSCAN)+=keyboard_raw.o endif -chip-$(CONFIG_USB_PD_TCPC)+=usb_pd_phy.o ifeq ($(CONFIG_DCRYPTO),y) CPPFLAGS += -I$(abspath ./chip/g) diff --git a/chip/host/usb_pd_phy.c b/chip/host/usb_pd_phy.c deleted file mode 100644 index 347035a6d0..0000000000 --- a/chip/host/usb_pd_phy.c +++ /dev/null @@ -1,370 +0,0 @@ -/* 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 "common.h" -#include "console.h" -#include "crc.h" -#include "task.h" -#include "usb_pd.h" -#include "usb_pd_config.h" -#include "util.h" - -#define PREAMBLE_OFFSET 60 /* Any number should do */ - -/* - * Maximum size of a Power Delivery packet (in bits on the wire) : - * 16-bit header + 0..7 32-bit data objects (+ 4b5b encoding) - * 64-bit preamble + SOP (4x 5b) + message in 4b5b + 32-bit CRC + EOP (1x 5b) - * = 64 + 4*5 + 16 * 5/4 + 7 * 32 * 5/4 + 32 * 5/4 + 5 - */ -#define PD_BIT_LEN 429 - -static struct pd_physical { - int hw_init_done; - - uint8_t bits[PD_BIT_LEN]; - int total; - int has_preamble; - int rx_started; - int rx_monitoring; - - int preamble_written; - int has_msg; - int last_edge_written; - uint8_t out_msg[PD_BIT_LEN / 5]; - int verified_idx; -} pd_phy[CONFIG_USB_PD_PORT_MAX_COUNT]; - -static const uint16_t enc4b5b[] = { - 0x1E, 0x09, 0x14, 0x15, 0x0A, 0x0B, 0x0E, 0x0F, 0x12, 0x13, 0x16, - 0x17, 0x1A, 0x1B, 0x1C, 0x1D}; - -/* Test utilities */ -static void pd_test_reset_phy(int port) -{ - int i; - int enc_len = PD_BIT_LEN / 5; - - for (i = 0; i < PD_BIT_LEN; i++) - pd_phy[port].bits[i] = 0; - - for (i = 0; i < enc_len; i++) - pd_phy[port].out_msg[i] = 0; - - pd_phy[port].total = 0; - pd_phy[port].has_preamble = 0; - pd_phy[port].rx_started = 0; - pd_phy[port].rx_monitoring = 0; - pd_phy[port].preamble_written = 0; - pd_phy[port].has_msg = 0; - pd_phy[port].last_edge_written = 0; - pd_phy[port].verified_idx = 0; -} - -void pd_test_rx_set_preamble(int port, int has_preamble) -{ - pd_phy[port].total = 0; - pd_phy[port].has_preamble = has_preamble; -} - -void pd_test_rx_msg_append_bits(int port, uint32_t bits, int nb) -{ - int i; - - for (i = 0; i < nb; ++i) { - pd_phy[port].bits[pd_phy[port].total++] = bits & 1; - bits >>= 1; - } -} - -void pd_test_rx_msg_append_kcode(int port, uint8_t kcode) -{ - pd_test_rx_msg_append_bits(port, kcode, 5); -} - -void pd_test_rx_msg_append_sop(int port) -{ - pd_test_rx_msg_append_kcode(port, PD_SYNC1); - pd_test_rx_msg_append_kcode(port, PD_SYNC1); - pd_test_rx_msg_append_kcode(port, PD_SYNC1); - pd_test_rx_msg_append_kcode(port, PD_SYNC2); -} - -void pd_test_rx_msg_append_sop_prime(int port) -{ - pd_test_rx_msg_append_kcode(port, PD_SYNC1); - pd_test_rx_msg_append_kcode(port, PD_SYNC1); - pd_test_rx_msg_append_kcode(port, PD_SYNC3); - pd_test_rx_msg_append_kcode(port, PD_SYNC3); -} - -void pd_test_rx_msg_append_sop_prime_prime(int port) -{ - pd_test_rx_msg_append_kcode(port, PD_SYNC1); - pd_test_rx_msg_append_kcode(port, PD_SYNC3); - pd_test_rx_msg_append_kcode(port, PD_SYNC1); - pd_test_rx_msg_append_kcode(port, PD_SYNC3); -} - -void pd_test_rx_msg_append_eop(int port) -{ - pd_test_rx_msg_append_kcode(port, PD_EOP); -} - -void pd_test_rx_msg_append_last_edge(int port) -{ - /* end with 1, 1, 0 similar to pd_write_last_edge() */ - pd_test_rx_msg_append_bits(port, 3, 6); -} - -void pd_test_rx_msg_append_4b(int port, uint8_t val) -{ - pd_test_rx_msg_append_bits(port, enc4b5b[val & 0xF], 5); -} - -void pd_test_rx_msg_append_short(int port, uint16_t val) -{ - pd_test_rx_msg_append_4b(port, (val >> 0) & 0xF); - pd_test_rx_msg_append_4b(port, (val >> 4) & 0xF); - pd_test_rx_msg_append_4b(port, (val >> 8) & 0xF); - pd_test_rx_msg_append_4b(port, (val >> 12) & 0xF); -} - -void pd_test_rx_msg_append_word(int port, uint32_t val) -{ - pd_test_rx_msg_append_short(port, val & 0xFFFF); - pd_test_rx_msg_append_short(port, val >> 16); -} - -void pd_simulate_rx(int port) -{ - if (!pd_phy[port].rx_monitoring) - return; - - pd_phy[port].rx_started = 1; - pd_rx_disable_monitoring(port); - pd_rx_event(port); -} - -static int pd_test_tx_msg_verify(int port, uint8_t raw) -{ - int verified_idx = pd_phy[port].verified_idx++; - return pd_phy[port].out_msg[verified_idx] == raw; -} - -int pd_test_tx_msg_verify_kcode(int port, uint8_t kcode) -{ - return pd_test_tx_msg_verify(port, kcode); -} - -int pd_test_tx_msg_verify_sop(int port) -{ - crc32_init(); - return pd_test_tx_msg_verify_kcode(port, PD_SYNC1) && - pd_test_tx_msg_verify_kcode(port, PD_SYNC1) && - pd_test_tx_msg_verify_kcode(port, PD_SYNC1) && - pd_test_tx_msg_verify_kcode(port, PD_SYNC2); -} - -int pd_test_tx_msg_verify_sop_prime(int port) -{ - crc32_init(); - return pd_test_tx_msg_verify_kcode(port, PD_SYNC1) && - pd_test_tx_msg_verify_kcode(port, PD_SYNC1) && - pd_test_tx_msg_verify_kcode(port, PD_SYNC3) && - pd_test_tx_msg_verify_kcode(port, PD_SYNC3); -} - -int pd_test_tx_msg_verify_sop_prime_prime(int port) -{ - crc32_init(); - return pd_test_tx_msg_verify_kcode(port, PD_SYNC1) && - pd_test_tx_msg_verify_kcode(port, PD_SYNC3) && - pd_test_tx_msg_verify_kcode(port, PD_SYNC1) && - pd_test_tx_msg_verify_kcode(port, PD_SYNC3); -} - -int pd_test_tx_msg_verify_eop(int port) -{ - return pd_test_tx_msg_verify_kcode(port, PD_EOP); -} - -int pd_test_tx_msg_verify_4b5b(int port, uint8_t b4) -{ - return pd_test_tx_msg_verify(port, enc4b5b[b4]); -} - -int pd_test_tx_msg_verify_short(int port, uint16_t val) -{ - crc32_hash16(val); - return pd_test_tx_msg_verify_4b5b(port, (val >> 0) & 0xF) && - pd_test_tx_msg_verify_4b5b(port, (val >> 4) & 0xF) && - pd_test_tx_msg_verify_4b5b(port, (val >> 8) & 0xF) && - pd_test_tx_msg_verify_4b5b(port, (val >> 12) & 0xF); -} - -int pd_test_tx_msg_verify_word(int port, uint32_t val) -{ - return pd_test_tx_msg_verify_short(port, val & 0xFFFF) && - pd_test_tx_msg_verify_short(port, val >> 16); -} - -int pd_test_tx_msg_verify_crc(int port) -{ - return pd_test_tx_msg_verify_word(port, crc32_result()); -} - - -/* Mock functions */ - -void pd_init_dequeue(int port) -{ -} - -int pd_dequeue_bits(int port, int off, int len, uint32_t *val) -{ - int i; - - /* Rx must have started to receive message */ - ASSERT(pd_phy[port].rx_started); - - if (pd_phy[port].total <= off + len - PREAMBLE_OFFSET) - return -1; - *val = 0; - for (i = 0; i < len; ++i) - *val |= pd_phy[port].bits[off + i - PREAMBLE_OFFSET] << i; - return off + len; -} - -int pd_find_preamble(int port) -{ - return pd_phy[port].has_preamble ? PREAMBLE_OFFSET : -1; -} - -int pd_write_preamble(int port) -{ - ASSERT(pd_phy[port].preamble_written == 0); - pd_phy[port].preamble_written = 1; - ASSERT(pd_phy[port].has_msg == 0); - return 0; -} - -static uint8_t decode_bmc(uint32_t val10) -{ - uint8_t ret = 0; - int i; - - for (i = 0; i < 5; ++i) - if (!!(val10 & (1 << (2 * i))) != - !!(val10 & (1 << (2 * i + 1)))) - ret |= BIT(i); - return ret; -} - -int pd_write_sym(int port, int bit_off, uint32_t val10) -{ - pd_phy[port].out_msg[bit_off] = decode_bmc(val10); - pd_phy[port].has_msg = 1; - return bit_off + 1; -} - -int pd_write_last_edge(int port, int bit_off) -{ - pd_phy[port].last_edge_written = 1; - return bit_off; -} - -void pd_dump_packet(int port, const char *msg) -{ - /* Not implemented */ -} - -void pd_tx_set_circular_mode(int port) -{ - /* Not implemented */ -} - -void pd_tx_clear_circular_mode(int port) -{ - /* Not implemented */ -} - -int pd_start_tx(int port, int polarity, int bit_len) -{ - ASSERT(pd_phy[port].hw_init_done); - pd_phy[port].has_msg = 0; - pd_phy[port].preamble_written = 0; - pd_phy[port].verified_idx = 0; - pd_phy[port].total = 0; - - /* - * Hand over to test runner. The test runner must wake us after - * processing the packet. - */ - task_wake(TASK_ID_TEST_RUNNER); - task_wait_event(-1); - - return bit_len; -} - -void pd_tx_done(int port, int polarity) -{ - pd_test_reset_phy(port); -} - -void pd_rx_start(int port) -{ - ASSERT(pd_phy[port].hw_init_done); - - task_wake(TASK_ID_TEST_RUNNER); - task_wait_event(-1); - - pd_phy[port].rx_started = 1; -} - -void pd_rx_complete(int port) -{ - ASSERT(pd_phy[port].hw_init_done); - pd_test_reset_phy(port); -} - -int pd_rx_started(int port) -{ - return pd_phy[port].rx_started; -} - -void pd_rx_enable_monitoring(int port) -{ - ASSERT(pd_phy[port].hw_init_done); - pd_phy[port].rx_monitoring = 1; -} - -void pd_rx_disable_monitoring(int port) -{ - /* - * We disabled RX monitoring in TCPMv1 in set_state when - * transitioning from suspended to disconnected, but we only - * reinitialize after we have fully transitioned to disconnected. Don't - * assert that hw_init_done here since we have "valid" code that - * requires hw_init_done to be false when a port is suspended. - */ - pd_phy[port].rx_monitoring = 0; -} - -void pd_hw_release(int port) -{ - pd_phy[port].hw_init_done = 0; -} - -void pd_hw_init(int port, int role) -{ - pd_config_init(port, role); - pd_phy[port].hw_init_done = 1; -} - -void pd_set_clock(int port, int freq) -{ - /* Not implemented */ -} diff --git a/common/build.mk b/common/build.mk index ec592b8f9f..aaa3146d11 100644 --- a/common/build.mk +++ b/common/build.mk @@ -43,9 +43,6 @@ common-$(CONFIG_CAPSENSE)+=capsense.o common-$(CONFIG_CASE_CLOSED_DEBUG_V1)+=ccd_config.o common-$(CONFIG_CEC)+=cec.o common-$(CONFIG_CROS_BOARD_INFO)+=cbi.o -common-$(CONFIG_CHARGE_MANAGER)+=charge_manager.o -common-$(CONFIG_CHARGE_RAMP_HW)+=charge_ramp.o -common-$(CONFIG_CHARGE_RAMP_SW)+=charge_ramp.o charge_ramp_sw.o common-$(CONFIG_CMD_CHARGEN) += chargen.o common-$(CONFIG_CHARGER)+=charger.o charge_state_v2.o common-$(CONFIG_CHARGER_PROFILE_OVERRIDE_COMMON)+=charger_profile_override.o @@ -136,18 +133,9 @@ common-$(CONFIG_THROTTLE_AP_ON_BAT_DISCHG_CURRENT)+=throttle_ap.o common-$(CONFIG_THROTTLE_AP_ON_BAT_VOLTAGE)+=throttle_ap.o common-$(CONFIG_TPM_I2CS)+=i2cs_tpm.o common-$(CONFIG_U2F)+=u2f.o -common-$(CONFIG_USB_CHARGER)+=usb_charger.o common-$(CONFIG_USB_CONSOLE_STREAM)+=usb_console_stream.o common-$(CONFIG_USB_I2C)+=usb_i2c.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_common.o -ifeq ($(CONFIG_USB_SM_FRAMEWORK),) -common-$(CONFIG_USB_POWER_DELIVERY)+=usb_pd_protocol.o usb_pd_policy.o -endif common-$(CONFIG_USB_PD_LOGGING)+=event_log.o pd_log.o -common-$(CONFIG_USB_PD_TCPC)+=usb_pd_tcpc.o -common-$(CONFIG_USBC_PPC)+=usbc_ppc.o common-$(CONFIG_VBOOT_EFS)+=vboot/vboot.o common-$(CONFIG_VBOOT_HASH)+=sha256.o vboot_hash.o common-$(CONFIG_VOLUME_BUTTONS)+=button.o @@ -281,7 +269,6 @@ $(out)/$(PROJECT).exe: $(out)/cryptoc/libcryptoc.a endif include $(_common_dir)fpsensor/build.mk -include $(_common_dir)usbc/build.mk include $(_common_dir)mock/build.mk common-y+=$(foreach m,$(mock-y),mock/$(m)) diff --git a/common/charge_manager.c b/common/charge_manager.c deleted file mode 100644 index d95f5cb809..0000000000 --- a/common/charge_manager.c +++ /dev/null @@ -1,1455 +0,0 @@ -/* 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 "atomic.h" -#include "battery.h" -#include "charge_manager.h" -#include "charge_ramp.h" -#include "charger.h" -#include "console.h" -#include "gpio.h" -#include "hooks.h" -#include "host_command.h" -#include "system.h" -#include "tcpm.h" -#include "timer.h" -#include "usb_pd.h" -#include "usb_pd_tcpm.h" -#include "util.h" - -#define CPRINTS(format, args...) cprints(CC_USBCHARGE, format, ## args) - -#define POWER(charge_port) ((charge_port.current) * (charge_port.voltage)) - -/* Timeout for delayed override power swap, allow for 500ms extra */ -#define POWER_SWAP_TIMEOUT (PD_T_SRC_RECOVER_MAX + PD_T_SRC_TURN_ON + \ - PD_T_SAFE_0V + 500 * MSEC) - -/* - * Default charge supplier priority - * - * - Always pick dedicated charge if present since that is the best product - * decision. - * - Pick PD negotiated chargers over everything else since they have the most - * power potential and they may not currently be negotiated at a high power. - * (and they can at least provide 15W) - * - Pick Type-C which supplier current >= 1.5A, which has higher prioirty - * than the BC1.2 and Type-C with current under 1.5A. (USB-C spec 1.3 - * Table 4-17: TYPEC 3.0A, 1.5A > BC1.2 > TYPEC under 1.5A) - * - Then pick among the propreitary and BC1.2 chargers which ever has the - * highest available power. - * - Last, pick one from the rest suppliers. Also note that some boards assume - * wireless suppliers as low priority. - */ -__overridable const int supplier_priority[] = { -#if CONFIG_DEDICATED_CHARGE_PORT_COUNT > 0 - [CHARGE_SUPPLIER_DEDICATED] = 0, -#endif - [CHARGE_SUPPLIER_PD] = 1, - [CHARGE_SUPPLIER_TYPEC] = 2, - [CHARGE_SUPPLIER_TYPEC_DTS] = 2, -#ifdef CHARGE_MANAGER_BC12 - [CHARGE_SUPPLIER_PROPRIETARY] = 3, - [CHARGE_SUPPLIER_BC12_DCP] = 3, - [CHARGE_SUPPLIER_BC12_CDP] = 3, - [CHARGE_SUPPLIER_BC12_SDP] = 3, - [CHARGE_SUPPLIER_TYPEC_UNDER_1_5A] = 4, - [CHARGE_SUPPLIER_OTHER] = 4, - [CHARGE_SUPPLIER_VBUS] = 4, -#endif -#ifdef CONFIG_WIRELESS_CHARGER_P9221_R7 - [CHARGE_SUPPLIER_WPC_BPP] = 5, - [CHARGE_SUPPLIER_WPC_EPP] = 5, - [CHARGE_SUPPLIER_WPC_GPP] = 5, -#endif - -}; -BUILD_ASSERT(ARRAY_SIZE(supplier_priority) == CHARGE_SUPPLIER_COUNT); - -/* Keep track of available charge for each charge port. */ -static struct charge_port_info available_charge[CHARGE_SUPPLIER_COUNT] - [CHARGE_PORT_COUNT]; - -/* Keep track of when the supplier on each port is registered. */ -static timestamp_t registration_time[CHARGE_PORT_COUNT]; - -/* - * Charge current ceiling (mA) for ports. This can be set to temporarily limit - * the charge pulled from a port, without influencing the port selection logic. - * The ceiling can be set independently from several requestors, with the - * minimum ceiling taking effect. - */ -static int charge_ceil[CHARGE_PORT_COUNT][CEIL_REQUESTOR_COUNT]; - -/* Dual-role capability of attached partner port */ -static enum dualrole_capabilities dualrole_capability[CHARGE_PORT_COUNT]; - -#ifdef CONFIG_USB_PD_LOGGING -/* Mark port as dirty when making changes, for later logging */ -static int save_log[CHARGE_PORT_COUNT]; -#endif - -/* Store current state of port enable / charge current. */ -static int charge_port = CHARGE_PORT_NONE; -static int charge_current = CHARGE_CURRENT_UNINITIALIZED; -static int charge_current_uncapped = CHARGE_CURRENT_UNINITIALIZED; -static int charge_voltage; -static int charge_supplier = CHARGE_SUPPLIER_NONE; -static int override_port = OVERRIDE_OFF; - -static int delayed_override_port = OVERRIDE_OFF; -static timestamp_t delayed_override_deadline; - -static uint8_t source_port_rp[CONFIG_USB_PD_PORT_MAX_COUNT]; - -#ifdef CONFIG_USB_PD_MAX_TOTAL_SOURCE_CURRENT -/* 3A on one port and 1.5A on the rest */ -BUILD_ASSERT(CONFIG_USB_PD_PORT_MAX_COUNT * 1500 + 1500 <= - CONFIG_USB_PD_MAX_TOTAL_SOURCE_CURRENT); -#endif - -/* - * charge_manager initially operates in safe mode until asked to leave (through - * charge_manager_leave_safe_mode()). While in safe mode, the following - * behavior is altered: - * - * 1) All chargers are considered dedicated (and thus are valid charge source - * candidates) for the purpose of port selection. - * 2) Charge ceilings are ignored. Most significantly, ILIM won't drop on PD - * voltage transition. If current load is high during transition, some - * chargers may brown-out. - * 3) CHARGE_PORT_NONE will not be selected (POR default charge port will - * remain selected rather than CHARGE_PORT_NONE). - * - * After leaving safe mode, charge_manager reverts to its normal behavior and - * immediately selects charge port and current using standard rules. - */ -#ifdef CONFIG_CHARGE_MANAGER_SAFE_MODE -static int left_safe_mode; -#else -static const int left_safe_mode = 1; -#endif - -enum charge_manager_change_type { - CHANGE_CHARGE, - CHANGE_DUALROLE, -}; - -static int is_pd_port(int port) -{ - return port >= 0 && port < board_get_usb_pd_port_count(); -} - -static int is_sink(int port) -{ - if (!is_pd_port(port)) - return board_charge_port_is_sink(port); - - return pd_get_role(port) == PD_ROLE_SINK; -} - -/** - * Some of the SKUs in certain boards have less number of USB PD ports than - * defined in CONFIG_USB_PD_PORT_MAX_COUNT. With the charge port configuration - * for DEDICATED_PORT towards the end, this will lead to holes in the static - * configuration. The ports that fall in that hole are invalid and this function - * is used to check the validity of the ports. - */ -static int is_valid_port(int port) -{ - if (port < 0 || port >= CHARGE_PORT_COUNT) - return 0; - - /* Check if the port falls in the hole */ - if (port >= board_get_usb_pd_port_count() && - port < CONFIG_USB_PD_PORT_MAX_COUNT) - return 0; - return 1; -} - -#ifndef TEST_BUILD -static int is_connected(int port) -{ - if (!is_pd_port(port)) - return board_charge_port_is_connected(port); - - return pd_is_connected(port); -} -#endif /* !TEST_BUILD */ - -#ifndef CONFIG_CHARGE_MANAGER_DRP_CHARGING -/** - * In certain cases we need to override the default behavior of not charging - * from non-dedicated chargers. If the system is in RO and locked, we have no - * way of determining the actual dualrole capability of the charger because - * PD communication is not allowed, so we must assume that it is dedicated. - * Also, if no battery is present, the charger may be our only source of power, - * so again we must assume that the charger is dedicated. - * - * @return 1 when we need to override the a non-dedicated charger - * to be a dedicated one, 0 otherwise. - */ -static int charge_manager_spoof_dualrole_capability(void) -{ - return (system_get_image_copy() == SYSTEM_IMAGE_RO && - system_is_locked()) || !left_safe_mode; - -} -#endif /* !CONFIG_CHARGE_MANAGER_DRP_CHARGING */ - -/** - * Initialize available charge. Run before board init, so board init can - * initialize data, if needed. - */ -static void charge_manager_init(void) -{ - int i, j; - - for (i = 0; i < CHARGE_PORT_COUNT; ++i) { - if (!is_valid_port(i)) - continue; - for (j = 0; j < CHARGE_SUPPLIER_COUNT; ++j) { - available_charge[j][i].current = - CHARGE_CURRENT_UNINITIALIZED; - available_charge[j][i].voltage = - CHARGE_VOLTAGE_UNINITIALIZED; - } - for (j = 0; j < CEIL_REQUESTOR_COUNT; ++j) - charge_ceil[i][j] = CHARGE_CEIL_NONE; - if (!is_pd_port(i)) - dualrole_capability[i] = CAP_DEDICATED; - if (is_pd_port(i)) - source_port_rp[i] = CONFIG_USB_PD_PULLUP; - } -} -DECLARE_HOOK(HOOK_INIT, charge_manager_init, HOOK_PRIO_CHARGE_MANAGER_INIT); - -/** - * Check if the charge manager is seeded. - * - * @return 1 if all ports/suppliers have reported - * with some initial charge, 0 otherwise. - */ -static int charge_manager_is_seeded(void) -{ - /* Once we're seeded, we don't need to check again. */ - static int is_seeded; - int i, j; - - if (is_seeded) - return 1; - - for (i = 0; i < CHARGE_SUPPLIER_COUNT; ++i) { - for (j = 0; j < CHARGE_PORT_COUNT; ++j) { - if (!is_valid_port(j)) - continue; - if (available_charge[i][j].current == - CHARGE_CURRENT_UNINITIALIZED || - available_charge[i][j].voltage == - CHARGE_VOLTAGE_UNINITIALIZED) - return 0; - } - } - is_seeded = 1; - return 1; -} - -#ifndef TEST_BUILD -/** - * Get the maximum charge current for a port. - * - * @param port Charge port. - * @return Charge current (mA). - */ -static int charge_manager_get_source_current(int port) -{ - if (!is_pd_port(port)) - return 0; - - switch (source_port_rp[port]) { - case TYPEC_RP_3A0: - return 3000; - case TYPEC_RP_1A5: - return 1500; - case TYPEC_RP_USB: - default: - return 500; - } -} - -/* - * Find a supplier considering available current, voltage, power, and priority. - */ -static enum charge_supplier find_supplier(int port, enum charge_supplier sup, - int min_cur) -{ - int i; - for (i = 0; i < CHARGE_SUPPLIER_COUNT; ++i) { - if (available_charge[i][port].current <= min_cur || - available_charge[i][port].voltage <= 0) - /* Doesn't meet volt or current requirement. Skip it. */ - continue; - if (sup == CHARGE_SUPPLIER_NONE) - /* Haven't found any yet. Take it unconditionally. */ - sup = i; - else if (supplier_priority[sup] < supplier_priority[i]) - /* There is already a higher priority supplier. */ - continue; - else if (supplier_priority[i] < supplier_priority[sup]) - /* This has a higher priority. Take it. */ - sup = i; - else if (POWER(available_charge[i][port]) > - POWER(available_charge[sup][port])) - /* Priority is tie. Take it if power is higher. */ - sup = i; - } - return sup; -} - -/** - * Fills passed power_info structure with current info about the passed port. - * - * @param port Charge port. - * @param r USB PD power info to be updated. - */ -static void charge_manager_fill_power_info(int port, - struct ec_response_usb_pd_power_info *r) -{ - int sup = CHARGE_SUPPLIER_NONE; - - /* Determine supplier information to show. */ - if (port == charge_port) { - sup = charge_supplier; - } else { - /* Consider available current */ - sup = find_supplier(port, sup, 0); - if (sup == CHARGE_SUPPLIER_NONE) - /* Ignore available current */ - sup = find_supplier(port, sup, -1); - } - - /* Fill in power role */ - if (charge_port == port) - r->role = USB_PD_PORT_POWER_SINK; - else if (is_connected(port) && !is_sink(port)) - r->role = USB_PD_PORT_POWER_SOURCE; - else if (sup != CHARGE_SUPPLIER_NONE) - r->role = USB_PD_PORT_POWER_SINK_NOT_CHARGING; - else - r->role = USB_PD_PORT_POWER_DISCONNECTED; - - /* Is port partner dual-role capable */ - r->dualrole = (dualrole_capability[port] == CAP_DUALROLE); - - if (sup == CHARGE_SUPPLIER_NONE || - r->role == USB_PD_PORT_POWER_SOURCE) { - if (is_pd_port(port)) { - r->type = USB_CHG_TYPE_NONE; - r->meas.voltage_max = 0; - r->meas.voltage_now = - r->role == USB_PD_PORT_POWER_SOURCE ? 5000 : 0; - r->meas.current_max = - charge_manager_get_source_current(port); - r->max_power = 0; - } else { - r->type = USB_CHG_TYPE_NONE; - board_fill_source_power_info(port, r); - } - } else { - int use_ramp_current; - switch (sup) { - case CHARGE_SUPPLIER_PD: - r->type = USB_CHG_TYPE_PD; - break; - case CHARGE_SUPPLIER_TYPEC: - case CHARGE_SUPPLIER_TYPEC_DTS: - r->type = USB_CHG_TYPE_C; - break; -#ifdef CHARGE_MANAGER_BC12 - case CHARGE_SUPPLIER_PROPRIETARY: - r->type = USB_CHG_TYPE_PROPRIETARY; - break; - case CHARGE_SUPPLIER_BC12_DCP: - r->type = USB_CHG_TYPE_BC12_DCP; - break; - case CHARGE_SUPPLIER_BC12_CDP: - r->type = USB_CHG_TYPE_BC12_CDP; - break; - case CHARGE_SUPPLIER_BC12_SDP: - r->type = USB_CHG_TYPE_BC12_SDP; - break; - case CHARGE_SUPPLIER_VBUS: - r->type = USB_CHG_TYPE_VBUS; - break; -#endif -#ifdef CONFIG_WIRELESS_CHARGER_P9221_R7 - /* - * Todo:need kernel add wpc device node in power_supply - * before that use USB_CHG_TYPE_PROPRIETARY to present WPC. - */ - case CHARGE_SUPPLIER_WPC_BPP: - case CHARGE_SUPPLIER_WPC_EPP: - case CHARGE_SUPPLIER_WPC_GPP: - r->type = USB_CHG_TYPE_PROPRIETARY; - break; -#endif -#if CONFIG_DEDICATED_CHARGE_PORT_COUNT > 0 - case CHARGE_SUPPLIER_DEDICATED: - r->type = USB_CHG_TYPE_DEDICATED; - break; -#endif - default: -#ifdef CONFIG_WIRELESS_CHARGER_P9221_R7 - r->type = USB_CHG_TYPE_VBUS; -#else - r->type = USB_CHG_TYPE_OTHER; -#endif - } - r->meas.voltage_max = available_charge[sup][port].voltage; - - /* - * Report unknown charger CHARGE_DETECT_DELAY after supplier - * change since PD negotiation may take time. - */ - if (get_time().val < registration_time[port].val + - CHARGE_DETECT_DELAY) - r->type = USB_CHG_TYPE_UNKNOWN; - -#if defined(HAS_TASK_CHG_RAMP) || defined(CONFIG_CHARGE_RAMP_HW) - /* Read ramped current if active charging port */ - use_ramp_current = - (charge_port == port) && chg_ramp_allowed(sup); -#else - use_ramp_current = 0; -#endif - if (use_ramp_current) { - /* Current limit is output of ramp module */ - r->meas.current_lim = chg_ramp_get_current_limit(); - - /* - * If ramp is allowed, then the max current depends - * on if ramp is stable. If ramp is stable, then - * max current is same as input current limit. If - * ramp is not stable, then we report the maximum - * current we could ramp up to for this supplier. - * If ramp is not allowed, max current is just the - * available charge current. - */ - r->meas.current_max = chg_ramp_is_stable() ? - r->meas.current_lim : chg_ramp_max(sup, - available_charge[sup][port].current); - - r->max_power = - r->meas.current_max * r->meas.voltage_max; - } else { - r->meas.current_max = r->meas.current_lim = - available_charge[sup][port].current; - r->max_power = POWER(available_charge[sup][port]); - } - - /* - * If we are sourcing power, or sinking but not charging, then - * VBUS must be 5V. If we are charging, then read VBUS ADC. - */ - if (r->role == USB_PD_PORT_POWER_SINK_NOT_CHARGING) - r->meas.voltage_now = 5000; - else { -#if defined(CONFIG_USB_PD_VBUS_MEASURE_CHARGER) - r->meas.voltage_now = charger_get_vbus_voltage(port); -#elif defined(CONFIG_USB_PD_VBUS_MEASURE_ADC_EACH_PORT) - r->meas.voltage_now = - adc_read_channel(board_get_vbus_adc(port)); -#elif defined(CONFIG_USB_PD_VBUS_MEASURE_NOT_PRESENT) - /* No VBUS ADC channel - voltage is unknown */ - r->meas.voltage_now = 0; -#else - /* There is a single ADC that measures joint Vbus */ - r->meas.voltage_now = adc_read_channel(ADC_VBUS); -#endif - } - } -} -#endif /* TEST_BUILD */ - -#ifdef CONFIG_USB_PD_LOGGING -/** - * Saves a power state log entry with the current info about the passed port. - */ -void charge_manager_save_log(int port) -{ - uint16_t flags = 0; - struct ec_response_usb_pd_power_info pinfo; - - if (!is_pd_port(port)) - return; - - save_log[port] = 0; - charge_manager_fill_power_info(port, &pinfo); - - /* Flags are stored in the data field */ - if (port == override_port) - flags |= CHARGE_FLAGS_OVERRIDE; - if (port == delayed_override_port) - flags |= CHARGE_FLAGS_DELAYED_OVERRIDE; - flags |= pinfo.role | (pinfo.type << CHARGE_FLAGS_TYPE_SHIFT) | - (pinfo.dualrole ? CHARGE_FLAGS_DUAL_ROLE : 0); - - pd_log_event(PD_EVENT_MCU_CHARGE, - PD_LOG_PORT_SIZE(port, sizeof(pinfo.meas)), - flags, &pinfo.meas); -} -#endif /* CONFIG_USB_PD_LOGGING */ - -/** - * Attempt to switch to power source on port if applicable. - * - * @param port USB-C port to be swapped. - */ -static void charge_manager_switch_to_source(int port) -{ - if (!is_pd_port(port)) - return; - - /* If connected to dual-role device, then ask for a swap */ - if (dualrole_capability[port] == CAP_DUALROLE && is_sink(port)) - pd_request_power_swap(port); -} - -/** - * Return the computed charge ceiling for a port, which represents the - * minimum ceiling among all valid requestors. - * - * @param port Charge port. - * @return Charge ceiling (mA) or CHARGE_CEIL_NONE. - */ -static int charge_manager_get_ceil(int port) -{ - int ceil = CHARGE_CEIL_NONE; - int val, i; - - if (!is_valid_port(port)) - return ceil; - - for (i = 0; i < CEIL_REQUESTOR_COUNT; ++i) { - val = charge_ceil[port][i]; - if (val != CHARGE_CEIL_NONE && - (ceil == CHARGE_CEIL_NONE || val < ceil)) - ceil = val; - } - - return ceil; -} - -/** - * Select the 'best' charge port, as defined by the supplier heirarchy and the - * ability of the port to provide power. - * - * @param new_port Pointer to the best charge port by definition. - * @param new_supplier Pointer to the best charge supplier by definition. - */ -static void charge_manager_get_best_charge_port(int *new_port, - int *new_supplier) -{ - int supplier = CHARGE_SUPPLIER_NONE; - int port = CHARGE_PORT_NONE; - int best_port_power = -1, candidate_port_power; - int i, j; - - /* Skip port selection on OVERRIDE_DONT_CHARGE. */ - if (override_port != OVERRIDE_DONT_CHARGE) { - /* - * Charge supplier selection logic: - * 1. Prefer higher priority supply. - * 2. Prefer higher power over lower in case priority is tied. - * 3. Prefer current charge port over new port in case (1) - * and (2) are tied. - * available_charge can be changed at any time by other tasks, - * so make no assumptions about its consistency. - */ - for (i = 0; i < CHARGE_SUPPLIER_COUNT; ++i) - for (j = 0; j < CHARGE_PORT_COUNT; ++j) { - /* Skip this port if it is not valid. */ - if (!is_valid_port(j)) - continue; - - /* - * Skip this supplier if there is no - * available charge. - */ - if (available_charge[i][j].current == 0 || - available_charge[i][j].voltage == 0) - continue; - - /* - * Don't select this port if we have a - * charge on another override port. - */ - if (override_port != OVERRIDE_OFF && - override_port == port && - override_port != j) - continue; - -#ifndef CONFIG_CHARGE_MANAGER_DRP_CHARGING - /* - * Don't charge from a dual-role port unless - * it is our override port. - */ - if (dualrole_capability[j] != CAP_DEDICATED && - override_port != j && - !charge_manager_spoof_dualrole_capability()) - continue; -#endif - - candidate_port_power = - POWER(available_charge[i][j]); - - /* Select if no supplier chosen yet. */ - if (supplier == CHARGE_SUPPLIER_NONE || - /* ..or if supplier priority is higher. */ - supplier_priority[i] < - supplier_priority[supplier] || - /* ..or if this is our override port. */ - (j == override_port && - port != override_port) || - /* ..or if priority is tied and.. */ - (supplier_priority[i] == - supplier_priority[supplier] && - /* candidate port can supply more power or.. */ - (candidate_port_power > best_port_power || - /* - * candidate port is the active port and can - * supply the same amount of power. - */ - (candidate_port_power == best_port_power && - charge_port == j)))) { - supplier = i; - port = j; - best_port_power = candidate_port_power; - } - } - - } - - *new_port = port; - *new_supplier = supplier; -} - -/** - * Charge manager refresh -- responsible for selecting the active charge port - * and charge power. Called as a deferred task. - */ -static void charge_manager_refresh(void) -{ - /* Always initialize charge port on first pass */ - static int active_charge_port_initialized; - int new_supplier, new_port; - int new_charge_current, new_charge_current_uncapped; - int new_charge_voltage, i; - int updated_new_port = CHARGE_PORT_NONE; - int updated_old_port = CHARGE_PORT_NONE; - int ceil; - int power_changed = 0; - - /* Hunt for an acceptable charge port */ - while (1) { - charge_manager_get_best_charge_port(&new_port, &new_supplier); - - if (!left_safe_mode && new_port == CHARGE_PORT_NONE) - return; - - /* - * If the port or supplier changed, make an attempt to switch to - * the port. We will re-set the active port on a supplier change - * to give the board-level function another chance to reject - * the port, for example, if the port has become a charge - * source. - */ - if ((active_charge_port_initialized && - new_port == charge_port && - new_supplier == charge_supplier) || - board_set_active_charge_port(new_port) == EC_SUCCESS) - break; - - /* 'Dont charge' request must be accepted. */ - ASSERT(new_port != CHARGE_PORT_NONE); - - /* - * Zero the available charge on the rejected port so that - * it is no longer chosen. - */ - for (i = 0; i < CHARGE_SUPPLIER_COUNT; ++i) { - available_charge[i][new_port].current = 0; - available_charge[i][new_port].voltage = 0; - } - } - - active_charge_port_initialized = 1; - - /* - * Clear override if it wasn't selected as the 'best' port -- it means - * that no charge is available on the port, or the port was rejected. - */ - if (override_port >= 0 && override_port != new_port) - override_port = OVERRIDE_OFF; - - if (new_supplier == CHARGE_SUPPLIER_NONE) { - new_charge_current = 0; - new_charge_current_uncapped = 0; - new_charge_voltage = 0; - } else { - new_charge_current_uncapped = - available_charge[new_supplier][new_port].current; -#ifdef CONFIG_CHARGE_RAMP_HW - /* - * Allow to set the maximum current value, so the hardware can - * know the range of acceptable current values for its ramping. - */ - if (chg_ramp_allowed(new_supplier)) - new_charge_current_uncapped = - chg_ramp_max(new_supplier, - new_charge_current_uncapped); -#endif /* CONFIG_CHARGE_RAMP_HW */ - /* Enforce port charge ceiling. */ - ceil = charge_manager_get_ceil(new_port); - if (left_safe_mode && ceil != CHARGE_CEIL_NONE) - new_charge_current = MIN(ceil, - new_charge_current_uncapped); - else - new_charge_current = new_charge_current_uncapped; - - new_charge_voltage = - available_charge[new_supplier][new_port].voltage; - } - - /* Change the charge limit + charge port/supplier if modified. */ - if (new_port != charge_port || new_charge_current != charge_current || - new_supplier != charge_supplier) { -#ifdef HAS_TASK_CHG_RAMP - chg_ramp_charge_supplier_change( - new_port, new_supplier, new_charge_current, - registration_time[new_port], - new_charge_voltage); -#else -#ifdef CONFIG_CHARGE_RAMP_HW - /* Enable or disable charge ramp */ - charger_set_hw_ramp(chg_ramp_allowed(new_supplier)); -#endif - board_set_charge_limit(new_port, new_supplier, - new_charge_current, - new_charge_current_uncapped, - new_charge_voltage); -#endif /* HAS_TASK_CHG_RAMP */ - - power_changed = 1; - - CPRINTS("CL: p%d s%d i%d v%d", new_port, new_supplier, - new_charge_current, new_charge_voltage); - } - - /* - * Signal new power request only if the port changed, the voltage - * on the same port changed, or the actual uncapped current - * on the same port changed (don't consider ceil). - */ - if (new_port != CHARGE_PORT_NONE && - (new_port != charge_port || - new_charge_current_uncapped != charge_current_uncapped || - new_charge_voltage != charge_voltage)) - updated_new_port = new_port; - - /* If charge port changed, cleanup old port */ - if (charge_port != new_port && charge_port != CHARGE_PORT_NONE) { - /* Check if need power swap */ - charge_manager_switch_to_source(charge_port); - /* Signal new power request on old port */ - updated_old_port = charge_port; - } - - /* Update globals to reflect current state. */ - charge_current = new_charge_current; - charge_current_uncapped = new_charge_current_uncapped; - charge_voltage = new_charge_voltage; - charge_supplier = new_supplier; - charge_port = new_port; - -#ifdef CONFIG_USB_PD_LOGGING - /* - * Write a log under the following conditions: - * 1. A port becomes active or - * 2. A port becomes inactive or - * 3. The active charge port power limit changes or - * 4. Any supplier change on an inactive port - */ - if (updated_new_port != CHARGE_PORT_NONE) - save_log[updated_new_port] = 1; - /* Don't log non-meaningful changes on charge port */ - else if (charge_port != CHARGE_PORT_NONE) - save_log[charge_port] = 0; - - if (updated_old_port != CHARGE_PORT_NONE) - save_log[updated_old_port] = 1; - - for (i = 0; i < board_get_usb_pd_port_count(); ++i) - if (save_log[i]) - charge_manager_save_log(i); -#endif - - /* New power requests must be set only after updating the globals. */ - if (is_pd_port(updated_new_port)) - pd_set_new_power_request(updated_new_port); - if (is_pd_port(updated_old_port)) - pd_set_new_power_request(updated_old_port); - - if (power_changed) - /* notify host of power info change */ - pd_send_host_event(PD_EVENT_POWER_CHANGE); -} -DECLARE_DEFERRED(charge_manager_refresh); - -/** - * Called when charge override times out waiting for power swap. - */ -static void charge_override_timeout(void) -{ - delayed_override_port = OVERRIDE_OFF; - pd_send_host_event(PD_EVENT_POWER_CHANGE); -} -DECLARE_DEFERRED(charge_override_timeout); - -/** - * Called CHARGE_DETECT_DELAY after the most recent charge change on a port. - */ -static void charger_detect_debounced(void) -{ - /* Inform host that charger detection is debounced. */ - pd_send_host_event(PD_EVENT_POWER_CHANGE); -} -DECLARE_DEFERRED(charger_detect_debounced); - -/** - * Update charge parameters for a given port / supplier. - * - * @param change Type of change. - * @param supplier Charge supplier to be updated. - * @param port Charge port to be updated. - * @param charge Charge port current / voltage. - */ -static void charge_manager_make_change(enum charge_manager_change_type change, - int supplier, - int port, - const struct charge_port_info *charge) -{ - int i; - int clear_override = 0; - - if (!is_valid_port(port)) { - CPRINTS("%s: p%d invalid", __func__, port); - return; - } - - /* Determine if this is a change which can affect charge status */ - switch (change) { - case CHANGE_CHARGE: - /* Ignore changes where charge is identical */ - if (available_charge[supplier][port].current == - charge->current && - available_charge[supplier][port].voltage == - charge->voltage) - return; - if (charge->current > 0 && - available_charge[supplier][port].current == 0) - clear_override = 1; -#ifdef CONFIG_USB_PD_LOGGING - save_log[port] = 1; -#endif - break; - case CHANGE_DUALROLE: - /* - * Ignore all except for transition to non-dualrole, - * which may occur some time after we see a charge - */ -#ifndef CONFIG_CHARGE_MANAGER_DRP_CHARGING - if (dualrole_capability[port] != CAP_DEDICATED) -#endif - return; - /* Clear override only if a charge is present on the port */ - for (i = 0; i < CHARGE_SUPPLIER_COUNT; ++i) - if (available_charge[i][port].current > 0) { - clear_override = 1; - break; - } - /* - * If there is no charge present on the port, the dualrole - * change is meaningless to charge_manager. - */ - if (!clear_override) - return; - break; - } - - /* Remove override when a charger is plugged */ - if (clear_override && override_port != port -#ifndef CONFIG_CHARGE_MANAGER_DRP_CHARGING - /* only remove override when it's a dedicated charger */ - && dualrole_capability[port] == CAP_DEDICATED -#endif - ) { - override_port = OVERRIDE_OFF; - if (delayed_override_port != OVERRIDE_OFF) { - delayed_override_port = OVERRIDE_OFF; - hook_call_deferred(&charge_override_timeout_data, -1); - } - } - - if (change == CHANGE_CHARGE) { - available_charge[supplier][port].current = charge->current; - available_charge[supplier][port].voltage = charge->voltage; - registration_time[port] = get_time(); - - /* - * After CHARGE_DETECT_DELAY, inform the host that charger - * detection has been debounced. Since only one deferred - * routine exists for all ports, the deferred call for a given - * port may potentially be cancelled. This is mostly harmless - * since cancellation implies that PD_EVENT_POWER_CHANGE was - * just sent due to the power change on another port. - */ - if (charge->current > 0) - hook_call_deferred(&charger_detect_debounced_data, - CHARGE_DETECT_DELAY); - - /* - * If we have a charge on our delayed override port within - * the deadline, make it our override port. - */ - if (port == delayed_override_port && charge->current > 0 && - is_sink(delayed_override_port) && - get_time().val < delayed_override_deadline.val) { - delayed_override_port = OVERRIDE_OFF; - hook_call_deferred(&charge_override_timeout_data, -1); - charge_manager_set_override(port); - } - } - - /* - * Don't call charge_manager_refresh unless all ports + - * suppliers have reported in. We don't want to make changes - * to our charge port until we are certain we know what is - * attached. - */ - if (charge_manager_is_seeded()) - hook_call_deferred(&charge_manager_refresh_data, 0); -} - -void pd_set_input_current_limit(int port, uint32_t max_ma, - uint32_t supply_voltage) -{ - struct charge_port_info charge; - - charge.current = max_ma; - charge.voltage = supply_voltage; - charge_manager_update_charge(CHARGE_SUPPLIER_PD, port, &charge); -} - -void typec_set_input_current_limit(int port, typec_current_t max_ma, - uint32_t supply_voltage) -{ - struct charge_port_info charge; - int i; - int supplier; - int dts = !!(max_ma & TYPEC_CURRENT_DTS_MASK); - static const enum charge_supplier typec_suppliers[] = { - CHARGE_SUPPLIER_TYPEC, - CHARGE_SUPPLIER_TYPEC_DTS, -#ifdef CHARGE_MANAGER_BC12 - CHARGE_SUPPLIER_TYPEC_UNDER_1_5A, -#endif /* CHARGE_MANAGER_BC12 */ - }; - - charge.current = max_ma & TYPEC_CURRENT_ILIM_MASK; - charge.voltage = supply_voltage; -#if !defined(HAS_TASK_CHG_RAMP) && !defined(CONFIG_CHARGE_RAMP_HW) - /* - * DTS sources such as suzy-q may not be able to actually deliver - * their advertised current, so limit it to reduce chance of OC, - * if we can't ramp. - */ - if (dts) - charge.current = MIN(charge.current, 500); -#endif - - supplier = dts ? CHARGE_SUPPLIER_TYPEC_DTS : CHARGE_SUPPLIER_TYPEC; - -#ifdef CHARGE_MANAGER_BC12 - /* - * According to USB-C spec 1.3 Table 4-17 "Precedence of power source - * usage", the priority should be: USB-C 3.0A, 1.5A > BC1.2 > USB-C - * under 1.5A. Choosed the corresponding supplier type, according to - * charge current, to update. - */ - if (charge.current < 1500) - supplier = CHARGE_SUPPLIER_TYPEC_UNDER_1_5A; -#endif /* CHARGE_MANAGER_BC12 */ - - charge_manager_update_charge(supplier, port, &charge); - - /* - * TYPEC / TYPEC-DTS / TYPEC-UNDER_1_5A should be mutually exclusive. - * Zero'ing all the other suppliers. - */ - for (i = 0; i < ARRAY_SIZE(typec_suppliers); ++i) - if (supplier != typec_suppliers[i]) - charge_manager_update_charge(typec_suppliers[i], port, - NULL); -} - -void charge_manager_update_charge(int supplier, - int port, - const struct charge_port_info *charge) -{ - struct charge_port_info zero = {0}; - if (!charge) - charge = &zero; - charge_manager_make_change(CHANGE_CHARGE, supplier, port, charge); -} - -void charge_manager_update_dualrole(int port, enum dualrole_capabilities cap) -{ - if (!is_pd_port(port)) - return; - - /* Ignore when capability is unchanged */ - if (cap != dualrole_capability[port]) { - dualrole_capability[port] = cap; - charge_manager_make_change(CHANGE_DUALROLE, 0, port, NULL); - } -} - -#ifdef CONFIG_CHARGE_MANAGER_SAFE_MODE -void charge_manager_leave_safe_mode(void) -{ - if (left_safe_mode) - return; - - CPRINTS("%s()", __func__); - cflush(); - left_safe_mode = 1; - if (charge_manager_is_seeded()) - hook_call_deferred(&charge_manager_refresh_data, 0); -} -#endif - -void charge_manager_set_ceil(int port, enum ceil_requestor requestor, int ceil) -{ - if (!is_valid_port(port)) - return; - - if (charge_ceil[port][requestor] != ceil) { - charge_ceil[port][requestor] = ceil; - if (port == charge_port && charge_manager_is_seeded()) - hook_call_deferred(&charge_manager_refresh_data, 0); - } -} - -void charge_manager_force_ceil(int port, int ceil) -{ - /* - * Force our input current to ceil if we're exceeding it, without - * waiting for our deferred task to run. - */ - if (left_safe_mode && port == charge_port && ceil < charge_current) - board_set_charge_limit(port, CHARGE_SUPPLIER_PD, ceil, - charge_current_uncapped, charge_voltage); - - /* - * Now inform charge_manager so it stays in sync with the state of - * the world. - */ - charge_manager_set_ceil(port, CEIL_REQUESTOR_PD, ceil); -} - -int charge_manager_set_override(int port) -{ - int retval = EC_SUCCESS; - - CPRINTS("Charge Override: %d", port); - - /* - * If attempting to change the override port, then return - * error. Since we may be in the middle of a power swap on - * the original override port, it's too complicated to - * guarantee that the original override port is switched back - * to source. - */ - if (delayed_override_port != OVERRIDE_OFF) - return EC_ERROR_BUSY; - - /* Set the override port if it's a sink. */ - if (port < 0 || is_sink(port)) { - if (override_port != port) { - override_port = port; - if (charge_manager_is_seeded()) - hook_call_deferred( - &charge_manager_refresh_data, 0); - } - } - /* - * If the attached device is capable of being a sink, request a - * power swap and set the delayed override for swap completion. - */ - else if (!is_sink(port) && dualrole_capability[port] == CAP_DUALROLE) { - delayed_override_deadline.val = get_time().val + - POWER_SWAP_TIMEOUT; - delayed_override_port = port; - hook_call_deferred(&charge_override_timeout_data, - POWER_SWAP_TIMEOUT); - pd_request_power_swap(port); - /* Can't charge from requested port -- return error. */ - } else - retval = EC_ERROR_INVAL; - - return retval; -} - -int charge_manager_get_override(void) -{ - return override_port; -} - -int charge_manager_get_active_charge_port(void) -{ - return charge_port; -} - -int charge_manager_get_charger_current(void) -{ - return charge_current; -} - -int charge_manager_get_charger_voltage(void) -{ - return charge_voltage; -} - -int charge_manager_get_power_limit_uw(void) -{ - int current_ma = charge_current; - int voltage_mv = charge_voltage; - - if (current_ma == CHARGE_CURRENT_UNINITIALIZED || - voltage_mv == CHARGE_VOLTAGE_UNINITIALIZED) - return 0; - else - return current_ma * voltage_mv; -} - -#ifdef CONFIG_USB_PD_MAX_SINGLE_SOURCE_CURRENT - -/* Bitmap of ports used as power source */ -static volatile uint32_t source_port_bitmap; -BUILD_ASSERT(sizeof(source_port_bitmap)*8 >= CONFIG_USB_PD_PORT_MAX_COUNT); - -static inline int has_other_active_source(int port) -{ - return source_port_bitmap & ~BIT(port); -} - -static inline int is_active_source(int port) -{ - return source_port_bitmap & BIT(port); -} - -static int can_supply_max_current(int port) -{ -#ifdef CONFIG_USB_PD_MAX_TOTAL_SOURCE_CURRENT - /* - * This guarantees active 3A source continues to supply 3A. - * - * Since redistribution occurs sequentially, younger ports get - * priority. Priority surfaces only when 3A source is released. - * That is, when 3A source is released, the youngest active - * port gets 3A. - */ - int p; - if (!is_active_source(port)) - /* Non-active ports don't get 3A */ - return 0; - for (p = 0; p < board_get_usb_pd_port_count(); p++) { - if (p == port) - continue; - if (source_port_rp[p] == - CONFIG_USB_PD_MAX_SINGLE_SOURCE_CURRENT) - return 0; - } - return 1; -#else - return is_active_source(port) && !has_other_active_source(port); -#endif /* CONFIG_USB_PD_MAX_TOTAL_SOURCE_CURRENT */ -} - -void charge_manager_source_port(int port, int enable) -{ - uint32_t prev_bitmap = source_port_bitmap; - int p, rp; - - if (enable) - atomic_or(&source_port_bitmap, 1 << port); - else - atomic_clear(&source_port_bitmap, 1 << port); - - /* No change, exit early. */ - if (prev_bitmap == source_port_bitmap) - return; - - /* Set port limit according to policy */ - for (p = 0; p < board_get_usb_pd_port_count(); p++) { - rp = can_supply_max_current(p) ? - CONFIG_USB_PD_MAX_SINGLE_SOURCE_CURRENT : - CONFIG_USB_PD_PULLUP; - source_port_rp[p] = rp; - -#ifdef CONFIG_USB_PD_LOGGING - if (is_connected(p) && !is_sink(p)) - charge_manager_save_log(p); -#endif - - typec_set_source_current_limit(p, rp); - tcpm_select_rp_value(p, rp); - pd_update_contract(p); - } -} - -int charge_manager_get_source_pdo(const uint32_t **src_pdo, const int port) -{ - if (can_supply_max_current(port)) { - *src_pdo = pd_src_pdo_max; - return pd_src_pdo_max_cnt; - } - - *src_pdo = pd_src_pdo; - return pd_src_pdo_cnt; -} -#endif /* CONFIG_USB_PD_MAX_SINGLE_SOURCE_CURRENT */ - -#ifndef TEST_BUILD -static enum ec_status hc_pd_power_info(struct host_cmd_handler_args *args) -{ - const struct ec_params_usb_pd_power_info *p = args->params; - struct ec_response_usb_pd_power_info *r = args->response; - int port = p->port; - - /* If host is asking for the charging port, set port appropriately */ - if (port == PD_POWER_CHARGING_PORT) - port = charge_port; - - /* - * Not checking for invalid port here, because it might break existing - * contract with ectool users. The invalid ports will have the response - * voltage, current and power parameters set to 0. - */ - if (port >= CHARGE_PORT_COUNT) - return EC_RES_INVALID_PARAM; - - charge_manager_fill_power_info(port, r); - - args->response_size = sizeof(*r); - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_USB_PD_POWER_INFO, - hc_pd_power_info, - EC_VER_MASK(0)); -#endif /* TEST_BUILD */ - -static enum ec_status hc_charge_port_count(struct host_cmd_handler_args *args) -{ - struct ec_response_charge_port_count *resp = args->response; - - args->response_size = sizeof(*resp); - resp->port_count = CHARGE_PORT_COUNT; - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_CHARGE_PORT_COUNT, - hc_charge_port_count, - EC_VER_MASK(0)); - -static enum ec_status -hc_charge_port_override(struct host_cmd_handler_args *args) -{ - const struct ec_params_charge_port_override *p = args->params; - const int16_t override_port = p->override_port; - - if (override_port < OVERRIDE_DONT_CHARGE || - override_port >= CHARGE_PORT_COUNT) - return EC_RES_INVALID_PARAM; - - return charge_manager_set_override(override_port) == EC_SUCCESS ? - EC_RES_SUCCESS : EC_RES_ERROR; -} -DECLARE_HOST_COMMAND(EC_CMD_PD_CHARGE_PORT_OVERRIDE, - hc_charge_port_override, - EC_VER_MASK(0)); - -#if CONFIG_DEDICATED_CHARGE_PORT_COUNT > 0 -static enum ec_status hc_override_dedicated_charger_limit( - struct host_cmd_handler_args *args) -{ - const struct ec_params_dedicated_charger_limit *p = args->params; - struct charge_port_info ci = { - .current = p->current_lim, - .voltage = p->voltage_lim, - }; - - /* - * Allow a change only if the dedicated charge port is used. Host needs - * to apply a change every time a dedicated charger is plugged. - */ - if (charge_port != DEDICATED_CHARGE_PORT) - return EC_RES_UNAVAILABLE; - - charge_manager_update_charge(CHARGE_SUPPLIER_DEDICATED, - DEDICATED_CHARGE_PORT, &ci); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_OVERRIDE_DEDICATED_CHARGER_LIMIT, - hc_override_dedicated_charger_limit, - EC_VER_MASK(0)); -#endif - -static int command_charge_port_override(int argc, char **argv) -{ - int port = OVERRIDE_OFF; - int ret = EC_SUCCESS; - char *e; - - if (argc >= 2) { - port = strtoi(argv[1], &e, 0); - if (*e || port < OVERRIDE_DONT_CHARGE || - port >= CHARGE_PORT_COUNT) - return EC_ERROR_PARAM1; - ret = charge_manager_set_override(port); - } - - ccprintf("Override: %d\n", (argc >= 2 && ret == EC_SUCCESS) ? - port : override_port); - return ret; -} -DECLARE_CONSOLE_COMMAND(chgoverride, command_charge_port_override, - "[port | -1 | -2]", - "Force charging from a given port (-1 = off, -2 = disable charging)"); - -#ifdef CONFIG_CHARGE_MANAGER_EXTERNAL_POWER_LIMIT -static void charge_manager_set_external_power_limit(int current_lim, - int voltage_lim) -{ - int port; - - if (current_lim == EC_POWER_LIMIT_NONE) - current_lim = CHARGE_CEIL_NONE; - if (voltage_lim == EC_POWER_LIMIT_NONE) - voltage_lim = PD_MAX_VOLTAGE_MV; - - for (port = 0; port < board_get_usb_pd_port_count(); ++port) { - charge_manager_set_ceil(port, CEIL_REQUESTOR_HOST, current_lim); - pd_set_external_voltage_limit(port, voltage_lim); - } -} - -/* - * On transition out of S0, disable all external power limits, in case AP - * failed to clear them. - */ -static void charge_manager_external_power_limit_off(void) -{ - charge_manager_set_external_power_limit(EC_POWER_LIMIT_NONE, - EC_POWER_LIMIT_NONE); -} -DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, charge_manager_external_power_limit_off, - HOOK_PRIO_DEFAULT); - -static enum ec_status -hc_external_power_limit(struct host_cmd_handler_args *args) -{ - const struct ec_params_external_power_limit_v1 *p = args->params; - - charge_manager_set_external_power_limit(p->current_lim, - p->voltage_lim); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_EXTERNAL_POWER_LIMIT, - hc_external_power_limit, - EC_VER_MASK(1)); - -static int command_external_power_limit(int argc, char **argv) -{ - int max_current; - int max_voltage; - char *e; - - if (argc >= 2) { - max_current = strtoi(argv[1], &e, 10); - if (*e) - return EC_ERROR_PARAM1; - } else - max_current = EC_POWER_LIMIT_NONE; - - if (argc >= 3) { - max_voltage = strtoi(argv[2], &e, 10); - if (*e) - return EC_ERROR_PARAM1; - } else - max_voltage = EC_POWER_LIMIT_NONE; - - charge_manager_set_external_power_limit(max_current, max_voltage); - ccprintf("max req: %dmA %dmV\n", max_current, max_voltage); - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(chglim, command_external_power_limit, - "[max_current (mA)] [max_voltage (mV)]", - "Set max charger current / voltage"); -#endif /* CONFIG_CHARGE_MANAGER_EXTERNAL_POWER_LIMIT */ - -#ifdef CONFIG_CMD_CHARGE_SUPPLIER_INFO -static int charge_supplier_info(int argc, char **argv) -{ - ccprintf("port=%d, type=%d, cur=%dmA, vtg=%dmV, lsm=%d\n", - charge_manager_get_active_charge_port(), - charge_supplier, - charge_current, - charge_voltage, - left_safe_mode); - - return 0; -} -DECLARE_CONSOLE_COMMAND(chgsup, charge_supplier_info, - NULL, "print chg supplier info"); -#endif - -__overridable -int board_charge_port_is_sink(int port) -{ - return 1; -} - -__overridable -int board_charge_port_is_connected(int port) -{ - return 1; -} - -__overridable -void board_fill_source_power_info(int port, - struct ec_response_usb_pd_power_info *r) -{ - r->meas.voltage_now = 0; - r->meas.voltage_max = 0; - r->meas.current_max = 0; - r->meas.current_lim = 0; - r->max_power = 0; -} diff --git a/common/i2c_master.c b/common/i2c_master.c index d89e20b920..be371e52ed 100644 --- a/common/i2c_master.c +++ b/common/i2c_master.c @@ -15,8 +15,6 @@ #include "i2c.h" #include "system.h" #include "task.h" -#include "usb_pd.h" -#include "usb_pd_tcpm.h" #include "util.h" #include "watchdog.h" #include "virtual_battery.h" @@ -1085,25 +1083,6 @@ static void i2c_passthru_protect_port(uint32_t port) static void i2c_passthru_protect_tcpc_ports(void) { -#ifdef CONFIG_USB_PD_PORT_MAX_COUNT - int i; - - /* - * If WP is not enabled i.e. system is not locked leave the tunnels open - * so that factory line can do updates without a new RO BIOS. - */ - if (!system_is_locked()) { - CPRINTS("System unlocked, TCPC I2C tunnels may be unprotected"); - return; - } - - for (i = 0; i < board_get_usb_pd_port_count(); i++) { - /* TCPC tunnel not configured. No need to protect anything */ - if (!I2C_GET_ADDR(tcpc_config[i].i2c_info.addr_flags)) - continue; - i2c_passthru_protect_port(tcpc_config[i].i2c_info.port); - } -#endif } static enum ec_status diff --git a/common/mock/usb_mux_mock.c b/common/mock/usb_mux_mock.c deleted file mode 100644 index 28f07c54f7..0000000000 --- a/common/mock/usb_mux_mock.c +++ /dev/null @@ -1,51 +0,0 @@ -/* Copyright 2019 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. - */ -/* Mock USB Type-C mux */ - -#include "common.h" -#include "console.h" -#include "usb_mux.h" -#include "usb_mux_mock.h" -#include "memory.h" - -/* Public API for controlling/inspecting this mock */ -struct mock_usb_mux_ctrl mock_usb_mux; - -void mock_usb_mux_reset(void) -{ - memset(&mock_usb_mux, 0, sizeof(mock_usb_mux)); -} - -static int mock_init(int port) -{ - return EC_SUCCESS; -} - -static int mock_set(int port, mux_state_t mux_state) -{ - mock_usb_mux.state = mux_state; - ++mock_usb_mux.num_set_calls; - ccprints("Called into mux with %d", mux_state); - - return EC_SUCCESS; -} - -int mock_get(int port, mux_state_t *mux_state) -{ - *mux_state = mock_usb_mux.state; - return EC_SUCCESS; -} - -static int mock_enter_low_power_mode(int port) -{ - return EC_SUCCESS; -} - -const struct usb_mux_driver mock_usb_mux_driver = { - .init = &mock_init, - .set = &mock_set, - .get = &mock_get, - .enter_low_power_mode = &mock_enter_low_power_mode, -}; diff --git a/common/mock/usb_mux_mock.h b/common/mock/usb_mux_mock.h deleted file mode 100644 index 128286796b..0000000000 --- a/common/mock/usb_mux_mock.h +++ /dev/null @@ -1,19 +0,0 @@ -/* Copyright 2019 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. - */ -/* Mock USB Type-C mux */ - -#include "usb_mux.h" - -/* Controller for mux state */ -struct mock_usb_mux_ctrl { - mux_state_t state; - int num_set_calls; -}; - -/* Resets the state of the mock */ -void mock_usb_mux_reset(void); - -extern const struct usb_mux_driver mock_usb_mux_driver; -extern struct mock_usb_mux_ctrl mock_usb_mux; diff --git a/common/peripheral.c b/common/peripheral.c index 79a80b0ef9..9b77d49072 100644 --- a/common/peripheral.c +++ b/common/peripheral.c @@ -7,8 +7,6 @@ #include "compile_time_macros.h" #include "ec_commands.h" #include "host_command.h" -#include "usb_pd.h" -#include "usb_pd_tcpm.h" #ifdef CONFIG_HOSTCMD_LOCATE_CHIP static enum ec_status hc_locate_chip(struct host_cmd_handler_args *args) @@ -30,22 +28,7 @@ static enum ec_status hc_locate_chip(struct host_cmd_handler_args *args) #endif /* CONFIG_CROS_BOARD_INFO */ break; case EC_CHIP_TYPE_TCPC: -#if defined(CONFIG_USB_PD_PORT_MAX_COUNT) && !defined(CONFIG_USB_PD_TCPC) - if (params->index >= board_get_usb_pd_port_count()) - return EC_RES_OVERFLOW; - resp->bus_type = tcpc_config[params->index].bus_type; - if (resp->bus_type == EC_BUS_TYPE_I2C) { - resp->i2c_info.port = - tcpc_config[params->index].i2c_info.port; - resp->i2c_info.addr_flags = - tcpc_config[params->index].i2c_info.addr_flags; - } -#ifdef CONFIG_INTEL_VIRTUAL_MUX - resp->reserved = tcpc_config[params->index].usb23; -#endif -#else return EC_RES_UNAVAILABLE; -#endif /* CONFIG_USB_PD_PORT_MAX_COUNT */ break; default: /* The type was unrecognized */ diff --git a/common/rwsig.c b/common/rwsig.c index 8bccd2acb1..5e57f4cab5 100644 --- a/common/rwsig.c +++ b/common/rwsig.c @@ -18,7 +18,6 @@ #include "shared_mem.h" #include "system.h" #include "task.h" -#include "usb_pd.h" #include "util.h" #include "vb21_struct.h" #include "version.h" @@ -229,7 +228,6 @@ out: CPRINTS("RW verify %s", good ? "OK" : "FAILED"); if (!good) { - pd_log_event(PD_EVENT_ACC_RW_FAIL, 0, 0, NULL); /* RW firmware is invalid : do not jump there */ if (system_is_locked()) system_disable_jump(); diff --git a/common/system.c b/common/system.c index 036de2235e..62f7e6c40f 100644 --- a/common/system.c +++ b/common/system.c @@ -5,7 +5,6 @@ /* System module for Chrome EC : common functions */ #include "battery.h" -#include "charge_manager.h" #include "chipset.h" #include "clock.h" #include "common.h" @@ -33,8 +32,6 @@ #include "task.h" #include "timer.h" #include "uart.h" -#include "usb_pd.h" -#include "usb_pd_tcpm.h" #include "util.h" #include "version.h" #include "watchdog.h" @@ -848,24 +845,6 @@ static int handle_pending_reboot(enum ec_reboot_cmd cmd) case EC_REBOOT_JUMP_RW: return system_run_image_copy(system_get_active_copy()); case EC_REBOOT_COLD: -#ifdef HAS_TASK_PDCMD - /* - * Reboot the PD chip(s) as well, but first suspend the ports - * if this board has PD tasks running so they don't query the - * TCPCs while they reset. - */ -#ifdef HAS_TASK_PD_C0 - { - int port; - - for (port = 0; port < board_get_usb_pd_port_count(); - port++) - pd_set_suspend(port, 1); - } -#endif - board_reset_pd_mcu(); -#endif - cflush(); system_reset(SYSTEM_RESET_HARD); /* That shouldn't return... */ @@ -1531,9 +1510,6 @@ DECLARE_HOST_COMMAND(EC_CMD_REBOOT_EC, int system_can_boot_ap(void) { - int soc = -1; - int pow = -1; - #if defined(CONFIG_BATTERY) && \ defined(CONFIG_CHARGER_MIN_BAT_PCT_FOR_POWER_ON) /* Require a minimum battery level to power on. If battery isn't @@ -1543,18 +1519,8 @@ int system_can_boot_ap(void) return 1; #endif -#if defined(CONFIG_CHARGE_MANAGER) && \ - defined(CONFIG_CHARGER_MIN_POWER_MW_FOR_POWER_ON) - pow = charge_manager_get_power_limit_uw() / 1000; - if (pow >= CONFIG_CHARGER_MIN_POWER_MW_FOR_POWER_ON) - return 1; -#else /* For fixed AC system */ return 1; -#endif - - CPRINTS("Not enough power to boot (%d %%, %d mW)", soc, pow); - return 0; } #ifdef CONFIG_SERIALNO_LEN diff --git a/common/usb_charger.c b/common/usb_charger.c deleted file mode 100644 index 4a85dc6daf..0000000000 --- a/common/usb_charger.c +++ /dev/null @@ -1,120 +0,0 @@ -/* 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. - */ - -/* - * USB charger interface routines. This code assumes that CONFIG_CHARGE_MANAGER - * is defined and implemented. - * usb_charger_set_switches() must be implemented by a companion - * usb_switch driver. - * In addition, USB switch-specific usb_charger task or interrupt routine - * is necessary to update charge_manager with detected charger attributes. - */ - -#include "charge_manager.h" -#include "charger.h" -#include "common.h" -#include "console.h" -#include "gpio.h" -#include "hooks.h" -#include "stddef.h" -#include "task.h" -#include "usb_charge.h" -#include "usb_pd.h" -#include "usbc_ppc.h" - -static void update_vbus_supplier(int port, int vbus_level) -{ - struct charge_port_info charge = {0}; - - if (vbus_level && !usb_charger_port_is_sourcing_vbus(port)) { - charge.voltage = USB_CHARGER_VOLTAGE_MV; - charge.current = USB_CHARGER_MIN_CURR_MA; - } - - charge_manager_update_charge(CHARGE_SUPPLIER_VBUS, port, &charge); -} - -#ifdef CONFIG_USB_PD_5V_EN_CUSTOM -#define USB_5V_EN(port) board_is_sourcing_vbus(port) -#elif defined(CONFIG_USBC_PPC) -#define USB_5V_EN(port) ppc_is_sourcing_vbus(port) -#elif defined(CONFIG_USB_PD_5V_CHARGER_CTRL) -#define USB_5V_EN(port) charger_is_sourcing_otg_power(port) -#elif defined(CONFIG_USB_PD_5V_EN_ACTIVE_LOW) -#define USB_5V_EN(port) !gpio_get_level(GPIO_USB_C##port##_5V_EN_L) -#else -#define USB_5V_EN(port) gpio_get_level(GPIO_USB_C##port##_5V_EN) -#endif - -int usb_charger_port_is_sourcing_vbus(int port) -{ - if (port == 0) - return USB_5V_EN(0); -#if CONFIG_USB_PD_PORT_MAX_COUNT >= 2 - else if (port == 1) - return USB_5V_EN(1); -#endif - /* Not a valid port */ - return 0; -} - -void usb_charger_vbus_change(int port, int vbus_level) -{ - /* If VBUS has transitioned low, notify PD module directly */ - if (!vbus_level) - pd_vbus_low(port); - - /* Update VBUS supplier and signal VBUS change to USB_CHG task */ - update_vbus_supplier(port, vbus_level); - -#ifdef HAS_TASK_USB_CHG_P0 - /* USB Charger task(s) */ - task_set_event(USB_CHG_PORT_TO_TASK_ID(port), USB_CHG_EVENT_VBUS, 0); -#endif - -#if (defined(CONFIG_USB_PD_VBUS_DETECT_CHARGER) \ - || defined(CONFIG_USB_PD_VBUS_DETECT_PPC)) - /* USB PD task */ - task_wake(PD_PORT_TO_TASK_ID(port)); -#endif -} - -void usb_charger_reset_charge(int port) -{ - charge_manager_update_charge(CHARGE_SUPPLIER_PROPRIETARY, - port, NULL); - charge_manager_update_charge(CHARGE_SUPPLIER_BC12_CDP, - port, NULL); - charge_manager_update_charge(CHARGE_SUPPLIER_BC12_DCP, - port, NULL); - charge_manager_update_charge(CHARGE_SUPPLIER_BC12_SDP, - port, NULL); - charge_manager_update_charge(CHARGE_SUPPLIER_OTHER, - port, NULL); -#if CONFIG_DEDICATED_CHARGE_PORT_COUNT > 0 - charge_manager_update_charge(CHARGE_SUPPLIER_DEDICATED, - port, NULL); -#endif -#ifdef CONFIG_WIRELESS_CHARGER_P9221_R7 - charge_manager_update_charge(CHARGE_SUPPLIER_WPC_BPP, - port, NULL); - charge_manager_update_charge(CHARGE_SUPPLIER_WPC_EPP, - port, NULL); - charge_manager_update_charge(CHARGE_SUPPLIER_WPC_GPP, - port, NULL); -#endif - -} - -static void usb_charger_init(void) -{ - int i; - for (i = 0; i < board_get_usb_pd_port_count(); i++) { - usb_charger_reset_charge(i); - /* Initialize VBUS supplier based on whether VBUS is present. */ - update_vbus_supplier(i, pd_is_vbus_present(i)); - } -} -DECLARE_HOOK(HOOK_INIT, usb_charger_init, HOOK_PRIO_CHARGE_MANAGER_INIT + 1); diff --git a/common/usb_common.c b/common/usb_common.c deleted file mode 100644 index a068142ae5..0000000000 --- a/common/usb_common.c +++ /dev/null @@ -1,381 +0,0 @@ -/* Copyright 2019 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. - */ - -/* - * Contains common USB functions shared between the old (i.e. usb_pd_protocol) - * and the new (i.e. usb_sm_*) USB-C PD stacks. - */ - -#include "common.h" -#include "charge_state.h" -#include "task.h" -#include "usb_common.h" -#include "usb_pd.h" -#include "usb_pd_tcpm.h" -#include "util.h" - -int usb_get_battery_soc(void) -{ -#if defined(CONFIG_CHARGER) - return charge_get_percent(); -#elif defined(CONFIG_BATTERY) - return board_get_battery_soc(); -#else - return 0; -#endif -} - -/* - * CC values for regular sources and Debug sources (aka DTS) - * - * Source type Mode of Operation CC1 CC2 - * --------------------------------------------- - * Regular Default USB Power RpUSB Open - * Regular USB-C @ 1.5 A Rp1A5 Open - * Regular USB-C @ 3 A Rp3A0 Open - * DTS Default USB Power Rp3A0 Rp1A5 - * DTS USB-C @ 1.5 A Rp1A5 RpUSB - * DTS USB-C @ 3 A Rp3A0 RpUSB - */ - -typec_current_t usb_get_typec_current_limit(enum pd_cc_polarity_type polarity, - enum tcpc_cc_voltage_status cc1, enum tcpc_cc_voltage_status cc2) -{ - typec_current_t charge = 0; - enum tcpc_cc_voltage_status cc = polarity ? cc2 : cc1; - enum tcpc_cc_voltage_status cc_alt = polarity ? cc1 : cc2; - - switch (cc) { - case TYPEC_CC_VOLT_RP_3_0: - if (!cc_is_rp(cc_alt) || cc_alt == TYPEC_CC_VOLT_RP_DEF) - charge = 3000; - else if (cc_alt == TYPEC_CC_VOLT_RP_1_5) - charge = 500; - break; - case TYPEC_CC_VOLT_RP_1_5: - charge = 1500; - break; - case TYPEC_CC_VOLT_RP_DEF: - charge = 500; - break; - default: - break; - } - - if (IS_ENABLED(CONFIG_USBC_DISABLE_CHARGE_FROM_RP_DEF) && charge == 500) - charge = 0; - - if (cc_is_rp(cc_alt)) - charge |= TYPEC_CURRENT_DTS_MASK; - - return charge; -} - -enum pd_cc_polarity_type get_snk_polarity(enum tcpc_cc_voltage_status cc1, - enum tcpc_cc_voltage_status cc2) -{ - /* The following assumes: - * - * TYPEC_CC_VOLT_RP_3_0 > TYPEC_CC_VOLT_RP_1_5 - * TYPEC_CC_VOLT_RP_1_5 > TYPEC_CC_VOLT_RP_DEF - * TYPEC_CC_VOLT_RP_DEF > TYPEC_CC_VOLT_OPEN - */ - return cc2 > cc1; -} - -enum pd_cc_states pd_get_cc_state( - enum tcpc_cc_voltage_status cc1, enum tcpc_cc_voltage_status cc2) -{ - /* Port partner is a SNK */ - if (cc_is_snk_dbg_acc(cc1, cc2)) - return PD_CC_UFP_DEBUG_ACC; - if (cc_is_at_least_one_rd(cc1, cc2)) - return PD_CC_UFP_ATTACHED; - if (cc_is_audio_acc(cc1, cc2)) - return PD_CC_UFP_AUDIO_ACC; - - /* Port partner is a SRC */ - if (cc_is_rp(cc1) && cc_is_rp(cc2)) - return PD_CC_DFP_DEBUG_ACC; - if (cc_is_rp(cc1) || cc_is_rp(cc2)) - return PD_CC_DFP_ATTACHED; - - /* - * 1) Both lines are Vopen or - * 2) Only an e-marked cabled without a partner on the other side - */ - return PD_CC_NONE; -} -/* - * Zinger implements a board specific usb policy that does not define - * PD_MAX_VOLTAGE_MV and PD_OPERATING_POWER_MW. And in turn, does not - * use the following functions. - */ -#if defined(PD_MAX_VOLTAGE_MV) && defined(PD_OPERATING_POWER_MW) -int pd_find_pdo_index(uint32_t src_cap_cnt, const uint32_t * const src_caps, - int max_mv, uint32_t *selected_pdo) -{ - int i, uw, mv; - int ret = 0; - int cur_uw = 0; - int prefer_cur; - - int __attribute__((unused)) cur_mv = 0; - - /* max voltage is always limited by this boards max request */ - max_mv = MIN(max_mv, PD_MAX_VOLTAGE_MV); - - /* Get max power that is under our max voltage input */ - for (i = 0; i < src_cap_cnt; i++) { - /* its an unsupported Augmented PDO (PD3.0) */ - if ((src_caps[i] & PDO_TYPE_MASK) == PDO_TYPE_AUGMENTED) - continue; - - mv = ((src_caps[i] >> 10) & 0x3FF) * 50; - /* Skip invalid voltage */ - if (!mv) - continue; - /* Skip any voltage not supported by this board */ - if (!pd_is_valid_input_voltage(mv)) - continue; - - if ((src_caps[i] & PDO_TYPE_MASK) == PDO_TYPE_BATTERY) { - uw = 250000 * (src_caps[i] & 0x3FF); - } else { - int ma = (src_caps[i] & 0x3FF) * 10; - - ma = MIN(ma, PD_MAX_CURRENT_MA); - uw = ma * mv; - } - - if (mv > max_mv) - continue; - uw = MIN(uw, PD_MAX_POWER_MW * 1000); - prefer_cur = 0; - - /* Apply special rules in case of 'tie' */ - if (IS_ENABLED(PD_PREFER_LOW_VOLTAGE)) { - if (uw == cur_uw && mv < cur_mv) - prefer_cur = 1; - } else if (IS_ENABLED(PD_PREFER_HIGH_VOLTAGE)) { - if (uw == cur_uw && mv > cur_mv) - prefer_cur = 1; - } - - /* Prefer higher power, except for tiebreaker */ - if (uw > cur_uw || prefer_cur) { - ret = i; - cur_uw = uw; - cur_mv = mv; - } - } - - if (selected_pdo) - *selected_pdo = src_caps[ret]; - - return ret; -} - -void pd_extract_pdo_power(uint32_t pdo, uint32_t *ma, uint32_t *mv) -{ - int max_ma, uw; - - *mv = ((pdo >> 10) & 0x3FF) * 50; - - if (*mv == 0) { - *ma = 0; - return; - } - - if ((pdo & PDO_TYPE_MASK) == PDO_TYPE_BATTERY) { - uw = 250000 * (pdo & 0x3FF); - max_ma = 1000 * MIN(1000 * uw, PD_MAX_POWER_MW) / *mv; - } else { - max_ma = 10 * (pdo & 0x3FF); - max_ma = MIN(max_ma, PD_MAX_POWER_MW * 1000 / *mv); - } - - *ma = MIN(max_ma, PD_MAX_CURRENT_MA); -} - -void pd_build_request(uint32_t src_cap_cnt, const uint32_t * const src_caps, - int32_t vpd_vdo, uint32_t *rdo, uint32_t *ma, - uint32_t *mv, enum pd_request_type req_type, - uint32_t max_request_mv) -{ - uint32_t pdo; - int pdo_index, flags = 0; - int uw; - int max_or_min_ma; - int max_or_min_mw; - int max_vbus; - int vpd_vbus_dcr; - int vpd_gnd_dcr; - - if (req_type == PD_REQUEST_VSAFE5V) { - /* src cap 0 should be vSafe5V */ - pdo_index = 0; - pdo = src_caps[0]; - } else { - /* find pdo index for max voltage we can request */ - pdo_index = pd_find_pdo_index(src_cap_cnt, src_caps, - max_request_mv, &pdo); - } - - pd_extract_pdo_power(pdo, ma, mv); - - /* - * Adjust VBUS current if CTVPD device was detected. - */ - if (vpd_vdo > 0) { - max_vbus = VPD_VDO_MAX_VBUS(vpd_vdo); - vpd_vbus_dcr = VPD_VDO_VBUS_IMP(vpd_vdo) << 1; - vpd_gnd_dcr = VPD_VDO_GND_IMP(vpd_vdo); - - /* - * Valid max_vbus values: - * 00b - 20000 mV - * 01b - 30000 mV - * 10b - 40000 mV - * 11b - 50000 mV - */ - max_vbus = 20000 + max_vbus * 10000; - if (*mv > max_vbus) - *mv = max_vbus; - - /* - * 5000 mA cable: 150 = 750000 / 50000 - * 3000 mA cable: 250 = 750000 / 30000 - */ - if (*ma > 3000) - *ma = 750000 / (150 + vpd_vbus_dcr + vpd_gnd_dcr); - else - *ma = 750000 / (250 + vpd_vbus_dcr + vpd_gnd_dcr); - } - - uw = *ma * *mv; - /* Mismatch bit set if less power offered than the operating power */ - if (uw < (1000 * PD_OPERATING_POWER_MW)) - flags |= RDO_CAP_MISMATCH; - -#ifdef CONFIG_USB_PD_GIVE_BACK - /* Tell source we are give back capable. */ - flags |= RDO_GIVE_BACK; - - /* - * BATTERY PDO: Inform the source that the sink will reduce - * power to this minimum level on receipt of a GotoMin Request. - */ - max_or_min_mw = PD_MIN_POWER_MW; - - /* - * FIXED or VARIABLE PDO: Inform the source that the sink will - * reduce current to this minimum level on receipt of a GotoMin - * Request. - */ - max_or_min_ma = PD_MIN_CURRENT_MA; -#else - /* - * Can't give back, so set maximum current and power to - * operating level. - */ - max_or_min_ma = *ma; - max_or_min_mw = uw / 1000; -#endif - - if ((pdo & PDO_TYPE_MASK) == PDO_TYPE_BATTERY) { - int mw = uw / 1000; - *rdo = RDO_BATT(pdo_index + 1, mw, max_or_min_mw, flags); - } else { - *rdo = RDO_FIXED(pdo_index + 1, *ma, max_or_min_ma, flags); - } -} -#endif - -#ifdef CONFIG_USB_PD_ALT_MODE_DFP -void notify_sysjump_ready(volatile const task_id_t * const sysjump_task_waiting) -{ - /* - * If event was set from pd_prepare_sysjump, wake the - * task waiting on us to complete. - */ - if (*sysjump_task_waiting != TASK_ID_INVALID) - task_set_event(*sysjump_task_waiting, - TASK_EVENT_SYSJUMP_READY, 0); -} -#endif - -__attribute__((weak)) uint8_t board_get_usb_pd_port_count(void) -{ - return CONFIG_USB_PD_PORT_MAX_COUNT; -} - -enum pd_drp_next_states drp_auto_toggle_next_state( - uint64_t *drp_sink_time, - enum pd_power_role power_role, - enum pd_dual_role_states drp_state, - enum tcpc_cc_voltage_status cc1, - enum tcpc_cc_voltage_status cc2) -{ - /* Set to appropriate port state */ - if (cc_is_open(cc1, cc2)) { - /* - * If nothing is attached then use drp_state to determine next - * state. If DRP auto toggle is still on, then remain in the - * DRP_AUTO_TOGGLE state. Otherwise, stop dual role toggling - * and go to a disconnected state. - */ - switch (drp_state) { - case PD_DRP_TOGGLE_OFF: - return DRP_TC_DEFAULT; - case PD_DRP_FREEZE: - if (power_role == PD_ROLE_SINK) - return DRP_TC_UNATTACHED_SNK; - else - return DRP_TC_UNATTACHED_SRC; - case PD_DRP_FORCE_SINK: - return DRP_TC_UNATTACHED_SNK; - case PD_DRP_FORCE_SOURCE: - return DRP_TC_UNATTACHED_SRC; - case PD_DRP_TOGGLE_ON: - default: - return DRP_TC_DRP_AUTO_TOGGLE; - } - } else if ((cc_is_rp(cc1) || cc_is_rp(cc2)) && - drp_state != PD_DRP_FORCE_SOURCE) { - /* SNK allowed unless ForceSRC */ - return DRP_TC_UNATTACHED_SNK; - } else if (cc_is_at_least_one_rd(cc1, cc2) || - cc_is_audio_acc(cc1, cc2)) { - /* - * SRC allowed unless ForceSNK or Toggle Off - * - * Ideally we wouldn't use auto-toggle when drp_state is - * TOGGLE_OFF/FORCE_SINK, but for some TCPCs, auto-toggle can't - * be prevented in low power mode. Try being a sink in case the - * connected device is dual-role (this ensures reliable charging - * from a hub, b/72007056). 100 ms is enough time for a - * dual-role partner to switch from sink to source. If the - * connected device is sink-only, then we will attempt - * TC_UNATTACHED_SNK twice (due to debounce time), then return - * to low power mode (and stay there). After 200 ms, reset - * ready for a new connection. - */ - if (drp_state == PD_DRP_TOGGLE_OFF || - drp_state == PD_DRP_FORCE_SINK) { - if (get_time().val > *drp_sink_time + 200*MSEC) - *drp_sink_time = get_time().val; - if (get_time().val < *drp_sink_time + 100*MSEC) - return DRP_TC_UNATTACHED_SNK; - else - return DRP_TC_DRP_AUTO_TOGGLE; - } else { - return DRP_TC_UNATTACHED_SRC; - } - } else { - /* Anything else, keep toggling */ - return DRP_TC_DRP_AUTO_TOGGLE; - } -} diff --git a/common/usb_pd_policy.c b/common/usb_pd_policy.c deleted file mode 100644 index 2a843b97d0..0000000000 --- a/common/usb_pd_policy.c +++ /dev/null @@ -1,1158 +0,0 @@ -/* 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 "atomic.h" -#include "charge_manager.h" -#include "common.h" -#include "console.h" -#include "ec_commands.h" -#include "flash.h" -#include "gpio.h" -#include "hooks.h" -#include "host_command.h" -#include "mkbp_event.h" -#include "registers.h" -#include "rsa.h" -#include "sha256.h" -#include "system.h" -#include "task.h" -#include "tcpm.h" -#include "timer.h" -#include "util.h" -#include "usb_api.h" -#include "usb_common.h" -#include "usb_pd.h" -#include "usbc_ppc.h" -#include "version.h" - -#ifdef CONFIG_COMMON_RUNTIME -#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) -#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) -#else -#define CPRINTS(format, args...) -#define CPRINTF(format, args...) -#endif - -static int rw_flash_changed = 1; - -#ifdef CONFIG_MKBP_EVENT -static int dp_alt_mode_entry_get_next_event(uint8_t *data) -{ - return EC_SUCCESS; -} -DECLARE_EVENT_SOURCE(EC_MKBP_EVENT_DP_ALT_MODE_ENTERED, - dp_alt_mode_entry_get_next_event); - -void pd_notify_dp_alt_mode_entry(void) -{ - CPRINTS("Notifying AP of DP Alt Mode Entry..."); - mkbp_send_event(EC_MKBP_EVENT_DP_ALT_MODE_ENTERED); -} -#endif /* CONFIG_MKBP_EVENT */ - -int pd_check_requested_voltage(uint32_t rdo, const int port) -{ - int max_ma = rdo & 0x3FF; - int op_ma = (rdo >> 10) & 0x3FF; - int idx = RDO_POS(rdo); - uint32_t pdo; - uint32_t pdo_ma; -#if defined(CONFIG_USB_PD_DYNAMIC_SRC_CAP) || \ - defined(CONFIG_USB_PD_MAX_SINGLE_SOURCE_CURRENT) - const uint32_t *src_pdo; - const int pdo_cnt = charge_manager_get_source_pdo(&src_pdo, port); -#else - const uint32_t *src_pdo = pd_src_pdo; - const int pdo_cnt = pd_src_pdo_cnt; -#endif - - /* Board specific check for this request */ - if (pd_board_check_request(rdo, pdo_cnt)) - return EC_ERROR_INVAL; - - /* check current ... */ - pdo = 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 && !(rdo & RDO_CAP_MISMATCH)) - return EC_ERROR_INVAL; /* too much max current */ - - CPRINTF("Requested %d mV %d mA (for %d/%d mA)\n", - ((pdo >> 10) & 0x3ff) * 50, (pdo & 0x3ff) * 10, - op_ma * 10, max_ma * 10); - - /* Accept the requested voltage */ - return EC_SUCCESS; -} - -__attribute__((weak)) int pd_board_check_request(uint32_t rdo, int pdo_cnt) -{ - int idx = RDO_POS(rdo); - - /* Check for invalid index */ - return (!idx || idx > pdo_cnt) ? - EC_ERROR_INVAL : EC_SUCCESS; -} - -#ifdef CONFIG_USB_PD_DUAL_ROLE -/* Last received source cap */ -static uint32_t pd_src_caps[CONFIG_USB_PD_PORT_MAX_COUNT][PDO_MAX_OBJECTS]; -static uint8_t pd_src_cap_cnt[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/* Cap on the max voltage requested as a sink (in millivolts) */ -static unsigned max_request_mv = PD_MAX_VOLTAGE_MV; /* no cap */ - -const uint32_t * const pd_get_src_caps(int port) -{ - ASSERT(port < CONFIG_USB_PD_PORT_MAX_COUNT); - - return pd_src_caps[port]; -} - -uint8_t pd_get_src_cap_cnt(int port) -{ - ASSERT(port < CONFIG_USB_PD_PORT_MAX_COUNT); - - return pd_src_cap_cnt[port]; -} - -void pd_process_source_cap(int port, int cnt, uint32_t *src_caps) -{ -#ifdef CONFIG_CHARGE_MANAGER - uint32_t ma, mv, pdo; -#endif - int i; - - pd_src_cap_cnt[port] = cnt; - for (i = 0; i < cnt; i++) - pd_src_caps[port][i] = *src_caps++; - -#ifdef CONFIG_CHARGE_MANAGER - /* Get max power info that we could request */ - pd_find_pdo_index(pd_get_src_cap_cnt(port), pd_get_src_caps(port), - PD_MAX_VOLTAGE_MV, &pdo); - pd_extract_pdo_power(pdo, &ma, &mv); - - /* Set max. limit, but apply 500mA ceiling */ - charge_manager_set_ceil(port, CEIL_REQUESTOR_PD, PD_MIN_MA); - pd_set_input_current_limit(port, ma, mv); -#endif -} - -void pd_set_max_voltage(unsigned mv) -{ - max_request_mv = mv; -} - -unsigned pd_get_max_voltage(void) -{ - return max_request_mv; -} - -int pd_charge_from_device(uint16_t vid, uint16_t pid) -{ - /* TODO: rewrite into table if we get more of these */ - /* - * Allow-list Apple charge-through accessory since it doesn't set - * externally powered bit, but we still need to charge from it when - * we are a sink. - */ - return (vid == USB_VID_APPLE && (pid == 0x1012 || pid == 0x1013)); -} -#endif /* CONFIG_USB_PD_DUAL_ROLE */ - -static struct pd_cable cable[CONFIG_USB_PD_PORT_MAX_COUNT]; - -static uint8_t is_transmit_msg_sop_prime(int port) -{ - if (IS_ENABLED(CONFIG_USB_PD_DECODE_SOP)) - return !!(cable[port].flags & CABLE_FLAGS_SOP_PRIME_ENABLE); - - return 0; -} - -uint8_t is_sop_prime_ready(int port, uint8_t data_role, uint32_t pd_flags) -{ - /* - * Ref: USB PD 3.0 sec 2.5.4: When an Explicit Contract is in place the - * VCONN Source (either the DFP or the UFP) can communicate with the - * Cable Plug(s) using SOP’/SOP’’ Packets - * - * Ref: USB PD 2.0 sec 2.4.4: When an Explicit Contract is in place the - * DFP (either the Source or the Sink) can communicate with the - * Cable Plug(s) using SOP’/SOP” Packets. - * Sec 3.6.11 : Before communicating with a Cable Plug a Port Should - * ensure that it is the Vconn Source - */ - if (pd_flags & PD_FLAGS_VCONN_ON && (IS_ENABLED(CONFIG_USB_PD_REV30) || - data_role == PD_ROLE_DFP)) - return is_transmit_msg_sop_prime(port); - - return 0; -} - -void reset_pd_cable(int port) -{ - if (IS_ENABLED(CONFIG_USB_PD_DECODE_SOP)) - memset(&cable[port], 0, sizeof(cable[port])); -} - -enum idh_ptype get_usb_pd_mux_cable_type(int port) -{ - return cable[port].type; -} - -#ifdef CONFIG_USB_PD_ALT_MODE - -#ifdef CONFIG_USB_PD_ALT_MODE_DFP - -static struct pd_policy pe[CONFIG_USB_PD_PORT_MAX_COUNT]; - -static int is_vdo_present(int cnt, int index) -{ - return cnt > index; -} - -static void enable_transmit_sop_prime(int port) -{ - cable[port].flags |= CABLE_FLAGS_SOP_PRIME_ENABLE; -} - -static void disable_transmit_sop_prime(int port) -{ - cable[port].flags &= ~CABLE_FLAGS_SOP_PRIME_ENABLE; -} - -void pd_dfp_pe_init(int port) -{ - memset(&pe[port], 0, sizeof(struct pd_policy)); -} - -static void dfp_consume_identity(int port, int cnt, uint32_t *payload) -{ - int ptype = PD_IDH_PTYPE(payload[VDO_I(IDH)]); - size_t identity_size = MIN(sizeof(pe[port].identity), - (cnt - 1) * sizeof(uint32_t)); - pd_dfp_pe_init(port); - memcpy(&pe[port].identity, payload + 1, identity_size); - switch (ptype) { - case IDH_PTYPE_AMA: -/* Leave vbus ON if the following macro is false */ -#if defined(CONFIG_USB_PD_DUAL_ROLE) && defined(CONFIG_USBC_VCONN_SWAP) - /* Adapter is requesting vconn, try to supply it */ - if (PD_VDO_AMA_VCONN_REQ(payload[VDO_I(AMA)])) - pd_try_vconn_src(port); - - /* Only disable vbus if vconn was requested */ - if (PD_VDO_AMA_VCONN_REQ(payload[VDO_I(AMA)]) && - !PD_VDO_AMA_VBUS_REQ(payload[VDO_I(AMA)])) - pd_power_supply_reset(port); -#endif - break; - default: - break; - } -} - -static void dfp_consume_cable_response(int port, int cnt, uint32_t *payload) -{ - if (cable[port].is_identified) - return; - - if (is_vdo_present(cnt, VDO_INDEX_IDH)) { - cable[port].type = PD_IDH_PTYPE(payload[VDO_INDEX_IDH]); - if (is_vdo_present(cnt, VDO_INDEX_PTYPE_CABLE1)) - cable[port].attr.raw_value = - payload[VDO_INDEX_PTYPE_CABLE1]; - } - /* - * Ref USB PD Spec 3.0 Pg 145. For active cable there are two VDOs. - * Hence storing the second VDO. - */ - if (IS_ENABLED(CONFIG_USB_PD_REV30) && - is_vdo_present(cnt, VDO_INDEX_PTYPE_CABLE2) && - cable[port].type == IDH_PTYPE_ACABLE) { - cable[port].rev = PD_REV30; - cable[port].attr2.raw_value = payload[VDO_INDEX_PTYPE_CABLE2]; - } - cable[port].is_identified = 1; -} - -static int dfp_discover_ident(uint32_t *payload) -{ - payload[0] = VDO(USB_SID_PD, 1, CMD_DISCOVER_IDENT); - return 1; -} - -static int dfp_discover_svids(uint32_t *payload) -{ - payload[0] = VDO(USB_SID_PD, 1, CMD_DISCOVER_SVID); - return 1; -} - -static void dfp_consume_svids(int port, int cnt, uint32_t *payload) -{ - int i; - uint32_t *ptr = payload + 1; - int vdo = 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:SVIDCNT\n"); - break; - } - /* - * Verify we're still within the valid packet (count will be one - * for the VDM header + xVDOs) - */ - if (vdo >= cnt) - 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++; - vdo++; - } - /* TODO(tbroch) need to re-issue discover svids if > 12 */ - if (i && ((i % 12) == 0)) - CPRINTF("ERR:SVID+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_idx >= pe[port].svid_cnt) - return 0; - payload[0] = VDO(svid, 1, CMD_DISCOVER_MODES); - return 1; -} - -static void 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("ERR:NOMODE\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++; -} - -static int get_mode_idx(int port, uint16_t svid) -{ - int i; - - for (i = 0; i < PD_AMODE_COUNT; i++) { - if (pe[port].amodes[i].fx->svid == svid) - return i; - } - return -1; -} - -static struct svdm_amode_data *get_modep(int port, uint16_t svid) -{ - int idx = get_mode_idx(port, svid); - - return (idx == -1) ? NULL : &pe[port].amodes[idx]; -} - -int pd_alt_mode(int port, uint16_t svid) -{ - struct svdm_amode_data *modep = get_modep(port, svid); - - return (modep) ? modep->opos : -1; -} - -int allocate_mode(int port, uint16_t svid) -{ - int i, j; - struct svdm_amode_data *modep; - int mode_idx = get_mode_idx(port, svid); - - if (mode_idx != -1) - return mode_idx; - - /* There's no space to enter another mode */ - if (pe[port].amode_idx == PD_AMODE_COUNT) { - CPRINTF("ERR:NO AMODE SPACE\n"); - return -1; - } - - /* Allocate ... if SVID == 0 enter default supported policy */ - for (i = 0; i < supported_modes_cnt; i++) { - for (j = 0; j < pe[port].svid_cnt; j++) { - struct svdm_svid_data *svidp = &pe[port].svids[j]; - if ((svidp->svid != supported_modes[i].svid) || - (svid && (svidp->svid != svid))) - continue; - - modep = &pe[port].amodes[pe[port].amode_idx]; - modep->fx = &supported_modes[i]; - modep->data = &pe[port].svids[j]; - pe[port].amode_idx++; - return pe[port].amode_idx - 1; - } - } - return -1; -} - -/* - * Enter default mode ( payload[0] == 0 ) or attempt to enter mode via svid & - * opos -*/ -uint32_t pd_dfp_enter_mode(int port, uint16_t svid, int opos) -{ - int mode_idx = allocate_mode(port, svid); - struct svdm_amode_data *modep; - uint32_t mode_caps; - - if (mode_idx == -1) - return 0; - modep = &pe[port].amodes[mode_idx]; - - if (!opos) { - /* choose the lowest as default */ - modep->opos = 1; - } else if (opos <= modep->data->mode_cnt) { - modep->opos = opos; - } else { - CPRINTF("opos error\n"); - return 0; - } - - mode_caps = modep->data->mode_vdo[modep->opos - 1]; - if (modep->fx->enter(port, mode_caps) == -1) - return 0; - - /* SVDM to send to UFP for mode entry */ - return VDO(modep->fx->svid, 1, CMD_ENTER_MODE | VDO_OPOS(modep->opos)); -} - -static int validate_mode_request(struct svdm_amode_data *modep, - uint16_t svid, int opos) -{ - if (!modep->fx) - return 0; - - if (svid != modep->fx->svid) { - CPRINTF("ERR:svid r:0x%04x != c:0x%04x\n", - svid, modep->fx->svid); - return 0; - } - - if (opos != modep->opos) { - CPRINTF("ERR:opos r:%d != c:%d\n", - opos, modep->opos); - return 0; - } - - return 1; -} - -static void dfp_consume_attention(int port, uint32_t *payload) -{ - uint16_t svid = PD_VDO_VID(payload[0]); - int opos = PD_VDO_OPOS(payload[0]); - struct svdm_amode_data *modep = get_modep(port, svid); - - if (!modep || !validate_mode_request(modep, svid, opos)) - return; - - if (modep->fx->attention) - modep->fx->attention(port, payload); -} - -/* - * This algorithm defaults to choosing higher pin config over lower ones in - * order to prefer multi-function if desired. - * - * NAME | SIGNALING | OUTPUT TYPE | MULTI-FUNCTION | PIN CONFIG - * ------------------------------------------------------------- - * A | USB G2 | ? | no | 00_0001 - * B | USB G2 | ? | yes | 00_0010 - * C | DP | CONVERTED | no | 00_0100 - * D | PD | CONVERTED | yes | 00_1000 - * E | DP | DP | no | 01_0000 - * F | PD | DP | yes | 10_0000 - * - * if UFP has NOT asserted multi-function preferred code masks away B/D/F - * leaving only A/C/E. For single-output dongles that should leave only one - * possible pin config depending on whether its a converter DP->(VGA|HDMI) or DP - * output. If UFP is a USB-C receptacle it may assert C/D/E/F. The DFP USB-C - * receptacle must always choose C/D in those cases. - */ -int pd_dfp_dp_get_pin_mode(int port, uint32_t status) -{ - struct svdm_amode_data *modep = get_modep(port, USB_SID_DISPLAYPORT); - uint32_t mode_caps; - uint32_t pin_caps; - if (!modep) - return 0; - - mode_caps = modep->data->mode_vdo[modep->opos - 1]; - - /* TODO(crosbug.com/p/39656) revisit with DFP that can be a sink */ - pin_caps = PD_DP_PIN_CAPS(mode_caps); - - /* if don't want multi-function then ignore those pin configs */ - if (!PD_VDO_DPSTS_MF_PREF(status)) - pin_caps &= ~MODE_DP_PIN_MF_MASK; - - /* TODO(crosbug.com/p/39656) revisit if DFP drives USB Gen 2 signals */ - pin_caps &= ~MODE_DP_PIN_BR2_MASK; - - /* if C/D present they have precedence over E/F for USB-C->USB-C */ - if (pin_caps & (MODE_DP_PIN_C | MODE_DP_PIN_D)) - pin_caps &= ~(MODE_DP_PIN_E | MODE_DP_PIN_F); - - /* get_next_bit returns undefined for zero */ - if (!pin_caps) - return 0; - - return 1 << get_next_bit(&pin_caps); -} - -int pd_dfp_exit_mode(int port, uint16_t svid, int opos) -{ - struct svdm_amode_data *modep; - int idx; - - /* - * Empty svid signals we should reset DFP VDM state by exiting all - * entered modes then clearing state. This occurs when we've - * disconnected or for hard reset. - */ - if (!svid) { - for (idx = 0; idx < PD_AMODE_COUNT; idx++) - if (pe[port].amodes[idx].fx) - pe[port].amodes[idx].fx->exit(port); - - pd_dfp_pe_init(port); - return 0; - } - - /* - * TODO(crosbug.com/p/33946) : below needs revisited to allow multiple - * mode exit. Additionally it should honor OPOS == 7 as DFP's request - * to exit all modes. We currently don't have any UFPs that support - * multiple modes on one SVID. - */ - modep = get_modep(port, svid); - if (!modep || !validate_mode_request(modep, svid, opos)) - return 0; - - /* call DFPs exit function */ - modep->fx->exit(port); - /* exit the mode */ - modep->opos = 0; - return 1; -} - -uint16_t pd_get_identity_vid(int port) -{ - return PD_IDH_VID(pe[port].identity[0]); -} - -uint16_t pd_get_identity_pid(int port) -{ - return PD_PRODUCT_PID(pe[port].identity[2]); -} - -#ifdef CONFIG_CMD_USB_PD_PE -static void dump_pe(int port) -{ - const char * const idh_ptype_names[] = { - "UNDEF", "Hub", "Periph", "PCable", "ACable", "AMA", - "RSV6", "RSV7"}; - - int i, j, idh_ptype; - struct svdm_amode_data *modep; - uint32_t mode_caps; - - if (pe[port].identity[0] == 0) { - ccprintf("No identity discovered yet.\n"); - return; - } - idh_ptype = PD_IDH_PTYPE(pe[port].identity[0]); - ccprintf("IDENT:\n"); - ccprintf("\t[ID Header] %08x :: %s, VID:%04x\n", pe[port].identity[0], - idh_ptype_names[idh_ptype], pd_get_identity_vid(port)); - ccprintf("\t[Cert Stat] %08x\n", pe[port].identity[1]); - for (i = 2; i < ARRAY_SIZE(pe[port].identity); i++) { - ccprintf("\t"); - if (pe[port].identity[i]) - ccprintf("[%d] %08x ", i, pe[port].identity[i]); - } - ccprintf("\n"); - - 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"); - modep = get_modep(port, pe[port].svids[i].svid); - if (modep) { - mode_caps = modep->data->mode_vdo[modep->opos - 1]; - ccprintf("MODE[%d]: svid:%04x caps:%08x\n", modep->opos, - modep->fx->svid, 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 >= board_get_usb_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"); -#endif /* CONFIG_CMD_USB_PD_PE */ - -#endif /* CONFIG_USB_PD_ALT_MODE_DFP */ - -int pd_svdm(int port, int cnt, uint32_t *payload, uint32_t **rpayload) -{ - 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 */ - - 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: - if (svdm_rsp.amode) - func = svdm_rsp.amode->status; - break; - case CMD_DP_CONFIG: - if (svdm_rsp.amode) - func = svdm_rsp.amode->config; - break; - case CMD_EXIT_MODE: - func = svdm_rsp.exit_mode; - break; -#ifdef CONFIG_USB_PD_ALT_MODE_DFP - case CMD_ATTENTION: - /* - * attention is only SVDM with no response - * (just goodCRC) return zero here. - */ - dfp_consume_attention(port, payload); - return 0; -#endif - default: - CPRINTF("ERR:CMD:%d\n", cmd); - rsize = 0; - } - 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; - } - payload[0] |= VDO_SVDM_VERS(pd_get_vdo_ver(port)); - } else if (cmd_type == CMDT_RSP_ACK) { -#ifdef CONFIG_USB_PD_ALT_MODE_DFP - struct svdm_amode_data *modep; - - modep = get_modep(port, PD_VDO_VID(payload[0])); -#endif - switch (cmd) { -#ifdef CONFIG_USB_PD_ALT_MODE_DFP - case CMD_DISCOVER_IDENT: - /* Received a SOP Prime Discover Ident msg */ - if (is_transmit_msg_sop_prime(port)) { - /* Store cable type */ - dfp_consume_cable_response(port, cnt, payload); - disable_transmit_sop_prime(port); - rsize = dfp_discover_svids(payload); - /* Received a SOP Discover Ident Message */ - } else if (IS_ENABLED(CONFIG_USB_PD_DECODE_SOP)) { - dfp_consume_identity(port, cnt, payload); - /* Send SOP' Discover Ident message */ - if (!cable[port].is_identified) { - rsize = dfp_discover_ident(payload); - enable_transmit_sop_prime(port); - } - } else { - dfp_consume_identity(port, cnt, payload); - rsize = dfp_discover_svids(payload); - } -#ifdef CONFIG_CHARGE_MANAGER - if (pd_charge_from_device(pd_get_identity_vid(port), - pd_get_identity_pid(port))) - charge_manager_update_dualrole(port, - CAP_DEDICATED); -#endif - break; - case CMD_DISCOVER_SVID: - dfp_consume_svids(port, cnt, payload); - rsize = dfp_discover_modes(port, payload); - break; - case CMD_DISCOVER_MODES: - dfp_consume_modes(port, cnt, payload); - rsize = dfp_discover_modes(port, payload); - /* enter the default mode for DFP */ - if (!rsize) { - payload[0] = pd_dfp_enter_mode(port, 0, 0); - if (payload[0]) - rsize = 1; - } - break; - case CMD_ENTER_MODE: - if (!modep) { - rsize = 0; - } else { - if (!modep->opos) - pd_dfp_enter_mode(port, 0, 0); - - if (modep->opos) { - rsize = modep->fx->status(port, - payload); - payload[0] |= PD_VDO_OPOS(modep->opos); - } - } - break; - case CMD_DP_STATUS: - /* DP status response & UFP's DP attention have same - payload */ - dfp_consume_attention(port, payload); - if (modep && modep->opos) - rsize = modep->fx->config(port, payload); - else - rsize = 0; - break; - case CMD_DP_CONFIG: - if (modep && modep->opos && modep->fx->post_config) - modep->fx->post_config(port); - /* no response after DFPs ack */ - rsize = 0; - break; - case CMD_EXIT_MODE: - /* no response after DFPs ack */ - rsize = 0; - break; -#endif - case CMD_ATTENTION: - /* no response after DFPs ack */ - rsize = 0; - break; - default: - CPRINTF("ERR:CMD:%d\n", cmd); - rsize = 0; - } - - payload[0] |= VDO_CMDT(CMDT_INIT); - payload[0] |= VDO_SVDM_VERS(pd_get_vdo_ver(port)); -#ifdef CONFIG_USB_PD_ALT_MODE_DFP - } 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 */ - rsize = 1; - break; - case CMD_ENTER_MODE: - /* Error */ - CPRINTF("ERR:ENTBUSY\n"); - rsize = 0; - break; - case CMD_EXIT_MODE: - rsize = 0; - break; - default: - rsize = 0; - } - } else if (cmd_type == CMDT_RSP_NAK) { - rsize = 0; - /* Send SOP' Discover Ident message, if not already received. */ - if (IS_ENABLED(CONFIG_USB_PD_DECODE_SOP) && - !cable[port].is_identified && (cmd == CMD_DISCOVER_IDENT)) { - rsize = dfp_discover_ident(payload); - enable_transmit_sop_prime(port); - } -#endif /* CONFIG_USB_PD_ALT_MODE_DFP */ - } else { - CPRINTF("ERR:CMDT:%d\n", cmd); - /* do not answer */ - rsize = 0; - } - return rsize; -} - -#else - -int pd_svdm(int port, int cnt, uint32_t *payload, uint32_t **rpayload) -{ - return 0; -} - -#endif /* CONFIG_USB_PD_ALT_MODE */ - -#ifdef CONFIG_CMD_USB_PD_CABLE -static const char * const cable_type[] = { - [IDH_PTYPE_PCABLE] = "Passive", - [IDH_PTYPE_ACABLE] = "Active", -}; - -static const char * const cable_curr[] = { - [CABLE_CURRENT_3A] = "3A", - [CABLE_CURRENT_5A] = "5A", -}; - -static const char * const cable_ss_support[] = { - [USB_SS_U2_ONLY] = "Not supported", - [USB_SS_U31_GEN1] = "Gen 1", - [USB_SS_U31_GEN2] = "Gen 1 and Gen 2", -}; - -static const char * const vbus_max[] = { - [CABLE_VBUS_20V] = "20V", - [CABLE_VBUS_30V] = "30V", - [CABLE_VBUS_40V] = "40V", - [CABLE_VBUS_50V] = "50V", -}; -static const char * const conn_type[] = { - [CONNECTOR_ATYPE] = "Type A", - [CONNECTOR_BTYPE] = "Type B", - [CONNECTOR_CTYPE] = "Type C", - [CONNECTOR_CAPTIVE] = "Captive", -}; - -static int command_cable(int argc, char **argv) -{ - int port; - char *e; - - if (argc < 2) - return EC_ERROR_PARAM_COUNT; - port = strtoi(argv[1], &e, 0); - if (*e || port >= board_get_usb_pd_port_count()) - return EC_ERROR_PARAM2; - - if (!cable[port].is_identified) { - ccprintf("Cable not identified.\n"); - return EC_SUCCESS; - } - - ccprintf("Cable Type: "); - if (cable[port].type != IDH_PTYPE_PCABLE && - cable[port].type != IDH_PTYPE_ACABLE) { - ccprintf("Not Emark Cable\n"); - return EC_SUCCESS; - } - ccprintf("%s\n", cable_type[cable[port].type]); - - /* - * For rev 2.0, rev 3.0 active and passive cables have same bits for - * connector type (Bit 19:18) and current handling capability bit 6:5 - */ - ccprintf("Connector Type: %s\n", - cable[port].attr.rev20.connector > ARRAY_SIZE(conn_type) ? - "Invalid" : conn_type[cable[port].attr.rev20.connector]); - - if (cable[port].attr.rev20.current) { - ccprintf("Cable Current: %s\n", - cable[port].attr.rev20.current > ARRAY_SIZE(cable_curr) ? - "Invalid" : cable_curr[cable[port].attr.rev20.current]); - } else - ccprintf("Cable Current: Invalid\n"); - - /* - * For Rev 3.0 passive cables and Rev 2.0 active and passive cables, - * USB Superspeed Signaling support have same bits 2:0 - */ - if (cable[port].type == IDH_PTYPE_PCABLE) { - ccprintf("USB Superspeed Signaling support: %s\n", - cable[port].attr.rev20.ss > - ARRAY_SIZE(cable_ss_support) ? "Invalid" : - cable_ss_support[cable[port].attr.p_rev30.ss]); - } - - /* - * For Rev 3.0 active cables and Rev 2.0 active and passive cables, - * SOP" controller preset have same bit 3 - */ - if (cable[port].type == IDH_PTYPE_ACABLE) { - ccprintf("SOP' ' Controller: %s present\n", - cable[port].attr.rev20.controller ? "" : "Not"); - } - - if (cable[port].rev == PD_REV30) { - /* - * For Rev 3.0 active and passive cables, Max Vbus vtg have - * same bits 10:9. - */ - ccprintf("Max vbus voltage: %s\n", - cable[port].attr.p_rev30.vbus_max > - ARRAY_SIZE(vbus_max) ? "Invaild" : - vbus_max[cable[port].attr.p_rev30.vbus_max]); - - /* For Rev 3.0 Active cables */ - if (cable[port].type == IDH_PTYPE_ACABLE) { - ccprintf("SS signaling: USB_SS_GEN%u\n", - cable[port].attr2.a2_rev30.sss ? 2 : 1); - ccprintf("Number of SS lanes supported: %u\n", - cable[port].attr2.a2_rev30.lanes); - } - } - return EC_SUCCESS; -} - -DECLARE_CONSOLE_COMMAND(pdcable, command_cable, - "<port>", - "Cable Characteristics"); -#endif /* CONFIG_CMD_USB_PD_CABLE */ - -static void pd_usb_billboard_deferred(void) -{ -#if defined(CONFIG_USB_PD_ALT_MODE) && !defined(CONFIG_USB_PD_ALT_MODE_DFP) \ - && !defined(CONFIG_USB_PD_SIMPLE_DFP) && defined(CONFIG_USB_BOS) - - /* - * TODO(tbroch) - * 1. Will we have multiple type-C port UFPs - * 2. Will there be other modes applicable to DFPs besides DP - */ - if (!pd_alt_mode(0, USB_SID_DISPLAYPORT)) - usb_connect(); - -#endif -} -DECLARE_DEFERRED(pd_usb_billboard_deferred); - -#ifdef CONFIG_USB_PD_ALT_MODE_DFP -static enum ec_status hc_remote_pd_discovery(struct host_cmd_handler_args *args) -{ - const uint8_t *port = args->params; - struct ec_params_usb_pd_discovery_entry *r = args->response; - - if (*port >= board_get_usb_pd_port_count()) - return EC_RES_INVALID_PARAM; - - r->vid = pd_get_identity_vid(*port); - r->ptype = PD_IDH_PTYPE(pe[*port].identity[0]); - /* pid only included if vid is assigned */ - if (r->vid) - r->pid = PD_PRODUCT_PID(pe[*port].identity[2]); - - args->response_size = sizeof(*r); - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_USB_PD_DISCOVERY, - hc_remote_pd_discovery, - EC_VER_MASK(0)); - -static enum ec_status hc_remote_pd_get_amode(struct host_cmd_handler_args *args) -{ - struct svdm_amode_data *modep; - const struct ec_params_usb_pd_get_mode_request *p = args->params; - struct ec_params_usb_pd_get_mode_response *r = args->response; - - if (p->port >= board_get_usb_pd_port_count()) - return EC_RES_INVALID_PARAM; - - /* no more to send */ - if (p->svid_idx >= pe[p->port].svid_cnt) { - r->svid = 0; - args->response_size = sizeof(r->svid); - return EC_RES_SUCCESS; - } - - r->svid = pe[p->port].svids[p->svid_idx].svid; - r->opos = 0; - memcpy(r->vdo, pe[p->port].svids[p->svid_idx].mode_vdo, 24); - modep = get_modep(p->port, r->svid); - - if (modep) - r->opos = pd_alt_mode(p->port, r->svid); - - args->response_size = sizeof(*r); - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_USB_PD_GET_AMODE, - hc_remote_pd_get_amode, - EC_VER_MASK(0)); - -#endif - -#define FW_RW_END (CONFIG_EC_WRITABLE_STORAGE_OFF + \ - CONFIG_RW_STORAGE_OFF + CONFIG_RW_SIZE) - -uint8_t *flash_hash_rw(void) -{ - static struct sha256_ctx ctx; - - /* re-calculate RW hash when changed as its time consuming */ - if (rw_flash_changed) { - rw_flash_changed = 0; - SHA256_init(&ctx); - SHA256_update(&ctx, (void *)CONFIG_PROGRAM_MEMORY_BASE + - CONFIG_RW_MEM_OFF, - CONFIG_RW_SIZE - RSANUMBYTES); - return SHA256_final(&ctx); - } else { - return ctx.buf; - } -} - -void pd_get_info(uint32_t *info_data) -{ - void *rw_hash = flash_hash_rw(); - - /* copy first 20 bytes of RW hash */ - memcpy(info_data, rw_hash, 5 * sizeof(uint32_t)); - /* copy other info into data msg */ -#if defined(CONFIG_USB_PD_HW_DEV_ID_BOARD_MAJOR) && \ - defined(CONFIG_USB_PD_HW_DEV_ID_BOARD_MINOR) - info_data[5] = VDO_INFO(CONFIG_USB_PD_HW_DEV_ID_BOARD_MAJOR, - CONFIG_USB_PD_HW_DEV_ID_BOARD_MINOR, - ver_get_num_commits(system_get_image_copy()), - (system_get_image_copy() != SYSTEM_IMAGE_RO)); -#else - info_data[5] = 0; -#endif -} - -int pd_custom_flash_vdm(int port, int cnt, uint32_t *payload) -{ - static int flash_offset; - int rsize = 1; /* default is just VDM header returned */ - - switch (PD_VDO_CMD(payload[0])) { - case VDO_CMD_VERSION: - memcpy(payload + 1, ¤t_image_data.version, 24); - rsize = 7; - break; - case VDO_CMD_REBOOT: - /* ensure the power supply is in a safe state */ - pd_power_supply_reset(0); - system_reset(0); - break; - case VDO_CMD_READ_INFO: - /* copy info into response */ - pd_get_info(payload + 1); - rsize = 7; - break; - case VDO_CMD_FLASH_ERASE: - /* do not kill the code under our feet */ - if (system_get_image_copy() != SYSTEM_IMAGE_RO) - break; - pd_log_event(PD_EVENT_ACC_RW_ERASE, 0, 0, NULL); - flash_offset = CONFIG_EC_WRITABLE_STORAGE_OFF + - CONFIG_RW_STORAGE_OFF; - flash_physical_erase(CONFIG_EC_WRITABLE_STORAGE_OFF + - CONFIG_RW_STORAGE_OFF, CONFIG_RW_SIZE); - rw_flash_changed = 1; - break; - case VDO_CMD_FLASH_WRITE: - /* do not kill the code under our feet */ - if ((system_get_image_copy() != SYSTEM_IMAGE_RO) || - (flash_offset < CONFIG_EC_WRITABLE_STORAGE_OFF + - CONFIG_RW_STORAGE_OFF)) - break; - flash_physical_write(flash_offset, 4*(cnt - 1), - (const char *)(payload+1)); - flash_offset += 4*(cnt - 1); - rw_flash_changed = 1; - break; - case VDO_CMD_ERASE_SIG: - /* this is not touching the code area */ - { - uint32_t zero = 0; - int offset; - /* zeroes the area containing the RSA signature */ - for (offset = FW_RW_END - RSANUMBYTES; - offset < FW_RW_END; offset += 4) - flash_physical_write(offset, 4, - (const char *)&zero); - } - break; - default: - /* Unknown : do not answer */ - return 0; - } - return rsize; -} - -#ifdef CONFIG_USB_PD_DISCHARGE -void pd_set_vbus_discharge(int port, int enable) -{ - static struct mutex discharge_lock[CONFIG_USB_PD_PORT_MAX_COUNT]; - - mutex_lock(&discharge_lock[port]); - enable &= !board_vbus_source_enabled(port); -#ifdef CONFIG_USB_PD_DISCHARGE_GPIO - if (!port) - gpio_set_level(GPIO_USB_C0_DISCHARGE, enable); -#if CONFIG_USB_PD_PORT_MAX_COUNT > 1 - else - gpio_set_level(GPIO_USB_C1_DISCHARGE, enable); -#endif /* CONFIG_USB_PD_PORT_MAX_COUNT */ -#elif defined(CONFIG_USB_PD_DISCHARGE_TCPC) - tcpc_discharge_vbus(port, enable); -#elif defined(CONFIG_USB_PD_DISCHARGE_PPC) - ppc_discharge_vbus(port, enable); -#else -#error "PD discharge implementation not defined" -#endif - mutex_unlock(&discharge_lock[port]); -} -#endif /* CONFIG_USB_PD_DISCHARGE */ diff --git a/common/usb_pd_protocol.c b/common/usb_pd_protocol.c deleted file mode 100644 index 8303f9de62..0000000000 --- a/common/usb_pd_protocol.c +++ /dev/null @@ -1,5761 +0,0 @@ -/* 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 "atomic.h" -#include "battery.h" -#include "battery_smart.h" -#include "board.h" -#include "charge_manager.h" -#include "charge_state.h" -#include "chipset.h" -#include "common.h" -#include "console.h" -#include "ec_commands.h" -#include "gpio.h" -#include "hooks.h" -#include "host_command.h" -#include "registers.h" -#include "system.h" -#include "task.h" -#include "timer.h" -#include "util.h" -#include "usb_charge.h" -#include "usb_common.h" -#include "usb_mux.h" -#include "usb_pd.h" -#include "usb_pd_tcpm.h" -#include "usb_pd_tcpc.h" -#include "usbc_ppc.h" -#include "tcpm.h" -#include "version.h" -#include "vboot.h" - -#ifdef CONFIG_COMMON_RUNTIME -#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) -#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) - -BUILD_ASSERT(CONFIG_USB_PD_PORT_MAX_COUNT <= EC_USB_PD_MAX_PORTS); - -/* - * If we are trying to upgrade the TCPC port that is supplying power, then we - * need to ensure that the battery has enough charge for the upgrade. 100mAh - * is about 5% of most batteries, and it should be enough charge to get us - * through the EC jump to RW and PD upgrade. - */ -#define MIN_BATTERY_FOR_TCPC_UPGRADE_MAH 100 /* mAH */ - -/* - * Debug log level - higher number == more log - * Level 0: Log state transitions - * Level 1: Level 0, plus state name - * Level 2: Level 1, plus packet info - * Level 3: Level 2, plus ping packet and packet dump on error - * - * Note that higher log level causes timing changes and thus may affect - * performance. - * - * Can be limited to constant debug_level by CONFIG_USB_PD_DEBUG_LEVEL - */ -#ifdef CONFIG_USB_PD_DEBUG_LEVEL -static const int debug_level = CONFIG_USB_PD_DEBUG_LEVEL; -#else -static int debug_level; -#endif - -/* - * PD communication enabled flag. When false, PD state machine still - * detects source/sink connection and disconnection, and will still - * provide VBUS, but never sends any PD communication. - */ -static uint8_t pd_comm_enabled[CONFIG_USB_PD_PORT_MAX_COUNT]; -#else /* CONFIG_COMMON_RUNTIME */ -#define CPRINTF(format, args...) -#define CPRINTS(format, args...) -static const int debug_level; -#endif - -#ifdef CONFIG_USB_PD_DUAL_ROLE -#define DUAL_ROLE_IF_ELSE(port, sink_clause, src_clause) \ - (pd[port].power_role == PD_ROLE_SINK ? (sink_clause) : (src_clause)) -#else -#define DUAL_ROLE_IF_ELSE(port, sink_clause, src_clause) (src_clause) -#endif - -#define READY_RETURN_STATE(port) DUAL_ROLE_IF_ELSE(port, PD_STATE_SNK_READY, \ - PD_STATE_SRC_READY) - -/* Type C supply voltage (mV) */ -#define TYPE_C_VOLTAGE 5000 /* mV */ - -/* PD counter definitions */ -#define PD_MESSAGE_ID_COUNT 7 -#define PD_HARD_RESET_COUNT 2 -#define PD_CAPS_COUNT 50 -#define PD_SNK_CAP_RETRIES 3 - -/* - * The time that we allow the port partner to send any messages after an - * explicit contract is established. 200ms was chosen somewhat arbitrarily as - * it should be long enough for sources to decide to send a message if they were - * going to, but not so long that a "low power charger connected" notification - * would be shown in the chrome OS UI. - */ -#define SNK_READY_HOLD_OFF_US (200 * MSEC) -/* - * For the same purpose as SNK_READY_HOLD_OFF_US, but this delay can be longer - * since the concern over "low power charger" is not relevant when connected as - * a source and the additional delay avoids a race condition where the partner - * port sends a power role swap request close to when the VDM discover identity - * message gets sent. - */ -#define SRC_READY_HOLD_OFF_US (400 * MSEC) - -enum vdm_states { - VDM_STATE_ERR_BUSY = -3, - VDM_STATE_ERR_SEND = -2, - VDM_STATE_ERR_TMOUT = -1, - VDM_STATE_DONE = 0, - /* Anything >0 represents an active state */ - VDM_STATE_READY = 1, - VDM_STATE_BUSY = 2, - VDM_STATE_WAIT_RSP_BUSY = 3, -}; - -#ifdef CONFIG_USB_PD_DUAL_ROLE -/* Port dual-role state */ -enum pd_dual_role_states drp_state[CONFIG_USB_PD_PORT_MAX_COUNT] = { - [0 ... (CONFIG_USB_PD_PORT_MAX_COUNT - 1)] = - CONFIG_USB_PD_INITIAL_DRP_STATE}; - -/* Enable variable for Try.SRC states */ -static uint8_t pd_try_src_enable; -#endif - -#ifdef CONFIG_USB_PD_REV30 -/* - * The spec. revision is used to index into this array. - * Rev 0 (PD 1.0) - return PD_CTRL_REJECT - * Rev 1 (PD 2.0) - return PD_CTRL_REJECT - * Rev 2 (PD 3.0) - return PD_CTRL_NOT_SUPPORTED - */ -static const uint8_t refuse[] = { - PD_CTRL_REJECT, PD_CTRL_REJECT, PD_CTRL_NOT_SUPPORTED}; -#define REFUSE(r) refuse[r] -#else -#define REFUSE(r) PD_CTRL_REJECT -#endif - -#ifdef CONFIG_USB_PD_REV30 -/* - * The spec. revision is used to index into this array. - * Rev 0 (VDO 1.0) - return VDM_VER10 - * Rev 1 (VDO 1.0) - return VDM_VER10 - * Rev 2 (VDO 2.0) - return VDM_VER20 - */ -static const uint8_t vdo_ver[] = { - VDM_VER10, VDM_VER10, VDM_VER20}; -#define VDO_VER(v) vdo_ver[v] -#else -#define VDO_VER(v) VDM_VER10 -#endif - -#ifdef CONFIG_USB_PD_ALT_MODE_DFP -/* Tracker for which task is waiting on sysjump prep to finish */ -static volatile task_id_t sysjump_task_waiting = TASK_ID_INVALID; -#endif - -static struct pd_protocol { - /* current port power role (SOURCE or SINK) */ - uint8_t power_role; - /* current port data role (DFP or UFP) */ - uint8_t data_role; - /* 3-bit rolling message ID counter */ - uint8_t msg_id; - /* Port polarity : 0 => CC1 is CC line, 1 => CC2 is CC line */ - uint8_t polarity; - /* PD state for port */ - enum pd_states task_state; - /* PD state when we run state handler the last time */ - enum pd_states last_state; - /* bool: request state change to SUSPENDED */ - uint8_t req_suspend_state; - /* The state to go to after timeout */ - enum pd_states timeout_state; - /* port flags, see PD_FLAGS_* */ - uint32_t flags; - /* Timeout for the current state. Set to 0 for no timeout. */ - uint64_t timeout; - /* Time for source recovery after hard reset */ - uint64_t src_recover; - /* Time for CC debounce end */ - uint64_t cc_debounce; - /* The cc state */ - enum pd_cc_states cc_state; - /* status of last transmit */ - uint8_t tx_status; - - /* Last received */ - uint8_t last_msg_id; - - /* last requested voltage PDO index */ - int requested_idx; -#ifdef CONFIG_USB_PD_DUAL_ROLE - /* Current limit / voltage based on the last request message */ - uint32_t curr_limit; - uint32_t supply_voltage; - /* Signal charging update that affects the port */ - int new_power_request; - /* Store previously requested voltage request */ - int prev_request_mv; - /* Time for Try.SRC states */ - uint64_t try_src_marker; - uint64_t try_timeout; -#endif - -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER - /* Time to enter low power mode */ - uint64_t low_power_time; - /* Tasks to notify after TCPC has been reset */ - int tasks_waiting_on_reset; - /* Tasks preventing TCPC from entering low power mode */ - int tasks_preventing_lpm; -#endif - -#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE - /* - * Timer for handling TOGGLE_OFF/FORCE_SINK mode when auto-toggle - * enabled. See drp_auto_toggle_next_state() for details. - */ - uint64_t drp_sink_time; -#endif - - /* - * Time to ignore Vbus absence due to external IC debounce detection - * logic immediately after a power role swap. - */ - uint64_t vbus_debounce_time; - - /* PD state for Vendor Defined Messages */ - enum vdm_states vdm_state; - /* Timeout for the current vdm state. Set to 0 for no timeout. */ - timestamp_t vdm_timeout; - /* next Vendor Defined Message to send */ - uint32_t vdo_data[VDO_MAX_SIZE]; - uint8_t vdo_count; - /* VDO to retry if UFP responder replied busy. */ - uint32_t vdo_retry; - - /* Attached ChromeOS device id, RW hash, and current RO / RW image */ - uint16_t dev_id; - uint32_t dev_rw_hash[PD_RW_HASH_SIZE/4]; - enum ec_current_image current_image; -#ifdef CONFIG_USB_PD_REV30 - /* PD Collision avoidance buffer */ - uint16_t ca_buffered; - uint16_t ca_header; - uint32_t ca_buffer[PDO_MAX_OBJECTS]; - enum tcpm_transmit_type ca_type; - /* protocol revision */ - uint8_t rev; -#endif - /* - * Some port partners are really chatty after an explicit contract is - * established. Therefore, we allow this time for the port partner to - * send any messages in order to avoid a collision of sending messages - * of our own. - */ - uint64_t ready_state_holdoff_timer; - /* - * PD 2.0 spec, section 6.5.11.1 - * When we can give up on a HARD_RESET transmission. - */ - uint64_t hard_reset_complete_timer; -} pd[CONFIG_USB_PD_PORT_MAX_COUNT]; - -#ifdef CONFIG_COMMON_RUNTIME -static const char * const pd_state_names[] = { - "DISABLED", "SUSPENDED", - "SNK_DISCONNECTED", "SNK_DISCONNECTED_DEBOUNCE", - "SNK_HARD_RESET_RECOVER", - "SNK_DISCOVERY", "SNK_REQUESTED", "SNK_TRANSITION", "SNK_READY", - "SNK_SWAP_INIT", "SNK_SWAP_SNK_DISABLE", - "SNK_SWAP_SRC_DISABLE", "SNK_SWAP_STANDBY", "SNK_SWAP_COMPLETE", - "SRC_DISCONNECTED", "SRC_DISCONNECTED_DEBOUNCE", - "SRC_HARD_RESET_RECOVER", "SRC_STARTUP", - "SRC_DISCOVERY", "SRC_NEGOCIATE", "SRC_ACCEPTED", "SRC_POWERED", - "SRC_TRANSITION", "SRC_READY", "SRC_GET_SNK_CAP", "DR_SWAP", - "SRC_SWAP_INIT", "SRC_SWAP_SNK_DISABLE", "SRC_SWAP_SRC_DISABLE", - "SRC_SWAP_STANDBY", - "VCONN_SWAP_SEND", "VCONN_SWAP_INIT", "VCONN_SWAP_READY", - "SOFT_RESET", "HARD_RESET_SEND", "HARD_RESET_EXECUTE", "BIST_RX", - "BIST_TX", - "DRP_AUTO_TOGGLE", -}; -BUILD_ASSERT(ARRAY_SIZE(pd_state_names) == PD_STATE_COUNT); -#endif - -/* - * 4 entry rw_hash table of type-C devices that AP has firmware updates for. - */ -#ifdef CONFIG_COMMON_RUNTIME -#define RW_HASH_ENTRIES 4 -static struct ec_params_usb_pd_rw_hash_entry rw_hash_table[RW_HASH_ENTRIES]; -#endif - -int pd_comm_is_enabled(int port) -{ -#ifdef CONFIG_COMMON_RUNTIME - return pd_comm_enabled[port]; -#else - return 1; -#endif -} - -static inline void set_state_timeout(int port, - uint64_t timeout, - enum pd_states timeout_state) -{ - pd[port].timeout = timeout; - pd[port].timeout_state = timeout_state; -} - -#ifdef CONFIG_USB_PD_REV30 -int pd_get_rev(int port) -{ - return pd[port].rev; -} - -int pd_get_vdo_ver(int port) -{ - return vdo_ver[pd[port].rev]; -} -#endif - -/* Return flag for pd state is connected */ -int pd_is_connected(int port) -{ - if (pd[port].task_state == PD_STATE_DISABLED) - return 0; - -#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE - if (pd[port].task_state == PD_STATE_DRP_AUTO_TOGGLE) - return 0; -#endif - - return DUAL_ROLE_IF_ELSE(port, - /* sink */ - pd[port].task_state != PD_STATE_SNK_DISCONNECTED && - pd[port].task_state != PD_STATE_SNK_DISCONNECTED_DEBOUNCE, - /* source */ - pd[port].task_state != PD_STATE_SRC_DISCONNECTED && - pd[port].task_state != PD_STATE_SRC_DISCONNECTED_DEBOUNCE); -} - -/* - * Return true if partner port is a DTS or TS capable of entering debug - * mode (eg. is presenting Rp/Rp or Rd/Rd). - */ -int pd_ts_dts_plugged(int port) -{ - return pd[port].flags & PD_FLAGS_TS_DTS_PARTNER; -} - -/* Return true if partner port is known to be PD capable. */ -int pd_capable(int port) -{ - return pd[port].flags & PD_FLAGS_PREVIOUS_PD_CONN; -} - -/* - * Return true if partner port is capable of communication over USB data - * lines. - */ -int pd_get_partner_usb_comm_capable(int port) -{ - return pd[port].flags & PD_FLAGS_PARTNER_USB_COMM; -} - -#ifdef CONFIG_USB_PD_DUAL_ROLE -void pd_vbus_low(int port) -{ - pd[port].flags &= ~PD_FLAGS_VBUS_NEVER_LOW; -} -#endif - -int pd_is_vbus_present(int port) -{ -#ifdef CONFIG_USB_PD_VBUS_DETECT_TCPC - return tcpm_get_vbus_level(port); -#else - return pd_snk_is_vbus_provided(port); -#endif -} - -/** - * This function checks the current CC status of the port partner - * and returns true if the attached partner is UFP. - */ -int pd_partner_is_ufp(int port) -{ - return pd[port].cc_state == PD_CC_UFP_ATTACHED || - pd[port].cc_state == PD_CC_UFP_DEBUG_ACC || - pd[port].cc_state == PD_CC_UFP_AUDIO_ACC; -} - -int pd_is_debug_acc(int port) -{ - return pd[port].cc_state == PD_CC_UFP_DEBUG_ACC || - pd[port].cc_state == PD_CC_DFP_DEBUG_ACC; -} - -static void set_polarity(int port, int polarity) -{ - tcpm_set_polarity(port, polarity); -#ifdef CONFIG_USBC_PPC_POLARITY - ppc_set_polarity(port, polarity); -#endif /* defined(CONFIG_USBC_PPC_POLARITY) */ -} - -#ifdef CONFIG_USBC_VCONN -static void set_vconn(int port, int enable) -{ - /* - * We always need to tell the TCPC to enable Vconn first, otherwise some - * TCPCs get confused when a PPC sets secondary CC line to 5V and TCPC - * immediately disconnect. If there is a PPC, both devices will - * potentially source Vconn, but that should be okay since Vconn has - * "make before break" electrical requirements when swapping anyway. - */ - tcpm_set_vconn(port, enable); -#ifdef CONFIG_USBC_PPC_VCONN - ppc_set_vconn(port, enable); -#endif -} -#endif /* defined(CONFIG_USBC_VCONN) */ - -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER - -/* 10 ms is enough time for any TCPC transaction to complete. */ -#define PD_LPM_DEBOUNCE_US (10 * MSEC) - -/* This is only called from the PD tasks that owns the port. */ -static void handle_device_access(int port) -{ - /* This should only be called from the PD task */ - assert(port == TASK_ID_TO_PD_PORT(task_get_current())); - - pd[port].low_power_time = get_time().val + PD_LPM_DEBOUNCE_US; - if (pd[port].flags & PD_FLAGS_LPM_ENGAGED) { - CPRINTS("TCPC p%d Exit Low Power Mode", port); - pd[port].flags &= ~(PD_FLAGS_LPM_ENGAGED | - PD_FLAGS_LPM_REQUESTED); - /* - * Wake to ensure we make another pass through the main task - * loop after clearing the flags. - */ - task_wake(PD_PORT_TO_TASK_ID(port)); - } -} - -static int pd_device_in_low_power(int port) -{ - /* - * If we are actively waking the device up in the PD task, do not - * let TCPC operation wait or retry because we are in low power mode. - */ - if (port == TASK_ID_TO_PD_PORT(task_get_current()) && - (pd[port].flags & PD_FLAGS_LPM_TRANSITION)) - return 0; - - return pd[port].flags & PD_FLAGS_LPM_ENGAGED; -} - -static int reset_device_and_notify(int port) -{ - int rv; - int task, waiting_tasks; - - /* This should only be called from the PD task */ - assert(port == TASK_ID_TO_PD_PORT(task_get_current())); - - pd[port].flags |= PD_FLAGS_LPM_TRANSITION; - rv = tcpm_init(port); - pd[port].flags &= ~PD_FLAGS_LPM_TRANSITION; - - if (rv == EC_SUCCESS) - CPRINTS("TCPC p%d init ready", port); - else - CPRINTS("TCPC p%d init failed!", port); - - /* - * Before getting the other tasks that are waiting, clear the reset - * event from this PD task to prevent multiple reset/init events - * occurring. - * - * The double reset event happens when the higher priority PD interrupt - * task gets an interrupt during the above tcpm_init function. When that - * occurs, the higher priority task waits correctly for us to finish - * waking the TCPC, but it has also set PD_EVENT_TCPC_RESET again, which - * would result in a second, unnecessary init. - */ - atomic_clear(task_get_event_bitmap(task_get_current()), - PD_EVENT_TCPC_RESET); - - waiting_tasks = atomic_read_clear(&pd[port].tasks_waiting_on_reset); - - /* - * Now that we are done waking up the device, handle device access - * manually because we ignored it while waking up device. - */ - handle_device_access(port); - - /* Clear SW LPM state; the state machine will set it again if needed */ - pd[port].flags &= ~PD_FLAGS_LPM_REQUESTED; - - /* Wake up all waiting tasks. */ - while (waiting_tasks) { - task = __fls(waiting_tasks); - waiting_tasks &= ~BIT(task); - task_set_event(task, TASK_EVENT_PD_AWAKE, 0); - } - - return rv; -} - -static void pd_wait_for_wakeup(int port) -{ - if (port == TASK_ID_TO_PD_PORT(task_get_current())) { - /* If we are in the PD task, we can directly reset */ - reset_device_and_notify(port); - } else { - /* Otherwise, we need to wait for the TCPC reset to complete */ - atomic_or(&pd[port].tasks_waiting_on_reset, - 1 << task_get_current()); - /* - * NOTE: We could be sending the PD task the reset event while - * it is already processing the reset event. If that occurs, - * then we will reset the TCPC multiple times, which is - * undesirable but most likely benign. Empirically, this doesn't - * happen much, but it if starts occurring, we can add a guard - * to prevent/reduce it. - */ - task_set_event(PD_PORT_TO_TASK_ID(port), - PD_EVENT_TCPC_RESET, 0); - task_wait_event_mask(TASK_EVENT_PD_AWAKE, -1); - } -} - -void pd_wait_exit_low_power(int port) -{ - if (pd_device_in_low_power(port)) - pd_wait_for_wakeup(port); -} - -/* - * This can be called from any task. If we are in the PD task, we can handle - * immediately. Otherwise, we need to notify the PD task via event. - */ -void pd_device_accessed(int port) -{ - if (port == TASK_ID_TO_PD_PORT(task_get_current())) { - /* Ignore any access to device while it is waking up */ - if (pd[port].flags & PD_FLAGS_LPM_TRANSITION) - return; - - handle_device_access(port); - } else { - task_set_event(PD_PORT_TO_TASK_ID(port), - PD_EVENT_DEVICE_ACCESSED, 0); - } -} - -void pd_prevent_low_power_mode(int port, int prevent) -{ - const int current_task_mask = (1 << task_get_current()); - - if (prevent) - atomic_or(&pd[port].tasks_preventing_lpm, current_task_mask); - else - atomic_clear(&pd[port].tasks_preventing_lpm, current_task_mask); -} - -/* This is only called from the PD tasks that owns the port. */ -static void exit_low_power_mode(int port) -{ - if (pd[port].flags & PD_FLAGS_LPM_ENGAGED) - reset_device_and_notify(port); - else - pd[port].flags &= ~PD_FLAGS_LPM_REQUESTED; -} - -#else /* !CONFIG_USB_PD_TCPC_LOW_POWER */ - -/* We don't need to notify anyone if low power mode isn't involved. */ -static int reset_device_and_notify(int port) -{ - const int rv = tcpm_init(port); - - if (rv == EC_SUCCESS) - CPRINTS("TCPC p%d init ready", port); - else - CPRINTS("TCPC p%d init failed!", port); - - return rv; -} - -#endif /* CONFIG_USB_PD_TCPC_LOW_POWER */ - -#ifdef CONFIG_USB_PD_DUAL_ROLE -static int get_bbram_idx(int port) -{ - switch (port) { - case 2: - return SYSTEM_BBRAM_IDX_PD2; - case 1: - return SYSTEM_BBRAM_IDX_PD1; - case 0: - return SYSTEM_BBRAM_IDX_PD0; - default: - return -1; - } -} - -static int pd_get_saved_port_flags(int port, uint8_t *flags) -{ - if (system_get_bbram(get_bbram_idx(port), flags) != EC_SUCCESS) { -#ifndef CHIP_HOST - CPRINTS("PD NVRAM FAIL"); -#endif - return EC_ERROR_UNKNOWN; - } - - return EC_SUCCESS; -} - -static void pd_set_saved_port_flags(int port, uint8_t flags) -{ - if (system_set_bbram(get_bbram_idx(port), flags) != EC_SUCCESS) { -#ifndef CHIP_HOST - CPRINTS("PD NVRAM FAIL"); -#endif - } -} - -static void pd_update_saved_port_flags(int port, uint8_t flag, uint8_t val) -{ - uint8_t saved_flags; - - if (pd_get_saved_port_flags(port, &saved_flags) != EC_SUCCESS) - return; - - if (val) - saved_flags |= flag; - else - saved_flags &= ~flag; - - pd_set_saved_port_flags(port, saved_flags); -} -#endif /* defined(CONFIG_USB_PD_DUAL_ROLE) */ - -/** - * Invalidate last message received at the port when the port gets disconnected - * or reset(soft/hard). This is used to identify and handle the duplicate - * messages. - * - * @param port USB PD TCPC port number - */ -static void invalidate_last_message_id(int port) -{ - /* - * Message id starts from 0 to 7. If last_msg_id is initialized to 0, - * it will lead to repetitive message id with first received packet, - * so initialize it with an invalid value 0xff. - */ - pd[port].last_msg_id = 0xff; -} - -/** - * Identify and drop any duplicate messages received at the port. - * - * @param port USB PD TCPC port number - * @param msg_header Message Header containing the RX message ID - * @return 1 if the received message is a duplicate one, 0 otherwise. - */ -static int consume_repeat_message(int port, uint16_t msg_header) -{ - uint8_t msg_id = PD_HEADER_ID(msg_header); - - /* If repeat message ignore, except softreset control request. */ - if (PD_HEADER_TYPE(msg_header) == PD_CTRL_SOFT_RESET && - PD_HEADER_CNT(msg_header) == 0) { - return 0; - } else if (pd[port].last_msg_id != msg_id) { - pd[port].last_msg_id = msg_id; - } else if (pd[port].last_msg_id == msg_id) { - CPRINTF("C%d Repeat msg_id %d\n", port, msg_id); - return 1; - } - - return 0; -} - -/** - * Returns true if the port is currently in the try src state. - */ -static inline int is_try_src(int port) -{ - return pd[port].flags & PD_FLAGS_TRY_SRC; -} - -static inline void set_state(int port, enum pd_states next_state) -{ - enum pd_states last_state = pd[port].task_state; -#ifdef CONFIG_LOW_POWER_IDLE - int i; -#endif - - set_state_timeout(port, 0, 0); - pd[port].task_state = next_state; - - if (last_state == next_state) - return; - -#if defined(CONFIG_USBC_PPC) && defined(CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE) - /* If we're entering DRP_AUTO_TOGGLE, there is no sink connected. */ - if (next_state == PD_STATE_DRP_AUTO_TOGGLE) { - ppc_sink_is_connected(port, 0); - /* - * Clear the overcurrent event counter - * since we've detected a disconnect. - */ - ppc_clear_oc_event_counter(port); - } -#endif /* CONFIG_USBC_PPC && CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE */ - -#ifdef CONFIG_USB_PD_DUAL_ROLE -#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE - /* Clear flag to allow DRP auto toggle when possible */ - if (last_state != PD_STATE_DRP_AUTO_TOGGLE) - pd[port].flags &= ~PD_FLAGS_TCPC_DRP_TOGGLE; -#endif - - /* Ignore dual-role toggling between sink and source */ - if ((last_state == PD_STATE_SNK_DISCONNECTED && - next_state == PD_STATE_SRC_DISCONNECTED) || - (last_state == PD_STATE_SRC_DISCONNECTED && - next_state == PD_STATE_SNK_DISCONNECTED)) - return; - - if (next_state == PD_STATE_SRC_DISCONNECTED || - next_state == PD_STATE_SNK_DISCONNECTED) { -#ifdef CONFIG_USBC_PPC - enum tcpc_cc_voltage_status cc1, cc2; - - tcpm_get_cc(port, &cc1, &cc2); - /* - * Neither a debug accessory nor UFP attached. - * Tell the PPC module that there is no sink connected. - */ - if (!cc_is_at_least_one_rd(cc1, cc2)) { - ppc_sink_is_connected(port, 0); - /* - * Clear the overcurrent event counter - * since we've detected a disconnect. - */ - ppc_clear_oc_event_counter(port); - } -#endif /* CONFIG_USBC_PPC */ - /* Clear the holdoff timer since the port is disconnected. */ - pd[port].ready_state_holdoff_timer = 0; - - /* - * We should not clear any flags when transitioning back to the - * disconnected state from the debounce state as the two states - * here are really the same states in the state diagram. - */ - if (last_state != PD_STATE_SNK_DISCONNECTED_DEBOUNCE && - last_state != PD_STATE_SRC_DISCONNECTED_DEBOUNCE) { - pd[port].flags &= ~PD_FLAGS_RESET_ON_DISCONNECT_MASK; - reset_pd_cable(port); - } - - /* Clear the input current limit */ - pd_set_input_current_limit(port, 0, 0); -#ifdef CONFIG_CHARGE_MANAGER - typec_set_input_current_limit(port, 0, 0); - charge_manager_set_ceil(port, - CEIL_REQUESTOR_PD, - CHARGE_CEIL_NONE); -#endif -#ifdef CONFIG_BC12_DETECT_DATA_ROLE_TRIGGER - /* - * When data role set events are used to enable BC1.2, then CC - * detach events are used to notify BC1.2 that it can be powered - * down. - */ - task_set_event(USB_CHG_PORT_TO_TASK_ID(port), - USB_CHG_EVENT_CC_OPEN, 0); -#endif /* CONFIG_BC12_DETECT_DATA_ROLE_TRIGGER */ -#ifdef CONFIG_USBC_VCONN - set_vconn(port, 0); -#endif /* defined(CONFIG_USBC_VCONN) */ - pd_update_saved_port_flags(port, PD_BBRMFLG_EXPLICIT_CONTRACT, - 0); -#else /* CONFIG_USB_PD_DUAL_ROLE */ - if (next_state == PD_STATE_SRC_DISCONNECTED) { -#ifdef CONFIG_USBC_VCONN - set_vconn(port, 0); -#endif /* CONFIG_USBC_VCONN */ -#endif /* !CONFIG_USB_PD_DUAL_ROLE */ - /* If we are source, make sure VBUS is off and restore RP */ - if (pd[port].power_role == PD_ROLE_SOURCE) { - /* Restore non-active ports to CONFIG_USB_PD_PULLUP */ - pd_power_supply_reset(port); - tcpm_set_cc(port, TYPEC_CC_RP); - } -#ifdef CONFIG_USB_PD_REV30 - /* Adjust rev to highest level*/ - pd[port].rev = PD_REV30; -#endif - pd[port].dev_id = 0; -#ifdef CONFIG_CHARGE_MANAGER - charge_manager_update_dualrole(port, CAP_UNKNOWN); -#endif -#ifdef CONFIG_USB_PD_ALT_MODE_DFP - pd_dfp_exit_mode(port, 0, 0); -#endif - /* - * Indicate that the port is disconnected by setting role to - * DFP as SoCs have special signals when they are the UFP ports - * (e.g. OTG signals) - */ - pd_execute_data_swap(port, PD_ROLE_DFP); -#ifdef CONFIG_USBC_SS_MUX - usb_mux_set(port, TYPEC_MUX_NONE, USB_SWITCH_DISCONNECT, - pd[port].polarity); -#endif - /* Disable TCPC RX */ - tcpm_set_rx_enable(port, 0); - - /* Invalidate message IDs. */ - invalidate_last_message_id(port); -#ifdef CONFIG_COMMON_RUNTIME - /* detect USB PD cc disconnect */ - hook_notify(HOOK_USB_PD_DISCONNECT); -#endif - } - -#ifdef CONFIG_LOW_POWER_IDLE - /* If a PD device is attached then disable deep sleep */ - for (i = 0; i < board_get_usb_pd_port_count(); i++) { - if (pd_capable(i)) - break; - } - if (i == board_get_usb_pd_port_count()) - enable_sleep(SLEEP_MASK_USB_PD); - else - disable_sleep(SLEEP_MASK_USB_PD); -#endif - - if (debug_level > 0) - CPRINTF("C%d st%d %s\n", port, next_state, - pd_state_names[next_state]); - else - CPRINTF("C%d st%d\n", port, next_state); -} - -/* increment message ID counter */ -static void inc_id(int port) -{ - pd[port].msg_id = (pd[port].msg_id + 1) & PD_MESSAGE_ID_COUNT; -} - -#ifdef CONFIG_USB_PD_REV30 -static void sink_can_xmit(int port, int rp) -{ - tcpm_select_rp_value(port, rp); - tcpm_set_cc(port, TYPEC_CC_RP); -} - -static inline void pd_ca_reset(int port) -{ - pd[port].ca_buffered = 0; -} -#endif - -void pd_transmit_complete(int port, int status) -{ - if (status == TCPC_TX_COMPLETE_SUCCESS) - inc_id(port); - - pd[port].tx_status = status; - task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_TX, 0); -} - -static int pd_transmit(int port, enum tcpm_transmit_type type, - uint16_t header, const uint32_t *data) -{ - int evt; - - /* If comms are disabled, do not transmit, return error */ - if (!pd_comm_is_enabled(port)) - return -1; - - /* Don't try to transmit anything until we have processed - * all RX messages. - */ - if (tcpm_has_pending_message(port)) - return -1; - -#ifdef CONFIG_USB_PD_REV30 - /* Source-coordinated collision avoidance */ - /* - * In order to avoid message collisions due to asynchronous Messaging - * sent from the Sink, the Source sets Rp to SinkTxOk to indicate to - * the Sink that it is ok to initiate an AMS. When the Source wishes - * to initiate an AMS it sets Rp to SinkTxNG. When the Sink detects - * that Rp is set to SinkTxOk it May initiate an AMS. When the Sink - * detects that Rp is set to SinkTxNG it Shall Not initiate an AMS - * and Shall only send Messages that are part of an AMS the Source has - * initiated. Note that this restriction applies to SOP* AMS’s i.e. - * for both Port to Port and Port to Cable Plug communications. - * - * This starts after an Explicit Contract is in place - * PD R3 V1.1 Section 2.5.2. - * - * Note: a Sink can still send Hard Reset signaling at any time. - */ - if ((pd[port].rev == PD_REV30) && - (pd[port].flags & PD_FLAGS_EXPLICIT_CONTRACT)) { - if (pd[port].power_role == PD_ROLE_SOURCE) { - /* - * Inform Sink that it can't transmit. If a sink - * transmition is in progress and a collsion occurs, - * a reset is generated. This should be rare because - * all extended messages are chunked. This effectively - * defaults to PD REV 2.0 collision avoidance. - */ - sink_can_xmit(port, SINK_TX_NG); - } else if (type != TCPC_TX_HARD_RESET) { - enum tcpc_cc_voltage_status cc1, cc2; - - tcpm_get_cc(port, &cc1, &cc2); - if (cc1 == TYPEC_CC_VOLT_RP_1_5 || - cc2 == TYPEC_CC_VOLT_RP_1_5) { - /* Sink can't transmit now. */ - /* Check if message is already buffered. */ - if (pd[port].ca_buffered) - return -1; - - /* Buffer message and send later. */ - pd[port].ca_type = type; - pd[port].ca_header = header; - memcpy(pd[port].ca_buffer, - data, sizeof(uint32_t) * - PD_HEADER_CNT(header)); - pd[port].ca_buffered = 1; - return 1; - } - } - } -#endif - tcpm_transmit(port, type, header, data); - - /* Wait until TX is complete */ - evt = task_wait_event_mask(PD_EVENT_TX, PD_T_TCPC_TX_TIMEOUT); - -#ifdef CONFIG_USB_PD_REV30 - /* - * If the source just completed a transmit, tell - * the sink it can transmit if it wants to. - */ - if ((pd[port].rev == PD_REV30) && - (pd[port].power_role == PD_ROLE_SOURCE) && - (pd[port].flags & PD_FLAGS_EXPLICIT_CONTRACT)) { - sink_can_xmit(port, SINK_TX_OK); - } -#endif - - if (evt & TASK_EVENT_TIMER) - return -1; - - /* TODO: give different error condition for failed vs discarded */ - return pd[port].tx_status == TCPC_TX_COMPLETE_SUCCESS ? 1 : -1; -} - -#ifdef CONFIG_USB_PD_REV30 -static void pd_ca_send_pending(int port) -{ - enum tcpc_cc_voltage_status cc1, cc2; - - /* Check if a message has been buffered. */ - if (!pd[port].ca_buffered) - return; - - tcpm_get_cc(port, &cc1, &cc2); - if ((cc1 != TYPEC_CC_VOLT_RP_1_5) && - (cc2 != TYPEC_CC_VOLT_RP_1_5)) - if (pd_transmit(port, pd[port].ca_type, - pd[port].ca_header, - pd[port].ca_buffer) < 0) - return; - - /* Message was sent, so free up the buffer. */ - pd[port].ca_buffered = 0; -} -#endif - -static void pd_update_roles(int port) -{ - /* Notify TCPC of role update */ - tcpm_set_msg_header(port, pd[port].power_role, pd[port].data_role); -} - -static int send_control(int port, int type) -{ - int bit_len; - uint16_t header = PD_HEADER(type, pd[port].power_role, - pd[port].data_role, pd[port].msg_id, 0, - pd_get_rev(port), 0); - - bit_len = pd_transmit(port, TCPC_TX_SOP, header, NULL); - if (debug_level >= 2) - CPRINTF("C%d CTRL[%d]>%d\n", port, type, bit_len); - - return bit_len; -} - -static int send_source_cap(int port) -{ - int bit_len; -#if defined(CONFIG_USB_PD_DYNAMIC_SRC_CAP) || \ - defined(CONFIG_USB_PD_MAX_SINGLE_SOURCE_CURRENT) - const uint32_t *src_pdo; - const int src_pdo_cnt = charge_manager_get_source_pdo(&src_pdo, port); -#else - const uint32_t *src_pdo = pd_src_pdo; - const int src_pdo_cnt = pd_src_pdo_cnt; -#endif - uint16_t header; - - if (src_pdo_cnt == 0) - /* No source capabilities defined, sink only */ - header = PD_HEADER(PD_CTRL_REJECT, pd[port].power_role, - pd[port].data_role, pd[port].msg_id, 0, - pd_get_rev(port), 0); - else - header = PD_HEADER(PD_DATA_SOURCE_CAP, pd[port].power_role, - pd[port].data_role, pd[port].msg_id, src_pdo_cnt, - pd_get_rev(port), 0); - - bit_len = pd_transmit(port, TCPC_TX_SOP, header, src_pdo); - if (debug_level >= 2) - CPRINTF("C%d srcCAP>%d\n", port, bit_len); - - return bit_len; -} - -#ifdef CONFIG_USB_PD_REV30 -static int send_battery_cap(int port, uint32_t *payload) -{ - int bit_len; - uint16_t msg[6] = {0, 0, 0, 0, 0, 0}; - uint16_t header = PD_HEADER(PD_EXT_BATTERY_CAP, - pd[port].power_role, - pd[port].data_role, - pd[port].msg_id, - 3, /* Number of Data Objects */ - pd[port].rev, - 1 /* This is an exteded message */ - ); - - /* Set extended header */ - msg[0] = PD_EXT_HEADER(0, /* Chunk Number */ - 0, /* Request Chunk */ - 9 /* Data Size in bytes */ - ); - /* Set VID */ - msg[1] = USB_VID_GOOGLE; - - /* Set PID */ - msg[2] = CONFIG_USB_PID; - - if (battery_is_present()) { - /* - * We only have one fixed battery, - * so make sure batt cap ref is 0. - */ - if (BATT_CAP_REF(payload[0]) != 0) { - /* Invalid battery reference */ - msg[5] = 1; - } else { - uint32_t v; - uint32_t c; - - /* - * The Battery Design Capacity field shall return the - * Battery’s design capacity in tenths of Wh. If the - * Battery is Hot Swappable and is not present, the - * Battery Design Capacity field shall be set to 0. If - * the Battery is unable to report its Design Capacity, - * it shall return 0xFFFF - */ - msg[3] = 0xffff; - - /* - * The Battery Last Full Charge Capacity field shall - * return the Battery’s last full charge capacity in - * tenths of Wh. If the Battery is Hot Swappable and - * is not present, the Battery Last Full Charge Capacity - * field shall be set to 0. If the Battery is unable to - * report its Design Capacity, the Battery Last Full - * Charge Capacity field shall be set to 0xFFFF. - */ - msg[4] = 0xffff; - - if (battery_design_voltage(&v) == 0) { - if (battery_design_capacity(&c) == 0) { - /* - * Wh = (c * v) / 1000000 - * 10th of a Wh = Wh * 10 - */ - msg[3] = DIV_ROUND_NEAREST((c * v), - 100000); - } - - if (battery_full_charge_capacity(&c) == 0) { - /* - * Wh = (c * v) / 1000000 - * 10th of a Wh = Wh * 10 - */ - msg[4] = DIV_ROUND_NEAREST((c * v), - 100000); - } - } - } - } - - bit_len = pd_transmit(port, TCPC_TX_SOP, header, (uint32_t *)msg); - if (debug_level >= 2) - CPRINTF("C%d batCap>%d\n", port, bit_len); - return bit_len; -} - -static int send_battery_status(int port, uint32_t *payload) -{ - int bit_len; - uint32_t msg = 0; - uint16_t header = PD_HEADER(PD_DATA_BATTERY_STATUS, - pd[port].power_role, - pd[port].data_role, - pd[port].msg_id, - 1, /* Number of Data Objects */ - pd[port].rev, - 0 /* This is NOT an extended message */ - ); - - if (battery_is_present()) { - /* - * We only have one fixed battery, - * so make sure batt cap ref is 0. - */ - if (BATT_CAP_REF(payload[0]) != 0) { - /* Invalid battery reference */ - msg |= BSDO_INVALID; - } else { - uint32_t v; - uint32_t c; - - if (battery_design_voltage(&v) != 0 || - battery_remaining_capacity(&c) != 0) { - msg |= BSDO_CAP(BSDO_CAP_UNKNOWN); - } else { - /* - * Wh = (c * v) / 1000000 - * 10th of a Wh = Wh * 10 - */ - msg |= BSDO_CAP(DIV_ROUND_NEAREST((c * v), - 100000)); - } - - /* Battery is present */ - msg |= BSDO_PRESENT; - - /* - * For drivers that are not smart battery compliant, - * battery_status() returns EC_ERROR_UNIMPLEMENTED and - * the battery is assumed to be idle. - */ - if (battery_status(&c) != 0) { - msg |= BSDO_IDLE; /* assume idle */ - } else { - if (c & STATUS_FULLY_CHARGED) - /* Fully charged */ - msg |= BSDO_IDLE; - else if (c & STATUS_DISCHARGING) - /* Discharging */ - msg |= BSDO_DISCHARGING; - /* else battery is charging.*/ - } - } - } else { - msg = BSDO_CAP(BSDO_CAP_UNKNOWN); - } - - bit_len = pd_transmit(port, TCPC_TX_SOP, header, &msg); - if (debug_level >= 2) - CPRINTF("C%d batStat>%d\n", port, bit_len); - - return bit_len; -} -#endif - -#ifdef CONFIG_USB_PD_DUAL_ROLE -static void send_sink_cap(int port) -{ - int bit_len; - uint16_t header = PD_HEADER(PD_DATA_SINK_CAP, pd[port].power_role, - pd[port].data_role, pd[port].msg_id, pd_snk_pdo_cnt, - pd_get_rev(port), 0); - - bit_len = pd_transmit(port, TCPC_TX_SOP, header, pd_snk_pdo); - if (debug_level >= 2) - CPRINTF("C%d snkCAP>%d\n", port, bit_len); -} - -static int send_request(int port, uint32_t rdo) -{ - int bit_len; - uint16_t header = PD_HEADER(PD_DATA_REQUEST, pd[port].power_role, - pd[port].data_role, pd[port].msg_id, 1, - pd_get_rev(port), 0); - - bit_len = pd_transmit(port, TCPC_TX_SOP, header, &rdo); - if (debug_level >= 2) - CPRINTF("C%d REQ>%d\n", port, bit_len); - - return bit_len; -} - -#endif /* CONFIG_USB_PD_DUAL_ROLE */ - -#ifdef CONFIG_COMMON_RUNTIME -static int send_bist_cmd(int port) -{ - /* currently only support sending bist carrier 2 */ - uint32_t bdo = BDO(BDO_MODE_CARRIER2, 0); - int bit_len; - uint16_t header = PD_HEADER(PD_DATA_BIST, pd[port].power_role, - pd[port].data_role, pd[port].msg_id, 1, - pd_get_rev(port), 0); - - bit_len = pd_transmit(port, TCPC_TX_SOP, header, &bdo); - CPRINTF("C%d BIST>%d\n", port, bit_len); - - return bit_len; -} -#endif - -static void queue_vdm(int port, uint32_t *header, const uint32_t *data, - int data_cnt) -{ - pd[port].vdo_count = data_cnt + 1; - pd[port].vdo_data[0] = header[0]; - memcpy(&pd[port].vdo_data[1], data, sizeof(uint32_t) * data_cnt); - /* Set ready, pd task will actually send */ - pd[port].vdm_state = VDM_STATE_READY; -} - -static void handle_vdm_request(int port, int cnt, uint32_t *payload) -{ - int rlen = 0; - uint32_t *rdata; - - if (pd[port].vdm_state == VDM_STATE_BUSY) { - /* If UFP responded busy retry after timeout */ - if (PD_VDO_CMDT(payload[0]) == CMDT_RSP_BUSY) { - pd[port].vdm_timeout.val = get_time().val + - PD_T_VDM_BUSY; - pd[port].vdm_state = VDM_STATE_WAIT_RSP_BUSY; - pd[port].vdo_retry = (payload[0] & ~VDO_CMDT_MASK) | - CMDT_INIT; - return; - } else { - pd[port].vdm_state = VDM_STATE_DONE; - } - } - - if (PD_VDO_SVDM(payload[0])) - rlen = pd_svdm(port, cnt, payload, &rdata); - else - rlen = pd_custom_vdm(port, cnt, payload, &rdata); - - if (rlen > 0) { - queue_vdm(port, rdata, &rdata[1], rlen - 1); - return; - } - if (debug_level >= 2) - CPRINTF("C%d Unhandled VDM VID %04x CMD %04x\n", - port, PD_VDO_VID(payload[0]), payload[0] & 0xFFFF); -} - -static __maybe_unused int pd_is_disconnected(int port) -{ - return pd[port].task_state == PD_STATE_SRC_DISCONNECTED -#ifdef CONFIG_USB_PD_DUAL_ROLE - || pd[port].task_state == PD_STATE_SNK_DISCONNECTED -#endif - ; -} - -static void set_usb_mux_with_current_data_role(int port) -{ -#ifdef CONFIG_USBC_SS_MUX - /* - * If the SoC is down, then we disconnect the MUX to save power since - * no one cares about the data lines. - */ -#ifdef CONFIG_POWER_COMMON - if (chipset_in_or_transitioning_to_state(CHIPSET_STATE_ANY_OFF)) { - usb_mux_set(port, TYPEC_MUX_NONE, USB_SWITCH_DISCONNECT, - pd[port].polarity); - return; - } -#endif /* CONFIG_POWER_COMMON */ - - /* - * When PD stack is disconnected, then mux should be disconnected, which - * is also what happens in the set_state disconnection code. Once the - * PD state machine progresses out of disconnect, the MUX state will - * be set correctly again. - */ - if (pd_is_disconnected(port)) - usb_mux_set(port, TYPEC_MUX_NONE, USB_SWITCH_DISCONNECT, - pd[port].polarity); - /* - * If new data role isn't DFP and we only support DFP, also disconnect. - */ - else if (IS_ENABLED(CONFIG_USBC_SS_MUX_DFP_ONLY) && - pd[port].data_role != PD_ROLE_DFP) - usb_mux_set(port, TYPEC_MUX_NONE, USB_SWITCH_DISCONNECT, - pd[port].polarity); - /* - * Otherwise connect mux since we are in S3+ - */ - else - usb_mux_set(port, TYPEC_MUX_USB, USB_SWITCH_CONNECT, - pd[port].polarity); - -#endif /* CONFIG_USBC_SS_MUX */ -} - -static void pd_set_data_role(int port, int role) -{ - pd[port].data_role = role; -#ifdef CONFIG_USB_PD_DUAL_ROLE - pd_update_saved_port_flags(port, PD_BBRMFLG_DATA_ROLE, role); -#endif /* defined(CONFIG_USB_PD_DUAL_ROLE) */ - pd_execute_data_swap(port, role); - - set_usb_mux_with_current_data_role(port); - pd_update_roles(port); -#ifdef CONFIG_BC12_DETECT_DATA_ROLE_TRIGGER - /* - * For BC1.2 detection that is triggered on data role change events - * instead of VBUS changes, need to set an event to wake up the USB_CHG - * task and indicate the current data role. - */ - if (role == PD_ROLE_UFP) - task_set_event(USB_CHG_PORT_TO_TASK_ID(port), - USB_CHG_EVENT_DR_UFP, 0); - else if (role == PD_ROLE_DFP) - task_set_event(USB_CHG_PORT_TO_TASK_ID(port), - USB_CHG_EVENT_DR_DFP, 0); -#endif /* CONFIG_BC12_DETECT_DATA_ROLE_TRIGGER */ -} - -#ifdef CONFIG_USBC_VCONN -static void pd_set_vconn_role(int port, int role) -{ - if (role == PD_ROLE_VCONN_ON) - pd[port].flags |= PD_FLAGS_VCONN_ON; - else - pd[port].flags &= ~PD_FLAGS_VCONN_ON; - -#ifdef CONFIG_USB_PD_DUAL_ROLE - pd_update_saved_port_flags(port, PD_BBRMFLG_VCONN_ROLE, role); -#endif -} -#endif /* CONFIG_USBC_VCONN */ - -void pd_execute_hard_reset(int port) -{ - int hard_rst_tx = pd[port].last_state == PD_STATE_HARD_RESET_SEND; - - CPRINTF("C%d HARD RST %cX\n", port, hard_rst_tx ? 'T' : 'R'); - - pd[port].msg_id = 0; - invalidate_last_message_id(port); -#ifdef CONFIG_USB_PD_ALT_MODE_DFP - pd_dfp_exit_mode(port, 0, 0); -#endif - -#ifdef CONFIG_USB_PD_REV30 - pd[port].rev = PD_REV30; - pd_ca_reset(port); -#endif - /* - * Fake set last state to hard reset to make sure that the next - * state to run knows that we just did a hard reset. - */ - pd[port].last_state = PD_STATE_HARD_RESET_EXECUTE; - -#ifdef CONFIG_USB_PD_DUAL_ROLE - /* - * If we are swapping to a source and have changed to Rp, restore back - * to Rd and turn off vbus to match our power_role. - */ - if (pd[port].task_state == PD_STATE_SNK_SWAP_STANDBY || - pd[port].task_state == PD_STATE_SNK_SWAP_COMPLETE) { - tcpm_set_cc(port, TYPEC_CC_RD); - pd_power_supply_reset(port); - } - - /* Set initial data role (matching power role) */ - pd_set_data_role(port, pd[port].power_role); - if (pd[port].power_role == PD_ROLE_SINK) { - /* Clear the input current limit */ - pd_set_input_current_limit(port, 0, 0); -#ifdef CONFIG_CHARGE_MANAGER - charge_manager_set_ceil(port, - CEIL_REQUESTOR_PD, - CHARGE_CEIL_NONE); -#endif /* CONFIG_CHARGE_MANAGER */ - -#ifdef CONFIG_USBC_VCONN - /* - * Sink must turn off Vconn after a hard reset if it was being - * sourced previously - */ - if (pd[port].flags & PD_FLAGS_VCONN_ON) { - set_vconn(port, 0); - pd_set_vconn_role(port, PD_ROLE_VCONN_OFF); - } -#endif - - set_state(port, PD_STATE_SNK_HARD_RESET_RECOVER); - return; - } -#endif /* CONFIG_USB_PD_DUAL_ROLE */ - - if (!hard_rst_tx) - usleep(PD_T_PS_HARD_RESET); - - /* We are a source, cut power */ - pd_power_supply_reset(port); - pd[port].src_recover = get_time().val + PD_T_SRC_RECOVER; -#ifdef CONFIG_USBC_VCONN - set_vconn(port, 0); -#endif - set_state(port, PD_STATE_SRC_HARD_RESET_RECOVER); -} - -static void execute_soft_reset(int port) -{ - pd[port].msg_id = 0; - invalidate_last_message_id(port); - set_state(port, DUAL_ROLE_IF_ELSE(port, PD_STATE_SNK_DISCOVERY, - PD_STATE_SRC_DISCOVERY)); - CPRINTF("C%d Soft Rst\n", port); -} - -void pd_soft_reset(void) -{ - int i; - - for (i = 0; i < board_get_usb_pd_port_count(); ++i) - if (pd_is_connected(i)) { - set_state(i, PD_STATE_SOFT_RESET); - task_wake(PD_PORT_TO_TASK_ID(i)); - } -} - -#ifdef CONFIG_USB_PD_DUAL_ROLE -/* - * Request desired charge voltage from source. - * Returns EC_SUCCESS on success or non-zero on failure. - */ -static int pd_send_request_msg(int port, int always_send_request) -{ - uint32_t rdo, curr_limit, supply_voltage; - int res; - -#ifdef CONFIG_CHARGE_MANAGER - int charging = (charge_manager_get_active_charge_port() == port); -#else - const int charging = 1; -#endif - -#ifdef CONFIG_USB_PD_CHECK_MAX_REQUEST_ALLOWED - int max_request_allowed = pd_is_max_request_allowed(); -#else - const int max_request_allowed = 1; -#endif - - /* Clear new power request */ - pd[port].new_power_request = 0; - - /* Build and send request RDO */ - /* - * If this port is not actively charging or we are not allowed to - * request the max voltage, then select vSafe5V - */ - pd_build_request(pd_get_src_cap_cnt(port), pd_get_src_caps(port), 0, - &rdo, &curr_limit, &supply_voltage, - charging && max_request_allowed ? - PD_REQUEST_MAX : PD_REQUEST_VSAFE5V, - pd_get_max_voltage()); - - if (!always_send_request) { - /* Don't re-request the same voltage */ - if (pd[port].prev_request_mv == supply_voltage) - return EC_SUCCESS; -#ifdef CONFIG_CHARGE_MANAGER - /* Limit current to PD_MIN_MA during transition */ - else - charge_manager_force_ceil(port, PD_MIN_MA); -#endif - } - - CPRINTF("C%d Req [%d] %dmV %dmA", port, RDO_POS(rdo), - supply_voltage, curr_limit); - if (rdo & RDO_CAP_MISMATCH) - CPRINTF(" Mismatch"); - CPRINTF("\n"); - - pd[port].curr_limit = curr_limit; - pd[port].supply_voltage = supply_voltage; - pd[port].prev_request_mv = supply_voltage; - res = send_request(port, rdo); - if (res < 0) - return res; - set_state(port, PD_STATE_SNK_REQUESTED); - return EC_SUCCESS; -} -#endif - -static void pd_update_pdo_flags(int port, uint32_t pdo) -{ -#ifdef CONFIG_CHARGE_MANAGER -#ifdef CONFIG_USB_PD_ALT_MODE_DFP - int charge_allowlisted = - (pd[port].power_role == PD_ROLE_SINK && - pd_charge_from_device(pd_get_identity_vid(port), - pd_get_identity_pid(port))); -#else - const int charge_allowlisted = 0; -#endif -#endif - - /* can only parse PDO flags if type is fixed */ - if ((pdo & PDO_TYPE_MASK) != PDO_TYPE_FIXED) - return; - -#ifdef CONFIG_USB_PD_DUAL_ROLE - if (pdo & PDO_FIXED_DUAL_ROLE) - pd[port].flags |= PD_FLAGS_PARTNER_DR_POWER; - else - pd[port].flags &= ~PD_FLAGS_PARTNER_DR_POWER; - - if (pdo & PDO_FIXED_EXTERNAL) - pd[port].flags |= PD_FLAGS_PARTNER_EXTPOWER; - else - pd[port].flags &= ~PD_FLAGS_PARTNER_EXTPOWER; - - if (pdo & PDO_FIXED_COMM_CAP) - pd[port].flags |= PD_FLAGS_PARTNER_USB_COMM; - else - pd[port].flags &= ~PD_FLAGS_PARTNER_USB_COMM; -#endif - - if (pdo & PDO_FIXED_DATA_SWAP) - pd[port].flags |= PD_FLAGS_PARTNER_DR_DATA; - else - pd[port].flags &= ~PD_FLAGS_PARTNER_DR_DATA; - -#ifdef CONFIG_CHARGE_MANAGER - /* - * Treat device as a dedicated charger (meaning we should charge - * from it) if it does not support power swap, or if it is externally - * powered, or if we are a sink and the device identity matches a - * charging allowlist. - */ - if (!(pd[port].flags & PD_FLAGS_PARTNER_DR_POWER) || - (pd[port].flags & PD_FLAGS_PARTNER_EXTPOWER) || - charge_allowlisted) - charge_manager_update_dualrole(port, CAP_DEDICATED); - else - charge_manager_update_dualrole(port, CAP_DUALROLE); -#endif -} - -static void handle_data_request(int port, uint16_t head, - uint32_t *payload) -{ - int type = PD_HEADER_TYPE(head); - int cnt = PD_HEADER_CNT(head); - - switch (type) { -#ifdef CONFIG_USB_PD_DUAL_ROLE - case PD_DATA_SOURCE_CAP: - if ((pd[port].task_state == PD_STATE_SNK_DISCOVERY) - || (pd[port].task_state == PD_STATE_SNK_TRANSITION) - || (pd[port].task_state == PD_STATE_SNK_REQUESTED) -#ifdef CONFIG_USB_PD_VBUS_DETECT_NONE - || (pd[port].task_state == - PD_STATE_SNK_HARD_RESET_RECOVER) -#endif - || (pd[port].task_state == PD_STATE_SNK_READY)) { -#ifdef CONFIG_USB_PD_REV30 - /* - * Only adjust sink rev if source rev is higher. - */ - if (PD_HEADER_REV(head) < pd[port].rev) - pd[port].rev = PD_HEADER_REV(head); -#endif - /* Port partner is now known to be PD capable */ - pd[port].flags |= PD_FLAGS_PREVIOUS_PD_CONN; - - /* src cap 0 should be fixed PDO */ - pd_update_pdo_flags(port, payload[0]); - - pd_process_source_cap(port, cnt, payload); - - /* Source will resend source cap on failure */ - pd_send_request_msg(port, 1); - } - break; -#endif /* CONFIG_USB_PD_DUAL_ROLE */ - case PD_DATA_REQUEST: - if ((pd[port].power_role == PD_ROLE_SOURCE) && (cnt == 1)) { -#ifdef CONFIG_USB_PD_REV30 - /* - * Adjust the rev level to what the sink supports. If - * they're equal, no harm done. - */ - pd[port].rev = PD_HEADER_REV(head); -#endif - if (!pd_check_requested_voltage(payload[0], port)) { - if (send_control(port, PD_CTRL_ACCEPT) < 0) - /* - * if we fail to send accept, do - * nothing and let sink timeout and - * send hard reset - */ - return; - - /* explicit contract is now in place */ - pd[port].flags |= PD_FLAGS_EXPLICIT_CONTRACT; -#ifdef CONFIG_USB_PD_DUAL_ROLE - pd_update_saved_port_flags( - port, PD_BBRMFLG_EXPLICIT_CONTRACT, 1); -#endif /* CONFIG_USB_PD_DUAL_ROLE */ -#ifdef CONFIG_USB_PD_REV30 - /* - * Start Source-coordinated collision - * avoidance - */ - if (pd[port].rev == PD_REV30 && - pd[port].power_role == PD_ROLE_SOURCE) - sink_can_xmit(port, SINK_TX_OK); -#endif - pd[port].requested_idx = RDO_POS(payload[0]); - set_state(port, PD_STATE_SRC_ACCEPTED); - return; - } - } - /* the message was incorrect or cannot be satisfied */ - send_control(port, PD_CTRL_REJECT); - /* keep last contract in place (whether implicit or explicit) */ - set_state(port, PD_STATE_SRC_READY); - break; - case PD_DATA_BIST: - /* If not in READY state, then don't start BIST */ - if (DUAL_ROLE_IF_ELSE(port, - pd[port].task_state == PD_STATE_SNK_READY, - pd[port].task_state == PD_STATE_SRC_READY)) { - /* currently only support sending bist carrier mode 2 */ - if ((payload[0] >> 28) == 5) { - /* bist data object mode is 2 */ - pd_transmit(port, TCPC_TX_BIST_MODE_2, 0, - NULL); - /* Set to appropriate port disconnected state */ - set_state(port, DUAL_ROLE_IF_ELSE(port, - PD_STATE_SNK_DISCONNECTED, - PD_STATE_SRC_DISCONNECTED)); - } - } - break; - case PD_DATA_SINK_CAP: - pd[port].flags |= PD_FLAGS_SNK_CAP_RECVD; - /* snk cap 0 should be fixed PDO */ - pd_update_pdo_flags(port, payload[0]); - if (pd[port].task_state == PD_STATE_SRC_GET_SINK_CAP) - set_state(port, PD_STATE_SRC_READY); - break; -#ifdef CONFIG_USB_PD_REV30 - case PD_DATA_BATTERY_STATUS: - break; -#endif - case PD_DATA_VENDOR_DEF: - handle_vdm_request(port, cnt, payload); - break; - default: - CPRINTF("C%d Unhandled data message type %d\n", port, type); - } -} - -#ifdef CONFIG_USB_PD_DUAL_ROLE -void pd_request_power_swap(int port) -{ - if (pd[port].task_state == PD_STATE_SRC_READY) - set_state(port, PD_STATE_SRC_SWAP_INIT); - else if (pd[port].task_state == PD_STATE_SNK_READY) - set_state(port, PD_STATE_SNK_SWAP_INIT); - task_wake(PD_PORT_TO_TASK_ID(port)); -} - -#ifdef CONFIG_USBC_VCONN_SWAP -static void pd_request_vconn_swap(int port) -{ - if (pd[port].task_state == PD_STATE_SRC_READY || - pd[port].task_state == PD_STATE_SNK_READY) - set_state(port, PD_STATE_VCONN_SWAP_SEND); - task_wake(PD_PORT_TO_TASK_ID(port)); -} - -void pd_try_vconn_src(int port) -{ - /* - * If we don't currently provide vconn, and we can supply it, send - * a vconn swap request. - */ - if (!(pd[port].flags & PD_FLAGS_VCONN_ON)) { - if (pd_check_vconn_swap(port)) - pd_request_vconn_swap(port); - } -} -#endif -#endif /* CONFIG_USB_PD_DUAL_ROLE */ - -void pd_request_data_swap(int port) -{ - if (DUAL_ROLE_IF_ELSE(port, - pd[port].task_state == PD_STATE_SNK_READY, - pd[port].task_state == PD_STATE_SRC_READY)) - set_state(port, PD_STATE_DR_SWAP); - task_wake(PD_PORT_TO_TASK_ID(port)); -} - -static void pd_set_power_role(int port, int role) -{ - pd[port].power_role = role; -#ifdef CONFIG_USB_PD_DUAL_ROLE - pd_update_saved_port_flags(port, PD_BBRMFLG_POWER_ROLE, role); -#endif /* defined(CONFIG_USB_PD_DUAL_ROLE) */ -} - -static void pd_dr_swap(int port) -{ - pd_set_data_role(port, !pd[port].data_role); - pd[port].flags |= PD_FLAGS_CHECK_IDENTITY; -} - -static void handle_ctrl_request(int port, uint16_t head, - uint32_t *payload) -{ - int type = PD_HEADER_TYPE(head); - int res; - - switch (type) { - case PD_CTRL_GOOD_CRC: - /* should not get it */ - break; - case PD_CTRL_PING: - /* Nothing else to do */ - break; - case PD_CTRL_GET_SOURCE_CAP: - if (pd[port].task_state == PD_STATE_SRC_READY) - set_state(port, PD_STATE_SRC_DISCOVERY); - else { - res = send_source_cap(port); - if ((res >= 0) && - (pd[port].task_state == PD_STATE_SRC_DISCOVERY)) - set_state(port, PD_STATE_SRC_NEGOCIATE); - } - break; - case PD_CTRL_GET_SINK_CAP: -#ifdef CONFIG_USB_PD_DUAL_ROLE - send_sink_cap(port); -#else - send_control(port, REFUSE(pd[port].rev)); -#endif - break; -#ifdef CONFIG_USB_PD_DUAL_ROLE - case PD_CTRL_GOTO_MIN: -#ifdef CONFIG_USB_PD_GIVE_BACK - if (pd[port].task_state == PD_STATE_SNK_READY) { - /* - * Reduce power consumption now! - * - * The source will restore power to this sink - * by sending a new source cap message at a - * later time. - */ - pd_snk_give_back(port, &pd[port].curr_limit, - &pd[port].supply_voltage); - set_state(port, PD_STATE_SNK_TRANSITION); - } -#endif - - break; - case PD_CTRL_PS_RDY: - if (pd[port].task_state == PD_STATE_SNK_SWAP_SRC_DISABLE) { - set_state(port, PD_STATE_SNK_SWAP_STANDBY); - } else if (pd[port].task_state == PD_STATE_SRC_SWAP_STANDBY) { - /* reset message ID and swap roles */ - pd[port].msg_id = 0; - pd_set_power_role(port, PD_ROLE_SINK); - pd_update_roles(port); - /* - * Give the state machine time to read VBUS as high. - * Note: This is empirically determined, not strictly - * part of the USB PD spec. - */ - pd[port].vbus_debounce_time = - get_time().val + PD_T_DEBOUNCE; - set_state(port, PD_STATE_SNK_DISCOVERY); -#ifdef CONFIG_USBC_VCONN_SWAP - } else if (pd[port].task_state == PD_STATE_VCONN_SWAP_INIT) { - /* - * If VCONN is on, then this PS_RDY tells us it's - * ok to turn VCONN off - */ - if (pd[port].flags & PD_FLAGS_VCONN_ON) - set_state(port, PD_STATE_VCONN_SWAP_READY); -#endif - } else if (pd[port].task_state == PD_STATE_SNK_DISCOVERY) { - /* Don't know what power source is ready. Reset. */ - set_state(port, PD_STATE_HARD_RESET_SEND); - } else if (pd[port].task_state == PD_STATE_SNK_SWAP_STANDBY) { - /* Do nothing, assume this is a redundant PD_RDY */ - } else if (pd[port].power_role == PD_ROLE_SINK) { - /* - * Give the source some time to send any messages before - * we start our interrogation. Add some jitter of up to - * ~192ms to prevent multiple collisions. - */ - if (pd[port].task_state == PD_STATE_SNK_TRANSITION) - pd[port].ready_state_holdoff_timer = - get_time().val + SNK_READY_HOLD_OFF_US - + (get_time().le.lo & 0xf) * 12 * MSEC; - - set_state(port, PD_STATE_SNK_READY); - pd_set_input_current_limit(port, pd[port].curr_limit, - pd[port].supply_voltage); -#ifdef CONFIG_CHARGE_MANAGER - /* Set ceiling based on what's negotiated */ - charge_manager_set_ceil(port, - CEIL_REQUESTOR_PD, - pd[port].curr_limit); -#endif - } - break; -#endif - case PD_CTRL_REJECT: - case PD_CTRL_WAIT: - if (pd[port].task_state == PD_STATE_DR_SWAP) { - if (type == PD_CTRL_WAIT) /* try again ... */ - pd[port].flags |= PD_FLAGS_CHECK_DR_ROLE; - set_state(port, READY_RETURN_STATE(port)); - } -#ifdef CONFIG_USBC_VCONN_SWAP - else if (pd[port].task_state == PD_STATE_VCONN_SWAP_SEND) - set_state(port, READY_RETURN_STATE(port)); -#endif -#ifdef CONFIG_USB_PD_DUAL_ROLE - else if (pd[port].task_state == PD_STATE_SRC_SWAP_INIT) - set_state(port, PD_STATE_SRC_READY); - else if (pd[port].task_state == PD_STATE_SNK_SWAP_INIT) - set_state(port, PD_STATE_SNK_READY); - else if (pd[port].task_state == PD_STATE_SNK_REQUESTED) { - /* - * On reception of a WAIT message, transition to - * PD_STATE_SNK_READY after PD_T_SINK_REQUEST ms to - * send another request. - * - * On reception of a REJECT message, transition to - * PD_STATE_SNK_READY but don't resend the request if - * we already have a contract in place. - * - * On reception of a REJECT message without a contract, - * transition to PD_STATE_SNK_DISCOVERY instead. - */ - if (type == PD_CTRL_WAIT) { - /* - * Trigger a new power request when - * we enter PD_STATE_SNK_READY - */ - pd[port].new_power_request = 1; - - /* - * After the request is triggered, - * make sure the request is sent. - */ - pd[port].prev_request_mv = 0; - - /* - * Transition to PD_STATE_SNK_READY - * after PD_T_SINK_REQUEST ms. - */ - set_state_timeout(port, - get_time().val + - PD_T_SINK_REQUEST, - PD_STATE_SNK_READY); - } else { - /* The request was rejected */ - const int in_contract = - pd[port].flags & - PD_FLAGS_EXPLICIT_CONTRACT; - set_state(port, - in_contract ? PD_STATE_SNK_READY - : PD_STATE_SNK_DISCOVERY); - } - } -#endif - break; - case PD_CTRL_ACCEPT: - if (pd[port].task_state == PD_STATE_SOFT_RESET) { - /* - * For the case that we sent soft reset in SNK_DISCOVERY - * on startup due to VBUS never low, clear the flag. - */ - pd[port].flags &= ~PD_FLAGS_VBUS_NEVER_LOW; - execute_soft_reset(port); - } else if (pd[port].task_state == PD_STATE_DR_SWAP) { - /* switch data role */ - pd_dr_swap(port); - set_state(port, READY_RETURN_STATE(port)); -#ifdef CONFIG_USB_PD_DUAL_ROLE -#ifdef CONFIG_USBC_VCONN_SWAP - } else if (pd[port].task_state == PD_STATE_VCONN_SWAP_SEND) { - /* switch vconn */ - set_state(port, PD_STATE_VCONN_SWAP_INIT); -#endif - } else if (pd[port].task_state == PD_STATE_SRC_SWAP_INIT) { - /* explicit contract goes away for power swap */ - pd[port].flags &= ~PD_FLAGS_EXPLICIT_CONTRACT; - pd_update_saved_port_flags(port, - PD_BBRMFLG_EXPLICIT_CONTRACT, - 0); - set_state(port, PD_STATE_SRC_SWAP_SNK_DISABLE); - } else if (pd[port].task_state == PD_STATE_SNK_SWAP_INIT) { - /* explicit contract goes away for power swap */ - pd[port].flags &= ~PD_FLAGS_EXPLICIT_CONTRACT; - pd_update_saved_port_flags(port, - PD_BBRMFLG_EXPLICIT_CONTRACT, - 0); - set_state(port, PD_STATE_SNK_SWAP_SNK_DISABLE); - } else if (pd[port].task_state == PD_STATE_SNK_REQUESTED) { - /* explicit contract is now in place */ - pd[port].flags |= PD_FLAGS_EXPLICIT_CONTRACT; - pd_update_saved_port_flags(port, - PD_BBRMFLG_EXPLICIT_CONTRACT, - 1); - set_state(port, PD_STATE_SNK_TRANSITION); -#endif - } - break; - case PD_CTRL_SOFT_RESET: - execute_soft_reset(port); - /* We are done, acknowledge with an Accept packet */ - send_control(port, PD_CTRL_ACCEPT); - break; - case PD_CTRL_PR_SWAP: -#ifdef CONFIG_USB_PD_DUAL_ROLE - if (pd_check_power_swap(port)) { - send_control(port, PD_CTRL_ACCEPT); - /* - * Clear flag for checking power role to avoid - * immediately requesting another swap. - */ - pd[port].flags &= ~PD_FLAGS_CHECK_PR_ROLE; - set_state(port, - DUAL_ROLE_IF_ELSE(port, - PD_STATE_SNK_SWAP_SNK_DISABLE, - PD_STATE_SRC_SWAP_SNK_DISABLE)); - } else { - send_control(port, REFUSE(pd[port].rev)); - } -#else - send_control(port, REFUSE(pd[port].rev)); -#endif - break; - case PD_CTRL_DR_SWAP: - if (pd_check_data_swap(port, pd[port].data_role)) { - /* - * Accept switch and perform data swap. Clear - * flag for checking data role to avoid - * immediately requesting another swap. - */ - pd[port].flags &= ~PD_FLAGS_CHECK_DR_ROLE; - if (send_control(port, PD_CTRL_ACCEPT) >= 0) - pd_dr_swap(port); - } else { - send_control(port, REFUSE(pd[port].rev)); - - } - break; - case PD_CTRL_VCONN_SWAP: -#ifdef CONFIG_USBC_VCONN_SWAP - if (pd[port].task_state == PD_STATE_SRC_READY || - pd[port].task_state == PD_STATE_SNK_READY) { - if (pd_check_vconn_swap(port)) { - if (send_control(port, PD_CTRL_ACCEPT) > 0) - set_state(port, - PD_STATE_VCONN_SWAP_INIT); - } else { - send_control(port, REFUSE(pd[port].rev)); - } - } -#else - send_control(port, REFUSE(pd[port].rev)); -#endif - break; - default: -#ifdef CONFIG_USB_PD_REV30 - send_control(port, PD_CTRL_NOT_SUPPORTED); -#endif - CPRINTF("C%d Unhandled ctrl message type %d\n", port, type); - } -} - -#ifdef CONFIG_USB_PD_REV30 -static void handle_ext_request(int port, uint16_t head, uint32_t *payload) -{ - int type = PD_HEADER_TYPE(head); - - switch (type) { - case PD_EXT_GET_BATTERY_CAP: - send_battery_cap(port, payload); - break; - case PD_EXT_GET_BATTERY_STATUS: - send_battery_status(port, payload); - break; - case PD_EXT_BATTERY_CAP: - break; - default: - send_control(port, PD_CTRL_NOT_SUPPORTED); - } -} -#endif - -static void handle_request(int port, uint16_t head, - uint32_t *payload) -{ - int cnt = PD_HEADER_CNT(head); - int data_role = PD_HEADER_DROLE(head); - int p; - - /* dump received packet content (only dump ping at debug level 3) */ - if ((debug_level == 2 && PD_HEADER_TYPE(head) != PD_CTRL_PING) || - debug_level >= 3) { - CPRINTF("C%d RECV %04x/%d ", port, head, cnt); - for (p = 0; p < cnt; p++) - CPRINTF("[%d]%08x ", p, payload[p]); - CPRINTF("\n"); - } - - /* - * If we are in disconnected state, we shouldn't get a request. Do - * a hard reset if we get one. - */ - if (!pd_is_connected(port)) - set_state(port, PD_STATE_HARD_RESET_SEND); - - /* - * When a data role conflict is detected, USB-C ErrorRecovery - * actions shall be performed, and transitioning to unattached state - * is one such legal action. - */ - if (pd[port].data_role == data_role) { - /* - * If the port doesn't support removing the terminations, just - * go to the unattached state. - */ - if (tcpm_set_cc(port, TYPEC_CC_OPEN) == EC_SUCCESS) { - /* Do not drive VBUS or VCONN. */ - pd_power_supply_reset(port); -#ifdef CONFIG_USBC_VCONN - set_vconn(port, 0); -#endif /* defined(CONFIG_USBC_VCONN) */ - usleep(PD_T_ERROR_RECOVERY); - - /* Restore terminations. */ - tcpm_set_cc(port, DUAL_ROLE_IF_ELSE(port, TYPEC_CC_RD, - TYPEC_CC_RP)); - } - set_state(port, - DUAL_ROLE_IF_ELSE(port, - PD_STATE_SNK_DISCONNECTED, - PD_STATE_SRC_DISCONNECTED)); - return; - } - -#ifdef CONFIG_USB_PD_REV30 - /* Check if this is an extended chunked data message. */ - if (pd[port].rev == PD_REV30 && PD_HEADER_EXT(head)) { - handle_ext_request(port, head, payload); - return; - } -#endif - if (cnt) - handle_data_request(port, head, payload); - else - handle_ctrl_request(port, head, payload); -} - -void pd_send_vdm(int port, uint32_t vid, int cmd, const uint32_t *data, - int count) -{ - if (count > VDO_MAX_SIZE - 1) { - CPRINTF("C%d VDM over max size\n", port); - return; - } - - /* set VDM header with VID & CMD */ - pd[port].vdo_data[0] = VDO(vid, ((vid & USB_SID_PD) == USB_SID_PD) ? - 1 : (PD_VDO_CMD(cmd) <= CMD_ATTENTION), cmd); -#ifdef CONFIG_USB_PD_REV30 - pd[port].vdo_data[0] |= VDO_SVDM_VERS(vdo_ver[pd[port].rev]); -#endif - queue_vdm(port, pd[port].vdo_data, data, count); - - task_wake(PD_PORT_TO_TASK_ID(port)); -} - -static inline int pdo_busy(int port) -{ - /* - * Note, main PDO state machine (pd_task) uses READY state exclusively - * to denote port partners have successfully negociated a contract. All - * other protocol actions force state transitions. - */ - int rv = (pd[port].task_state != PD_STATE_SRC_READY); -#ifdef CONFIG_USB_PD_DUAL_ROLE - rv &= (pd[port].task_state != PD_STATE_SNK_READY); -#endif - return rv; -} - -static uint64_t vdm_get_ready_timeout(uint32_t vdm_hdr) -{ - uint64_t timeout; - int cmd = PD_VDO_CMD(vdm_hdr); - - /* its not a structured VDM command */ - if (!PD_VDO_SVDM(vdm_hdr)) - return 500*MSEC; - - switch (PD_VDO_CMDT(vdm_hdr)) { - case CMDT_INIT: - if ((cmd == CMD_ENTER_MODE) || (cmd == CMD_EXIT_MODE)) - timeout = PD_T_VDM_WAIT_MODE_E; - else - timeout = PD_T_VDM_SNDR_RSP; - break; - default: - if ((cmd == CMD_ENTER_MODE) || (cmd == CMD_EXIT_MODE)) - timeout = PD_T_VDM_E_MODE; - else - timeout = PD_T_VDM_RCVR_RSP; - break; - } - return timeout; -} - -static void pd_vdm_send_state_machine(int port) -{ - int res; - uint16_t header; - - switch (pd[port].vdm_state) { - case VDM_STATE_READY: - /* Only transmit VDM if connected. */ - if (!pd_is_connected(port)) { - pd[port].vdm_state = VDM_STATE_ERR_BUSY; - break; - } - - /* - * if there's traffic or we're not in PDO ready state don't send - * a VDM. - */ - if (pdo_busy(port)) - break; - - /* - * To communicate with the cable plug, an explicit contract - * should be established, VCONN should be enabled and data role - * that can communicate with the cable plug should be in place. - * For USB3.0, UFP/DFP can communicate whereas in case of - * USB2.0 only DFP can talk to the cable plug. - * - * For communication between USB2.0 UFP and cable plug, - * data role swap takes place during source and sink - * negotiation and in case of failure, a soft reset is issued. - */ - if (is_sop_prime_ready(port, pd[port].data_role, - pd[port].flags)) { - /* Prepare SOP'/SOP'' header and send VDM */ - header = PD_HEADER( - PD_DATA_VENDOR_DEF, - PD_PLUG_FROM_DFP_UFP, - 0, - pd[port].msg_id, - (int)pd[port].vdo_count, - pd_get_rev(port), - 0); - res = pd_transmit(port, TCPC_TX_SOP_PRIME, header, - pd[port].vdo_data); - /* - * If there is no ack from the cable, its a non-emark - * cable and since, the pd flow should continue - * irrespective of cable response, sending - * discover_svid so the pd flow remains intact. - */ - if (res < 0) { - header = PD_HEADER(PD_DATA_VENDOR_DEF, - pd[port].power_role, - pd[port].data_role, - pd[port].msg_id, - (int)pd[port].vdo_count, - pd_get_rev(port), 0); - pd[port].vdo_data[0] = - VDO(USB_SID_PD, 1, CMD_DISCOVER_SVID); - res = pd_transmit(port, TCPC_TX_SOP, header, - pd[port].vdo_data); - reset_pd_cable(port); - } - } else { - /* Prepare SOP header and send VDM */ - header = PD_HEADER(PD_DATA_VENDOR_DEF, - pd[port].power_role, - pd[port].data_role, - pd[port].msg_id, - (int)pd[port].vdo_count, - pd_get_rev(port), 0); - res = pd_transmit(port, TCPC_TX_SOP, header, - pd[port].vdo_data); - } - - if (res < 0) { - pd[port].vdm_state = VDM_STATE_ERR_SEND; - } else { - pd[port].vdm_state = VDM_STATE_BUSY; - pd[port].vdm_timeout.val = get_time().val + - vdm_get_ready_timeout(pd[port].vdo_data[0]); - } - break; - case VDM_STATE_WAIT_RSP_BUSY: - /* wait and then initiate request again */ - if (get_time().val > pd[port].vdm_timeout.val) { - pd[port].vdo_data[0] = pd[port].vdo_retry; - pd[port].vdo_count = 1; - pd[port].vdm_state = VDM_STATE_READY; - } - break; - case VDM_STATE_BUSY: - /* Wait for VDM response or timeout */ - if (pd[port].vdm_timeout.val && - (get_time().val > pd[port].vdm_timeout.val)) { - pd[port].vdm_state = VDM_STATE_ERR_TMOUT; - } - break; - default: - break; - } -} - -#ifdef CONFIG_CMD_PD_DEV_DUMP_INFO -static inline void pd_dev_dump_info(uint16_t dev_id, uint8_t *hash) -{ - int j; - ccprintf("DevId:%d.%d Hash:", HW_DEV_ID_MAJ(dev_id), - HW_DEV_ID_MIN(dev_id)); - for (j = 0; j < PD_RW_HASH_SIZE; j += 4) { - ccprintf(" 0x%02x%02x%02x%02x", hash[j + 3], hash[j + 2], - hash[j + 1], hash[j]); - } - ccprintf("\n"); -} -#endif /* CONFIG_CMD_PD_DEV_DUMP_INFO */ - -int pd_dev_store_rw_hash(int port, uint16_t dev_id, uint32_t *rw_hash, - uint32_t current_image) -{ -#ifdef CONFIG_COMMON_RUNTIME - int i; -#endif - - pd[port].dev_id = dev_id; - memcpy(pd[port].dev_rw_hash, rw_hash, PD_RW_HASH_SIZE); -#ifdef CONFIG_CMD_PD_DEV_DUMP_INFO - if (debug_level >= 2) - pd_dev_dump_info(dev_id, (uint8_t *)rw_hash); -#endif - pd[port].current_image = current_image; - -#ifdef CONFIG_COMMON_RUNTIME - /* Search table for matching device / hash */ - for (i = 0; i < RW_HASH_ENTRIES; i++) - if (dev_id == rw_hash_table[i].dev_id) - return !memcmp(rw_hash, - rw_hash_table[i].dev_rw_hash, - PD_RW_HASH_SIZE); -#endif - return 0; -} - -#if defined(CONFIG_POWER_COMMON) || defined(CONFIG_USB_PD_ALT_MODE_DFP) -static void exit_dp_mode(int port) -{ -#ifdef CONFIG_USB_PD_ALT_MODE_DFP - int opos = pd_alt_mode(port, USB_SID_DISPLAYPORT); - - if (opos <= 0) - return; - - CPRINTS("C%d Exiting DP mode", port); - if (!pd_dfp_exit_mode(port, USB_SID_DISPLAYPORT, opos)) - return; - pd_send_vdm(port, USB_SID_DISPLAYPORT, - CMD_EXIT_MODE | VDO_OPOS(opos), NULL, 0); - pd_vdm_send_state_machine(port); - /* Have to wait for ACK */ -#endif /* CONFIG_USB_PD_ALT_MODE_DFP */ -} -#endif /* CONFIG_POWER_COMMON */ - -#ifdef CONFIG_POWER_COMMON -static void handle_new_power_state(int port) -{ - if (chipset_in_or_transitioning_to_state(CHIPSET_STATE_ANY_OFF)) - /* The SoC will negotiated DP mode again when it boots up */ - exit_dp_mode(port); - - /* Ensure mux is set properly after chipset transition */ - set_usb_mux_with_current_data_role(port); -} -#endif /* CONFIG_POWER_COMMON */ - -#ifdef CONFIG_USB_PD_DUAL_ROLE -enum pd_dual_role_states pd_get_dual_role(int port) -{ - return drp_state[port]; -} - -#ifdef CONFIG_USB_PD_TRY_SRC -static void pd_update_try_source(void) -{ - int i; - int try_src = 0; - int batt_soc = usb_get_battery_soc(); - - try_src = 0; - for (i = 0; i < board_get_usb_pd_port_count(); i++) - try_src |= drp_state[i] == PD_DRP_TOGGLE_ON; - - /* - * Enable try source when dual-role toggling AND battery is present - * and at some minimum percentage. - */ - pd_try_src_enable = try_src && - batt_soc >= CONFIG_USB_PD_TRY_SRC_MIN_BATT_SOC; - -#ifdef CONFIG_BATTERY_REVIVE_DISCONNECT - /* - * Don't attempt Try.Src if the battery is in the disconnect state. The - * discharge FET may not be enabled and so attempting Try.Src may cut - * off our only power source at the time. - */ - pd_try_src_enable &= (battery_get_disconnect_state() == - BATTERY_NOT_DISCONNECTED); -#elif defined(CONFIG_BATTERY_PRESENT_CUSTOM) || \ - defined(CONFIG_BATTERY_PRESENT_GPIO) - /* - * When battery is cutoff in ship mode it may not be reliable to - * check if battery is present with its state of charge. - * Also check if battery is initialized and ready to provide power. - */ - pd_try_src_enable &= (battery_is_present() == BP_YES); -#endif /* CONFIG_BATTERY_PRESENT_[CUSTOM|GPIO] */ - - /* - * Clear this flag to cover case where a TrySrc - * mode went from enabled to disabled and trying_source - * was active at that time. - */ - for (i = 0; i < board_get_usb_pd_port_count(); i++) - pd[i].flags &= ~PD_FLAGS_TRY_SRC; -} -#endif /* CONFIG_USB_PD_TRY_SRC */ - -#ifdef CONFIG_USB_PD_RESET_MIN_BATT_SOC -static void pd_update_snk_reset(void) -{ - int i; - int batt_soc = usb_get_battery_soc(); - - if (batt_soc < CONFIG_USB_PD_RESET_MIN_BATT_SOC) - return; - - for (i = 0; i < board_get_usb_pd_port_count(); i++) { - if (pd[i].flags & PD_FLAGS_SNK_WAITING_BATT) { - /* - * Battery has gained sufficient charge to kick off PD - * negotiation and withstand a hard reset. Clear the - * flag and let reset begin if task is waiting in - * SNK_DISCOVERY. - */ - pd[i].flags &= ~PD_FLAGS_SNK_WAITING_BATT; - - if (pd[i].task_state == PD_STATE_SNK_DISCOVERY) { - CPRINTS("C%d: Starting soft reset timer", i); - set_state_timeout(i, - get_time().val + PD_T_SINK_WAIT_CAP, - PD_STATE_SOFT_RESET); - } - } - } -} -#endif - -#if defined(CONFIG_USB_PD_TRY_SRC) || defined(CONFIG_USB_PD_RESET_MIN_BATT_SOC) -static void pd_update_battery_soc_change(void) -{ -#ifdef CONFIG_USB_PD_TRY_SRC - pd_update_try_source(); -#endif - -#ifdef CONFIG_USB_PD_RESET_MIN_BATT_SOC - pd_update_snk_reset(); -#endif -} -DECLARE_HOOK(HOOK_BATTERY_SOC_CHANGE, pd_update_battery_soc_change, - HOOK_PRIO_DEFAULT); -#endif /* CONFIG_USB_PD_TRY_SRC || CONFIG_USB_PD_RESET_MIN_BATT_SOC */ - -static inline void pd_set_dual_role_no_wakeup(int port, - enum pd_dual_role_states state) -{ - drp_state[port] = state; - -#ifdef CONFIG_USB_PD_TRY_SRC - pd_update_try_source(); -#endif -} - -void pd_set_dual_role(int port, enum pd_dual_role_states state) -{ - pd_set_dual_role_no_wakeup(port, state); - - /* Wake task up to process change */ - task_set_event(PD_PORT_TO_TASK_ID(port), - PD_EVENT_UPDATE_DUAL_ROLE, 0); -} - -/* This must only be called from the PD task */ -static void pd_update_dual_role_config(int port) -{ - /* - * Change to sink if port is currently a source AND (new DRP - * state is force sink OR new DRP state is either toggle off - * or debug accessory toggle only and we are in the source - * disconnected state). - */ - if (pd[port].power_role == PD_ROLE_SOURCE && - ((drp_state[port] == PD_DRP_FORCE_SINK && !pd_ts_dts_plugged(port)) - || (drp_state[port] == PD_DRP_TOGGLE_OFF - && pd[port].task_state == PD_STATE_SRC_DISCONNECTED))) { - pd_set_power_role(port, PD_ROLE_SINK); - set_state(port, PD_STATE_SNK_DISCONNECTED); - tcpm_set_cc(port, TYPEC_CC_RD); - /* Make sure we're not sourcing VBUS. */ - pd_power_supply_reset(port); - } - - /* - * Change to source if port is currently a sink and the - * new DRP state is force source. - */ - if (pd[port].power_role == PD_ROLE_SINK && - drp_state[port] == PD_DRP_FORCE_SOURCE) { - pd_set_power_role(port, PD_ROLE_SOURCE); - set_state(port, PD_STATE_SRC_DISCONNECTED); - tcpm_set_cc(port, TYPEC_CC_RP); - } -} - -int pd_get_role(int port) -{ - return pd[port].power_role; -} - -static int pd_is_power_swapping(int port) -{ - /* return true if in the act of swapping power roles */ - return pd[port].task_state == PD_STATE_SNK_SWAP_SNK_DISABLE || - pd[port].task_state == PD_STATE_SNK_SWAP_SRC_DISABLE || - pd[port].task_state == PD_STATE_SNK_SWAP_STANDBY || - pd[port].task_state == PD_STATE_SNK_SWAP_COMPLETE || - pd[port].task_state == PD_STATE_SRC_SWAP_SNK_DISABLE || - pd[port].task_state == PD_STATE_SRC_SWAP_SRC_DISABLE || - pd[port].task_state == PD_STATE_SRC_SWAP_STANDBY; -} - -/* - * Provide Rp to ensure the partner port is in a known state (eg. not - * PD negotiated, not sourcing 20V). - */ -static void pd_partner_port_reset(int port) -{ - uint64_t timeout; - uint8_t flags; - - /* - * If there is no contract in place (or if we fail to read the BBRAM - * flags), there is no need to reset the partner. - */ - if (pd_get_saved_port_flags(port, &flags) != EC_SUCCESS || - !(flags & PD_BBRMFLG_EXPLICIT_CONTRACT)) - return; - - /* - * If we reach here, an explicit contract is in place. - * - * If PD communications are allowed, don't apply Rp. We'll issue a - * SoftReset later on and renegotiate our contract. This particular - * condition only applies to unlocked RO images with an explicit - * contract in place. - */ - if (pd_comm_is_enabled(port)) - return; - - /* If we just lost power, don't apply Rp. */ - if (system_get_reset_flags() & - (EC_RESET_FLAG_BROWNOUT | EC_RESET_FLAG_POWER_ON)) - return; - - /* - * Clear the active contract bit before we apply Rp in case we - * intentionally brown out because we cut off our only power supply. - */ - pd_update_saved_port_flags(port, PD_BBRMFLG_EXPLICIT_CONTRACT, 0); - - /* Provide Rp for 200 msec. or until we no longer have VBUS. */ - CPRINTF("C%d Apply Rp!\n", port); - cflush(); - tcpm_set_cc(port, TYPEC_CC_RP); - timeout = get_time().val + 200 * MSEC; - - while (get_time().val < timeout && pd_is_vbus_present(port)) - msleep(10); -} -#endif /* CONFIG_USB_PD_DUAL_ROLE */ - -int pd_get_polarity(int port) -{ - return pd[port].polarity; -} - -int pd_get_partner_data_swap_capable(int port) -{ - /* return data swap capable status of port partner */ - return pd[port].flags & PD_FLAGS_PARTNER_DR_DATA; -} - -#ifdef CONFIG_COMMON_RUNTIME -void pd_comm_enable(int port, int enable) -{ - /* We don't check port >= CONFIG_USB_PD_PORT_MAX_COUNT deliberately */ - pd_comm_enabled[port] = enable; - - /* If type-C connection, then update the TCPC RX enable */ - if (pd_is_connected(port)) - tcpm_set_rx_enable(port, enable); - -#ifdef CONFIG_USB_PD_DUAL_ROLE - /* - * If communications are enabled, start hard reset timer for - * any port in PD_SNK_DISCOVERY. - */ - if (enable && pd[port].task_state == PD_STATE_SNK_DISCOVERY) - set_state_timeout(port, - get_time().val + PD_T_SINK_WAIT_CAP, - PD_STATE_HARD_RESET_SEND); -#endif -} -#endif - -void pd_ping_enable(int port, int enable) -{ - if (enable) - pd[port].flags |= PD_FLAGS_PING_ENABLED; - else - pd[port].flags &= ~PD_FLAGS_PING_ENABLED; -} - -__overridable uint8_t board_get_src_dts_polarity(int port) -{ - /* - * If the port in SRC DTS, the polarity is determined by the board, - * i.e. what Rp impedance the CC lines are pulled. If this function - * is not overridden, assume CC1 is primary. - */ - return 0; -} - -#if defined(CONFIG_CHARGE_MANAGER) - -/** - * Signal power request to indicate a charger update that affects the port. - */ -void pd_set_new_power_request(int port) -{ - pd[port].new_power_request = 1; - task_wake(PD_PORT_TO_TASK_ID(port)); -} -#endif /* CONFIG_CHARGE_MANAGER */ - -#if defined(CONFIG_USBC_BACKWARDS_COMPATIBLE_DFP) && defined(CONFIG_USBC_SS_MUX) -/* - * Backwards compatible DFP does not support USB SS because it applies VBUS - * before debouncing CC and setting USB SS muxes, but SS detection will fail - * before we are done debouncing CC. - */ -#error "Backwards compatible DFP does not support USB" -#endif - -#ifdef CONFIG_COMMON_RUNTIME - -/* Initialize globals based on system state. */ -static void pd_init_tasks(void) -{ - static int initialized; - int enable = 1; - int i; - - /* Initialize globals once, for all PD tasks. */ - if (initialized) - return; - -#if defined(HAS_TASK_CHIPSET) && defined(CONFIG_USB_PD_DUAL_ROLE) - /* Set dual-role state based on chipset power state */ - if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) - for (i = 0; i < board_get_usb_pd_port_count(); i++) - drp_state[i] = PD_DRP_FORCE_SINK; - else if (chipset_in_state(CHIPSET_STATE_ANY_SUSPEND)) - for (i = 0; i < board_get_usb_pd_port_count(); i++) - drp_state[i] = PD_DRP_TOGGLE_OFF; - else /* CHIPSET_STATE_ON */ - for (i = 0; i < board_get_usb_pd_port_count(); i++) - drp_state[i] = PD_DRP_TOGGLE_ON; -#endif - -#if defined(CONFIG_USB_PD_COMM_DISABLED) - enable = 0; -#elif defined(CONFIG_USB_PD_COMM_LOCKED) - /* Disable PD communication at init if we're in RO and locked. */ - if (!system_is_in_rw() && system_is_locked()) - enable = 0; -#ifdef CONFIG_VBOOT_EFS - if (vboot_need_pd_comm()) - enable = 1; -#endif -#endif - for (i = 0; i < board_get_usb_pd_port_count(); i++) - pd_comm_enabled[i] = enable; - CPRINTS("PD comm %sabled", enable ? "en" : "dis"); - - initialized = 1; -} -#endif /* CONFIG_COMMON_RUNTIME */ - -#if !defined(CONFIG_USB_PD_TCPC) && defined(CONFIG_USB_PD_DUAL_ROLE) -static int pd_restart_tcpc(int port) -{ - if (board_set_tcpc_power_mode) { - /* force chip reset */ - board_set_tcpc_power_mode(port, 0); - } - return tcpm_init(port); -} -#endif - -/* High-priority interrupt tasks implementations */ -#if defined(HAS_TASK_PD_INT_C0) || defined(HAS_TASK_PD_INT_C1) || \ - defined(HAS_TASK_PD_INT_C2) - -/* Used to conditionally compile code in main pd task. */ -#define HAS_DEFFERED_INTERRUPT_HANDLER - -/* Events for pd_interrupt_handler_task */ -#define PD_PROCESS_INTERRUPT BIT(0) - -static uint8_t pd_int_task_id[CONFIG_USB_PD_PORT_MAX_COUNT]; - -void schedule_deferred_pd_interrupt(const int port) -{ - task_set_event(pd_int_task_id[port], PD_PROCESS_INTERRUPT, 0); -} - -/* - * Theoretically, we may need to support up to 480 USB-PD packets per second for - * intensive operations such as FW update over PD. This value has tested well - * preventing watchdog resets with a single bad port partner plugged in. - */ -#define ALERT_STORM_MAX_COUNT 480 -#define ALERT_STORM_INTERVAL SECOND - -/** - * Main task entry point that handles PD interrupts for a single port - * - * @param p The PD port number for which to handle interrupts (pointer is - * reinterpreted as an integer directly). - */ -void pd_interrupt_handler_task(void *p) -{ - const int port = (int) ((intptr_t) p); - const int port_mask = (PD_STATUS_TCPC_ALERT_0 << port); - struct { - int count; - timestamp_t time; - } storm_tracker[CONFIG_USB_PD_PORT_MAX_COUNT] = {}; - - ASSERT(port >= 0 && port < CONFIG_USB_PD_PORT_MAX_COUNT); - - pd_int_task_id[port] = task_get_current(); - - while (1) { - const int evt = task_wait_event(-1); - - if (evt & PD_PROCESS_INTERRUPT) { - /* - * While the interrupt signal is asserted; we have more - * work to do. This effectively makes the interrupt a - * level-interrupt instead of an edge-interrupt without - * having to enable/disable a real level-interrupt in - * multiple locations. - * - * Also, if the port is disabled do not process - * interrupts. Upon existing suspend, we schedule a - * PD_PROCESS_INTERRUPT to check if we missed anything. - */ - while ((tcpc_get_alert_status() & port_mask) && - pd_is_port_enabled(port)) { - timestamp_t now; - - tcpc_alert(port); - - now = get_time(); - if (timestamp_expired( - storm_tracker[port].time, &now)) { - /* Reset timer into future */ - storm_tracker[port].time.val = - now.val + ALERT_STORM_INTERVAL; - - /* - * Start at 1 since we are processing - * an interrupt now - */ - storm_tracker[port].count = 1; - } else if (++storm_tracker[port].count > - ALERT_STORM_MAX_COUNT) { - CPRINTS("C%d Interrupt storm detected. " - "Disabling port for 5 seconds.", - port); - - pd_set_suspend(port, 1); - pd_deferred_resume(port); - } - } - } - } -} -#endif /* HAS_TASK_PD_INT_C0 || HAS_TASK_PD_INT_C1 || HAS_TASK_PD_INT_C2 */ - -void pd_task(void *u) -{ - int head; - int port = TASK_ID_TO_PD_PORT(task_get_current()); - uint32_t payload[7]; - int timeout = 10*MSEC; - enum tcpc_cc_voltage_status cc1, cc2; - int res, incoming_packet = 0; - int hard_reset_count = 0; -#ifdef CONFIG_USB_PD_DUAL_ROLE - uint64_t next_role_swap = PD_T_DRP_SNK; - uint8_t saved_flgs = 0; -#ifndef CONFIG_USB_PD_VBUS_DETECT_NONE - int snk_hard_reset_vbus_off = 0; -#endif -#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE - const int auto_toggle_supported = tcpm_auto_toggle_supported(port); -#endif -#if defined(CONFIG_CHARGE_MANAGER) - typec_current_t typec_curr = 0, typec_curr_change = 0; -#endif /* CONFIG_CHARGE_MANAGER */ -#endif /* CONFIG_USB_PD_DUAL_ROLE */ - enum pd_states this_state; - enum pd_cc_states new_cc_state; - timestamp_t now; - uint64_t next_src_cap = 0; - int caps_count = 0, hard_reset_sent = 0; - int snk_cap_count = 0; - int evt; - -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER - /* - * Set the ports in Low Power Mode so that other tasks wait until - * TCPC is initialized and ready. - */ - pd[port].flags |= PD_FLAGS_LPM_ENGAGED; -#endif - -#ifdef CONFIG_COMMON_RUNTIME - pd_init_tasks(); -#endif - - /* - * Ensure the power supply is in the default state and ensure we are not - * sourcing Vconn - */ - pd_power_supply_reset(port); -#ifdef CONFIG_USBC_VCONN - set_vconn(port, 0); -#endif - - /* Initialize TCPM driver and wait for TCPC to be ready */ - res = reset_device_and_notify(port); - invalidate_last_message_id(port); - -#ifdef CONFIG_USB_PD_DUAL_ROLE - pd_partner_port_reset(port); -#endif - - this_state = res ? PD_STATE_SUSPENDED : PD_DEFAULT_STATE(port); -#ifndef CONFIG_USB_PD_TCPC - if (!res) { - struct ec_response_pd_chip_info_v1 *info; - - if (tcpm_get_chip_info(port, 0, &info) == - EC_SUCCESS) { - CPRINTS("TCPC p%d VID:0x%x PID:0x%x DID:0x%x " - "FWV:0x%" PRIx64, - port, info->vendor_id, info->product_id, - info->device_id, info->fw_version_number); - } - } -#endif - -#ifdef CONFIG_USB_PD_REV30 - /* Set Revision to highest */ - pd[port].rev = PD_REV30; - pd_ca_reset(port); -#endif - -#ifdef CONFIG_USB_PD_DUAL_ROLE - /* - * If VBUS is high, then initialize flag for VBUS has always been - * present. This flag is used to maintain a PD connection after a - * reset by sending a soft reset. - */ - pd[port].flags |= - pd_is_vbus_present(port) ? PD_FLAGS_VBUS_NEVER_LOW : 0; -#endif - - /* Disable TCPC RX until connection is established */ - tcpm_set_rx_enable(port, 0); - -#ifdef CONFIG_USBC_SS_MUX - /* Initialize USB mux to its default state */ - usb_mux_init(port); -#endif - -#ifdef CONFIG_USB_PD_DUAL_ROLE - /* - * If there's an explicit contract in place, let's restore the data and - * power roles such that any messages we send to the port partner will - * still be valid. - */ - if (pd_comm_is_enabled(port) && - (pd_get_saved_port_flags(port, &saved_flgs) == EC_SUCCESS) && - (saved_flgs & PD_BBRMFLG_EXPLICIT_CONTRACT)) { - /* Only attempt to maintain previous sink contracts */ - if ((saved_flgs & PD_BBRMFLG_POWER_ROLE) == PD_ROLE_SINK) { - pd_set_power_role(port, - (saved_flgs & PD_BBRMFLG_POWER_ROLE) ? - PD_ROLE_SOURCE : PD_ROLE_SINK); - pd_set_data_role(port, - (saved_flgs & PD_BBRMFLG_DATA_ROLE) ? - PD_ROLE_DFP : PD_ROLE_UFP); -#ifdef CONFIG_USBC_VCONN - pd_set_vconn_role(port, - (saved_flgs & PD_BBRMFLG_VCONN_ROLE) ? - PD_ROLE_VCONN_ON : PD_ROLE_VCONN_OFF); -#endif /* CONFIG_USBC_VCONN */ - - /* - * Since there is an explicit contract in place, let's - * issue a SoftReset such that we can renegotiate with - * our port partner in order to synchronize our state - * machines. - */ - this_state = PD_STATE_SOFT_RESET; - - /* - * Re-discover any alternate modes we may have been - * using with this port partner. - */ - pd[port].flags |= PD_FLAGS_CHECK_IDENTITY; - } else { - /* - * Vbus was turned off during the power supply reset - * earlier, so clear the contract flag and re-start as - * default role - */ - pd_update_saved_port_flags(port, - PD_BBRMFLG_EXPLICIT_CONTRACT, 0); - - } - /* - * Set the TCPC reset event such that we can set our CC - * terminations, determine polarity, and enable RX so we - * can hear back from our port partner if maintaining our old - * connection. - */ - task_set_event(task_get_current(), PD_EVENT_TCPC_RESET, 0); - } -#endif /* defined(CONFIG_USB_PD_DUAL_ROLE) */ - /* Set the power role if we haven't already. */ - if (this_state != PD_STATE_SOFT_RESET) - pd_set_power_role(port, PD_ROLE_DEFAULT(port)); - - /* Initialize PD protocol state variables for each port. */ - pd[port].vdm_state = VDM_STATE_DONE; - set_state(port, this_state); - tcpm_select_rp_value(port, CONFIG_USB_PD_PULLUP); -#ifdef CONFIG_USB_PD_DUAL_ROLE - /* - * If we're not in an explicit contract, set our terminations to match - * our default power role. - */ - if (!(saved_flgs & PD_BBRMFLG_EXPLICIT_CONTRACT)) -#endif /* CONFIG_USB_PD_DUAL_ROLE */ - tcpm_set_cc(port, PD_ROLE_DEFAULT(port) == PD_ROLE_SOURCE ? - TYPEC_CC_RP : TYPEC_CC_RD); - -#ifdef CONFIG_USBC_PPC - /* - * Wait to initialize the PPC after setting the correct Rd values in - * the TCPC otherwise the TCPC might not be pulling the CC lines down - * when the PPC connects the CC lines from the USB connector to the - * TCPC cause the source to drop Vbus causing a brown out. - */ - ppc_init(port); -#endif - -#ifdef CONFIG_USB_PD_ALT_MODE_DFP - /* Initialize PD Policy engine */ - pd_dfp_pe_init(port); -#endif - -#ifdef CONFIG_CHARGE_MANAGER - /* Initialize PD and type-C supplier current limits to 0 */ - pd_set_input_current_limit(port, 0, 0); - typec_set_input_current_limit(port, 0, 0); - charge_manager_update_dualrole(port, CAP_UNKNOWN); -#endif - -#ifdef HAS_DEFFERED_INTERRUPT_HANDLER - /* - * Since most boards configure the TCPC interrupt as edge - * and it is possible that the interrupt line was asserted between init - * and calling set_state, we need to process any pending interrupts now. - * Otherwise future interrupts will never fire because another edge - * never happens. Note this needs to happen after set_state() is called. - */ - schedule_deferred_pd_interrupt(port); -#endif - - while (1) { -#ifdef CONFIG_USB_PD_REV30 - /* send any pending messages */ - pd_ca_send_pending(port); -#endif - /* process VDM messages last */ - pd_vdm_send_state_machine(port); - - /* Verify board specific health status : current, voltages... */ - res = pd_board_checks(); - if (res != EC_SUCCESS) { - /* cut the power */ - pd_execute_hard_reset(port); - /* notify the other side of the issue */ - pd_transmit(port, TCPC_TX_HARD_RESET, 0, NULL); - } - - /* wait for next event/packet or timeout expiration */ - evt = task_wait_event(timeout); - -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER - if (evt & PD_EXIT_LOW_POWER_EVENT_MASK) - exit_low_power_mode(port); - if (evt & PD_EVENT_DEVICE_ACCESSED) - handle_device_access(port); -#endif -#ifdef CONFIG_POWER_COMMON - if (evt & PD_EVENT_POWER_STATE_CHANGE) - handle_new_power_state(port); -#endif - -#if defined(CONFIG_USB_PD_ALT_MODE_DFP) - if (evt & PD_EVENT_SYSJUMP) { - exit_dp_mode(port); - notify_sysjump_ready(&sysjump_task_waiting); - - } -#endif - -#ifdef CONFIG_USB_PD_DUAL_ROLE - if (evt & PD_EVENT_UPDATE_DUAL_ROLE) - pd_update_dual_role_config(port); -#endif - -#ifdef CONFIG_USB_PD_TCPC - /* - * run port controller task to check CC and/or read incoming - * messages - */ - tcpc_run(port, evt); -#else - /* if TCPC has reset, then need to initialize it again */ - if (evt & PD_EVENT_TCPC_RESET) { - reset_device_and_notify(port); -#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE - } - - if ((evt & PD_EVENT_TCPC_RESET) && - (pd[port].task_state != PD_STATE_DRP_AUTO_TOGGLE)) { -#endif -#ifdef CONFIG_USB_PD_DUAL_ROLE - if (pd[port].task_state == PD_STATE_SOFT_RESET) { - enum tcpc_cc_voltage_status cc1, cc2; - - /* - * Set the terminations to match our power - * role. - */ - tcpm_set_cc(port, pd[port].power_role ? - TYPEC_CC_RP : TYPEC_CC_RD); - - /* Determine the polarity. */ - tcpm_get_cc(port, &cc1, &cc2); - if (pd[port].power_role == PD_ROLE_SINK) { - pd[port].polarity = - get_snk_polarity(cc1, cc2); - } else if (cc_is_snk_dbg_acc(cc1, cc2)) { - pd[port].polarity = - board_get_src_dts_polarity( - port); - } else { - pd[port].polarity = - (cc1 != TYPEC_CC_VOLT_RD); - } - } else -#endif /* CONFIG_USB_PD_DUAL_ROLE */ - { - /* Ensure CC termination is default */ - tcpm_set_cc(port, PD_ROLE_DEFAULT(port) == - PD_ROLE_SOURCE ? TYPEC_CC_RP : - TYPEC_CC_RD); - } - - /* - * If we have a stable contract in the default role, - * then simply update TCPC with some missing info - * so that we can continue without resetting PD comms. - * Otherwise, go to the default disconnected state - * and force renegotiation. - */ - if (pd[port].vdm_state == VDM_STATE_DONE && ( -#ifdef CONFIG_USB_PD_DUAL_ROLE - (PD_ROLE_DEFAULT(port) == PD_ROLE_SINK && - pd[port].task_state == PD_STATE_SNK_READY) || - (pd[port].task_state == PD_STATE_SOFT_RESET) || -#endif - (PD_ROLE_DEFAULT(port) == PD_ROLE_SOURCE && - pd[port].task_state == PD_STATE_SRC_READY))) { - set_polarity(port, pd[port].polarity); - tcpm_set_msg_header(port, pd[port].power_role, - pd[port].data_role); - tcpm_set_rx_enable(port, 1); - } else { - /* Ensure state variables are at default */ - pd_set_power_role(port, PD_ROLE_DEFAULT(port)); - pd[port].vdm_state = VDM_STATE_DONE; - set_state(port, PD_DEFAULT_STATE(port)); - } - } -#endif - -#ifdef CONFIG_USBC_PPC - /* - * TODO: Useful for non-PPC cases as well, but only needed - * for PPC cases right now. Revisit later. - */ - if (evt & PD_EVENT_SEND_HARD_RESET) - set_state(port, PD_STATE_HARD_RESET_SEND); -#endif /* defined(CONFIG_USBC_PPC) */ - - /* process any potential incoming message */ - incoming_packet = tcpm_has_pending_message(port); - if (incoming_packet) { - /* Dequeue and consume duplicate message ID. */ - if (tcpm_dequeue_message(port, payload, &head) == - EC_SUCCESS - && !consume_repeat_message(port, head) - ) - handle_request(port, head, payload); - - /* Check if there are any more messages */ - if (tcpm_has_pending_message(port)) - task_set_event(PD_PORT_TO_TASK_ID(port), - TASK_EVENT_WAKE, 0); - } - - if (pd[port].req_suspend_state) - set_state(port, PD_STATE_SUSPENDED); - - /* if nothing to do, verify the state of the world in 500ms */ - this_state = pd[port].task_state; - timeout = 500*MSEC; - switch (this_state) { - case PD_STATE_DISABLED: - /* Nothing to do */ - break; - case PD_STATE_SRC_DISCONNECTED: - timeout = 10*MSEC; - -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER - /* - * If SW decided we should be in a low power state and - * the CC lines did not change, then don't talk with the - * TCPC otherwise we might wake it up. - */ - if (pd[port].flags & PD_FLAGS_LPM_REQUESTED && - !(evt & PD_EVENT_CC)) { - timeout = -1; - break; - } -#endif /* CONFIG_USB_PD_TCPC_LOW_POWER */ - - tcpm_get_cc(port, &cc1, &cc2); - -#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE - /* - * Attempt TCPC auto DRP toggle if it is - * not already auto toggling and not try.src - */ - if (auto_toggle_supported && - !(pd[port].flags & PD_FLAGS_TCPC_DRP_TOGGLE) && - !is_try_src(port) && - cc_is_open(cc1, cc2)) { - set_state(port, PD_STATE_DRP_AUTO_TOGGLE); - timeout = 2*MSEC; - break; - } -#endif - /* - * Transition to DEBOUNCE if we detect appropriate - * signals - * - * (from 4.5.2.2.10.2 Exiting from Try.SRC State) - * If try_src -and- - * have only one Rd (not both) => DEBOUNCE - * - * (from 4.5.2.2.7.2 Exiting from Unattached.SRC State) - * If not try_src -and- - * have at least one Rd => DEBOUNCE -or- - * have audio access => DEBOUNCE - * - * try_src should not exit if both pins are Rd - */ - if ((is_try_src(port) && cc_is_only_one_rd(cc1, cc2)) || - (!is_try_src(port) && - (cc_is_at_least_one_rd(cc1, cc2) || - cc_is_audio_acc(cc1, cc2)))) { -#ifdef CONFIG_USBC_BACKWARDS_COMPATIBLE_DFP - /* Enable VBUS */ - if (pd_set_power_supply_ready(port)) - break; -#endif - pd[port].cc_state = PD_CC_NONE; - set_state(port, - PD_STATE_SRC_DISCONNECTED_DEBOUNCE); - break; - } -#if defined(CONFIG_USB_PD_DUAL_ROLE) - now = get_time(); - /* - * Try.SRC state is embedded here. The port - * shall transition to TryWait.SNK after - * tDRPTry (PD_T_DRP_TRY) and Vbus is within - * vSafe0V, or after tTryTimeout - * (PD_T_TRY_TIMEOUT). Otherwise we should stay - * within Try.SRC (break). - */ - if (is_try_src(port)) { - if (now.val < pd[port].try_src_marker) { - break; - } else if (now.val < pd[port].try_timeout) { - if (pd_is_vbus_present(port)) - break; - } - - /* - * Transition to TryWait.SNK now, so set - * state and update src marker time. - */ - set_state(port, PD_STATE_SNK_DISCONNECTED); - pd_set_power_role(port, PD_ROLE_SINK); - tcpm_set_cc(port, TYPEC_CC_RD); - pd[port].try_src_marker = - get_time().val + PD_T_DEBOUNCE; - timeout = 2 * MSEC; - break; - } - - /* - * If Try.SRC state is not active, then handle - * the normal DRP toggle from SRC->SNK. - */ - if (now.val < next_role_swap || - drp_state[port] == PD_DRP_FORCE_SOURCE || - drp_state[port] == PD_DRP_FREEZE) - break; - - /* - * Transition to SNK now, so set state and - * update next role swap time. - */ - set_state(port, PD_STATE_SNK_DISCONNECTED); - pd_set_power_role(port, PD_ROLE_SINK); - tcpm_set_cc(port, TYPEC_CC_RD); - next_role_swap = get_time().val + PD_T_DRP_SNK; - /* Swap states quickly */ - timeout = 2 * MSEC; -#endif - break; - case PD_STATE_SRC_DISCONNECTED_DEBOUNCE: - timeout = 20*MSEC; - tcpm_get_cc(port, &cc1, &cc2); - - if (cc_is_snk_dbg_acc(cc1, cc2)) { - /* Debug accessory */ - new_cc_state = PD_CC_UFP_DEBUG_ACC; - } else if (cc_is_at_least_one_rd(cc1, cc2)) { - /* UFP attached */ - new_cc_state = PD_CC_UFP_ATTACHED; - } else if (cc_is_audio_acc(cc1, cc2)) { - /* Audio accessory */ - new_cc_state = PD_CC_UFP_AUDIO_ACC; - } else { - /* No UFP */ - set_state(port, PD_STATE_SRC_DISCONNECTED); - timeout = 5*MSEC; - break; - } - - /* Set debounce timer */ - if (new_cc_state != pd[port].cc_state) { - pd[port].cc_debounce = - get_time().val + - (is_try_src(port) ? PD_T_DEBOUNCE - : PD_T_CC_DEBOUNCE); - pd[port].cc_state = new_cc_state; - break; - } - - /* Debounce the cc state */ - if (get_time().val < pd[port].cc_debounce) - break; - - /* Debounce complete */ - if (IS_ENABLED(CONFIG_COMMON_RUNTIME)) - hook_notify(HOOK_USB_PD_CONNECT); - -#ifdef CONFIG_USBC_PPC - /* - * If the port is latched off, just continue to - * monitor for a detach. - */ - if (ppc_is_port_latched_off(port)) - break; -#endif /* CONFIG_USBC_PPC */ - - /* UFP is attached */ - if (new_cc_state == PD_CC_UFP_ATTACHED || - new_cc_state == PD_CC_UFP_DEBUG_ACC) { -#ifdef CONFIG_USBC_PPC - /* Inform PPC that a sink is connected. */ - ppc_sink_is_connected(port, 1); -#endif /* CONFIG_USBC_PPC */ - if (new_cc_state == PD_CC_UFP_DEBUG_ACC) { - pd[port].polarity = - board_get_src_dts_polarity( - port); - } else { - pd[port].polarity = - (cc1 != TYPEC_CC_VOLT_RD); - } - set_polarity(port, pd[port].polarity); - - /* initial data role for source is DFP */ - pd_set_data_role(port, PD_ROLE_DFP); - - if (new_cc_state == PD_CC_UFP_DEBUG_ACC) - pd[port].flags |= - PD_FLAGS_TS_DTS_PARTNER; - -#ifdef CONFIG_USBC_VCONN - /* - * Do not source Vconn when debug accessory is - * detected. Section 4.5.2.2.17.1 in USB spec - * v1-3 - */ - if (new_cc_state != PD_CC_UFP_DEBUG_ACC) { - /* - * Start sourcing Vconn before Vbus to - * ensure we are within USB Type-C - * Spec 1.3 tVconnON. - */ - set_vconn(port, 1); - pd_set_vconn_role(port, - PD_ROLE_VCONN_ON); - } -#endif - -#ifndef CONFIG_USBC_BACKWARDS_COMPATIBLE_DFP - /* Enable VBUS */ - if (pd_set_power_supply_ready(port)) { -#ifdef CONFIG_USBC_VCONN - /* Stop sourcing Vconn if Vbus failed */ - set_vconn(port, 0); - pd_set_vconn_role(port, - PD_ROLE_VCONN_OFF); -#endif /* CONFIG_USBC_VCONN */ -#ifdef CONFIG_USBC_SS_MUX - usb_mux_set(port, TYPEC_MUX_NONE, - USB_SWITCH_DISCONNECT, - pd[port].polarity); -#endif /* CONFIG_USBC_SS_MUX */ - break; - } - /* - * Set correct Rp value determined during - * pd_set_power_supply_ready. This should be - * safe because Vconn is being sourced, - * preventing incorrect CCD detection. - */ - tcpm_set_cc(port, TYPEC_CC_RP); -#endif /* CONFIG_USBC_BACKWARDS_COMPATIBLE_DFP */ - /* If PD comm is enabled, enable TCPC RX */ - if (pd_comm_is_enabled(port)) - tcpm_set_rx_enable(port, 1); - - pd[port].flags |= PD_FLAGS_CHECK_PR_ROLE | - PD_FLAGS_CHECK_DR_ROLE; - hard_reset_count = 0; - timeout = 5*MSEC; - set_state(port, PD_STATE_SRC_STARTUP); - } - /* - * AUDIO_ACC will remain in this state indefinitely - * until disconnect. - */ - break; - case PD_STATE_SRC_HARD_RESET_RECOVER: - /* Do not continue until hard reset recovery time */ - if (get_time().val < pd[port].src_recover) { - timeout = 50*MSEC; - break; - } - -#ifdef CONFIG_USBC_VCONN - /* - * Start sourcing Vconn again and set the flag, in case - * it was 0 due to a previous swap - */ - set_vconn(port, 1); - pd_set_vconn_role(port, PD_ROLE_VCONN_ON); -#endif - - /* Enable VBUS */ - timeout = 10*MSEC; - if (pd_set_power_supply_ready(port)) { - set_state(port, PD_STATE_SRC_DISCONNECTED); - break; - } -#ifdef CONFIG_USB_PD_TCPM_TCPCI - /* - * After transmitting hard reset, TCPM writes - * to RECEIVE_DETECT register to enable - * PD message passing. - */ - if (pd_comm_is_enabled(port)) - tcpm_set_rx_enable(port, 1); -#endif /* CONFIG_USB_PD_TCPM_TCPCI */ - - set_state(port, PD_STATE_SRC_STARTUP); - break; - case PD_STATE_SRC_STARTUP: - /* Reset cable attributes and flags */ - reset_pd_cable(port); - /* Wait for power source to enable */ - if (pd[port].last_state != pd[port].task_state) { - pd[port].flags |= PD_FLAGS_CHECK_IDENTITY; - /* reset various counters */ - caps_count = 0; - pd[port].msg_id = 0; - snk_cap_count = 0; - set_state_timeout( - port, -#ifdef CONFIG_USBC_BACKWARDS_COMPATIBLE_DFP - /* - * delay for power supply to start up. - * subtract out debounce time if coming - * from debounce state since vbus is - * on during debounce. - */ - get_time().val + - PD_POWER_SUPPLY_TURN_ON_DELAY - - (pd[port].last_state == - PD_STATE_SRC_DISCONNECTED_DEBOUNCE - ? PD_T_CC_DEBOUNCE : 0), -#else - get_time().val + - PD_POWER_SUPPLY_TURN_ON_DELAY, -#endif - PD_STATE_SRC_DISCOVERY); - } - break; - case PD_STATE_SRC_DISCOVERY: - now = get_time(); - if (pd[port].last_state != pd[port].task_state) { - caps_count = 0; - next_src_cap = now.val; - /* - * If we have had PD connection with this port - * partner, then start NoResponseTimer. - */ - if (pd_capable(port)) - set_state_timeout(port, - get_time().val + - PD_T_NO_RESPONSE, - hard_reset_count < - PD_HARD_RESET_COUNT ? - PD_STATE_HARD_RESET_SEND : - PD_STATE_SRC_DISCONNECTED); - } - - /* Send source cap some minimum number of times */ - if (caps_count < PD_CAPS_COUNT && - next_src_cap <= now.val) { - /* Query capabilities of the other side */ - res = send_source_cap(port); - /* packet was acked => PD capable device) */ - if (res >= 0) { - set_state(port, - PD_STATE_SRC_NEGOCIATE); - timeout = 10*MSEC; - hard_reset_count = 0; - caps_count = 0; - /* Port partner is PD capable */ - pd[port].flags |= - PD_FLAGS_PREVIOUS_PD_CONN; - } else { /* failed, retry later */ - timeout = PD_T_SEND_SOURCE_CAP; - next_src_cap = now.val + - PD_T_SEND_SOURCE_CAP; - caps_count++; - } - } else if (caps_count < PD_CAPS_COUNT) { - timeout = next_src_cap - now.val; - } - break; - case PD_STATE_SRC_NEGOCIATE: - /* wait for a "Request" message */ - if (pd[port].last_state != pd[port].task_state) - set_state_timeout(port, - get_time().val + - PD_T_SENDER_RESPONSE, - PD_STATE_HARD_RESET_SEND); - break; - case PD_STATE_SRC_ACCEPTED: - /* Accept sent, wait for enabling the new voltage */ - if (pd[port].last_state != pd[port].task_state) - set_state_timeout( - port, - get_time().val + - PD_T_SINK_TRANSITION, - PD_STATE_SRC_POWERED); - break; - case PD_STATE_SRC_POWERED: - /* Switch to the new requested voltage */ - if (pd[port].last_state != pd[port].task_state) { - pd_transition_voltage(pd[port].requested_idx); - set_state_timeout( - port, - get_time().val + - PD_POWER_SUPPLY_TURN_ON_DELAY, - PD_STATE_SRC_TRANSITION); - } - break; - case PD_STATE_SRC_TRANSITION: - /* the voltage output is good, notify the source */ - res = send_control(port, PD_CTRL_PS_RDY); - if (res >= 0) { - timeout = 10*MSEC; - - /* - * Give the sink some time to send any messages - * before we may send messages of our own. Add - * some jitter of up to ~192ms, to prevent - * multiple collisions. This delay also allows - * the sink device to request power role swap - * and allow the the accept message to be sent - * prior to CMD_DISCOVER_IDENT being sent in the - * SRC_READY state. - */ - pd[port].ready_state_holdoff_timer = - get_time().val + SRC_READY_HOLD_OFF_US - + (get_time().le.lo & 0xf) * 12 * MSEC; - - /* it's time to ping regularly the sink */ - set_state(port, PD_STATE_SRC_READY); - } else { - /* The sink did not ack, cut the power... */ - set_state(port, PD_STATE_SRC_DISCONNECTED); - } - break; - case PD_STATE_SRC_READY: - timeout = PD_T_SOURCE_ACTIVITY; - - /* - * Don't send any traffic yet until our holdoff timer - * has expired. Some devices are chatty once we reach - * the SRC_READY state and we may end up in a collision - * of messages if we try to immediately send our - * interrogations. - */ - if (get_time().val <= - pd[port].ready_state_holdoff_timer) - break; - - /* - * Don't send any PD traffic if we woke up due to - * incoming packet or if VDO response pending to avoid - * collisions. - */ - if (incoming_packet || - (pd[port].vdm_state == VDM_STATE_BUSY)) - break; - - /* Send updated source capabilities to our partner */ - if (pd[port].flags & PD_FLAGS_UPDATE_SRC_CAPS) { - res = send_source_cap(port); - if (res >= 0) { - set_state(port, - PD_STATE_SRC_NEGOCIATE); - pd[port].flags &= - ~PD_FLAGS_UPDATE_SRC_CAPS; - } - break; - } - - /* Send get sink cap if haven't received it yet */ - if (!(pd[port].flags & PD_FLAGS_SNK_CAP_RECVD)) { - if (++snk_cap_count <= PD_SNK_CAP_RETRIES) { - /* Get sink cap to know if dual-role device */ - send_control(port, PD_CTRL_GET_SINK_CAP); - set_state(port, PD_STATE_SRC_GET_SINK_CAP); - break; - } else if (debug_level >= 2 && - snk_cap_count == PD_SNK_CAP_RETRIES+1) { - CPRINTF("C%d ERR SNK_CAP\n", port); - } - } - - /* Check power role policy, which may trigger a swap */ - if (pd[port].flags & PD_FLAGS_CHECK_PR_ROLE) { - pd_check_pr_role(port, PD_ROLE_SOURCE, - pd[port].flags); - pd[port].flags &= ~PD_FLAGS_CHECK_PR_ROLE; - break; - } - - /* Check data role policy, which may trigger a swap */ - if (pd[port].flags & PD_FLAGS_CHECK_DR_ROLE) { - pd_check_dr_role(port, pd[port].data_role, - pd[port].flags); - pd[port].flags &= ~PD_FLAGS_CHECK_DR_ROLE; - break; - } - - /* Send discovery SVDMs last */ - if (pd[port].data_role == PD_ROLE_DFP && - (pd[port].flags & PD_FLAGS_CHECK_IDENTITY)) { -#ifndef CONFIG_USB_PD_SIMPLE_DFP - pd_send_vdm(port, USB_SID_PD, - CMD_DISCOVER_IDENT, NULL, 0); -#endif - pd[port].flags &= ~PD_FLAGS_CHECK_IDENTITY; - break; - } - - if (!(pd[port].flags & PD_FLAGS_PING_ENABLED)) - break; - - /* Verify that the sink is alive */ - res = send_control(port, PD_CTRL_PING); - if (res >= 0) - break; - - /* Ping dropped. Try soft reset. */ - set_state(port, PD_STATE_SOFT_RESET); - timeout = 10 * MSEC; - break; - case PD_STATE_SRC_GET_SINK_CAP: - if (pd[port].last_state != pd[port].task_state) - set_state_timeout(port, - get_time().val + - PD_T_SENDER_RESPONSE, - PD_STATE_SRC_READY); - break; - case PD_STATE_DR_SWAP: - if (pd[port].last_state != pd[port].task_state) { - res = send_control(port, PD_CTRL_DR_SWAP); - if (res < 0) { - timeout = 10*MSEC; - /* - * If failed to get goodCRC, send - * soft reset, otherwise ignore - * failure. - */ - set_state(port, res == -1 ? - PD_STATE_SOFT_RESET : - READY_RETURN_STATE(port)); - break; - } - /* Wait for accept or reject */ - set_state_timeout(port, - get_time().val + - PD_T_SENDER_RESPONSE, - READY_RETURN_STATE(port)); - } - break; -#ifdef CONFIG_USB_PD_DUAL_ROLE - case PD_STATE_SRC_SWAP_INIT: - if (pd[port].last_state != pd[port].task_state) { - res = send_control(port, PD_CTRL_PR_SWAP); - if (res < 0) { - timeout = 10*MSEC; - /* - * If failed to get goodCRC, send - * soft reset, otherwise ignore - * failure. - */ - set_state(port, res == -1 ? - PD_STATE_SOFT_RESET : - PD_STATE_SRC_READY); - break; - } - /* Wait for accept or reject */ - set_state_timeout(port, - get_time().val + - PD_T_SENDER_RESPONSE, - PD_STATE_SRC_READY); - } - break; - case PD_STATE_SRC_SWAP_SNK_DISABLE: - /* Give time for sink to stop drawing current */ - if (pd[port].last_state != pd[port].task_state) - set_state_timeout(port, - get_time().val + - PD_T_SINK_TRANSITION, - PD_STATE_SRC_SWAP_SRC_DISABLE); - break; - case PD_STATE_SRC_SWAP_SRC_DISABLE: - if (pd[port].last_state != pd[port].task_state) { - /* Turn power off */ - pd_power_supply_reset(port); - - /* - * Switch to Rd and swap roles to sink - * - * The reason we do this as early as possible is - * to help prevent CC disconnection cases where - * both partners are applying an Rp. Certain PD - * stacks (e.g. qualcomm), reflexively apply - * their Rp once VBUS falls beneath - * ~3.67V. (b/77827528). - */ - tcpm_set_cc(port, TYPEC_CC_RD); - pd_set_power_role(port, PD_ROLE_SINK); - - /* Inform TCPC of power role update. */ - pd_update_roles(port); - - set_state_timeout(port, - get_time().val + - PD_POWER_SUPPLY_TURN_OFF_DELAY, - PD_STATE_SRC_SWAP_STANDBY); - } - break; - case PD_STATE_SRC_SWAP_STANDBY: - /* Send PS_RDY to let sink know our power is off */ - if (pd[port].last_state != pd[port].task_state) { - /* Send PS_RDY */ - res = send_control(port, PD_CTRL_PS_RDY); - if (res < 0) { - timeout = 10*MSEC; - set_state(port, - PD_STATE_SRC_DISCONNECTED); - break; - } - /* Wait for PS_RDY from new source */ - set_state_timeout(port, - get_time().val + - PD_T_PS_SOURCE_ON, - PD_STATE_SNK_DISCONNECTED); - } - break; - case PD_STATE_SUSPENDED: { -#ifndef CONFIG_USB_PD_TCPC - int rstatus; -#endif - CPRINTS("TCPC p%d suspended!", port); - pd[port].req_suspend_state = 0; -#ifdef CONFIG_USB_PD_TCPC - pd_rx_disable_monitoring(port); - pd_hw_release(port); - pd_power_supply_reset(port); -#else - pd_power_supply_reset(port); -#ifdef CONFIG_USBC_VCONN - set_vconn(port, 0); -#endif - rstatus = tcpm_release(port); - if (rstatus != 0 && rstatus != EC_ERROR_UNIMPLEMENTED) - CPRINTS("TCPC p%d release failed!", port); -#endif - /* Drain any outstanding software message queues. */ - tcpm_clear_pending_messages(port); - - /* Wait for resume */ - while (pd[port].task_state == PD_STATE_SUSPENDED) { -#ifdef CONFIG_USB_PD_ALT_MODE_DFP - int evt = task_wait_event(-1); - - if (evt & PD_EVENT_SYSJUMP) - /* Nothing to do for sysjump prep */ - notify_sysjump_ready( - &sysjump_task_waiting); -#else - task_wait_event(-1); -#endif - } -#ifdef CONFIG_USB_PD_TCPC - pd_hw_init(port, PD_ROLE_DEFAULT(port)); - CPRINTS("TCPC p%d resumed!", port); -#else - if (rstatus != EC_ERROR_UNIMPLEMENTED && - pd_restart_tcpc(port) != 0) { - /* stay in PD_STATE_SUSPENDED */ - CPRINTS("TCPC p%d restart failed!", port); - break; - } - /* Set the CC termination and state back to default */ - tcpm_set_cc(port, - PD_ROLE_DEFAULT(port) == PD_ROLE_SOURCE ? - TYPEC_CC_RP : - TYPEC_CC_RD); - set_state(port, PD_DEFAULT_STATE(port)); - CPRINTS("TCPC p%d resumed!", port); -#endif - break; - } - case PD_STATE_SNK_DISCONNECTED: -#ifdef CONFIG_USB_PD_LOW_POWER - timeout = (drp_state[port] != - PD_DRP_TOGGLE_ON ? SECOND : 10*MSEC); -#else - timeout = 10*MSEC; -#endif - -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER - /* - * If SW decided we should be in a low power state and - * the CC lines did not change, then don't talk with the - * TCPC otherwise we might wake it up. - */ - if (pd[port].flags & PD_FLAGS_LPM_REQUESTED && - !(evt & PD_EVENT_CC)) { - timeout = -1; - break; - } -#endif /* CONFIG_USB_PD_TCPC_LOW_POWER */ - - tcpm_get_cc(port, &cc1, &cc2); - -#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE - /* - * Attempt TCPC auto DRP toggle if it is not already - * auto toggling and not try.src, and dual role toggling - * is allowed. - */ - if (auto_toggle_supported && - !(pd[port].flags & PD_FLAGS_TCPC_DRP_TOGGLE) && - !is_try_src(port) && - cc_is_open(cc1, cc2) && - (drp_state[port] == PD_DRP_TOGGLE_ON)) { - set_state(port, PD_STATE_DRP_AUTO_TOGGLE); - timeout = 2*MSEC; - break; - } -#endif - - /* Source connection monitoring */ - if (!cc_is_open(cc1, cc2)) { - pd[port].cc_state = PD_CC_NONE; - hard_reset_count = 0; - new_cc_state = PD_CC_NONE; - pd[port].cc_debounce = get_time().val + - PD_T_CC_DEBOUNCE; - set_state(port, - PD_STATE_SNK_DISCONNECTED_DEBOUNCE); - timeout = 10*MSEC; - break; - } - - /* - * If Try.SRC is active and failed to detect a SNK, - * then it transitions to TryWait.SNK. Need to prevent - * normal dual role toggle until tDRPTryWait timer - * expires. - */ - if (pd[port].flags & PD_FLAGS_TRY_SRC) { - if (get_time().val > pd[port].try_src_marker) - pd[port].flags &= ~PD_FLAGS_TRY_SRC; - break; - } - - /* If no source detected, check for role toggle. */ - if (drp_state[port] == PD_DRP_TOGGLE_ON && - get_time().val >= next_role_swap) { - /* Swap roles to source */ - pd_set_power_role(port, PD_ROLE_SOURCE); - set_state(port, PD_STATE_SRC_DISCONNECTED); - tcpm_set_cc(port, TYPEC_CC_RP); - next_role_swap = get_time().val + PD_T_DRP_SRC; - -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER - /* - * Clear low power mode flag as we are swapping - * states quickly. - */ - pd[port].flags &= ~PD_FLAGS_LPM_REQUESTED; -#endif - - /* Swap states quickly */ - timeout = 2*MSEC; - break; - } - -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER - /* - * If we are remaining in the SNK_DISCONNECTED state, - * let's go into low power mode and wait for a change on - * CC status. - */ - pd[port].flags |= PD_FLAGS_LPM_REQUESTED; -#endif/* CONFIG_USB_PD_TCPC_LOW_POWER */ - break; - - case PD_STATE_SNK_DISCONNECTED_DEBOUNCE: - tcpm_get_cc(port, &cc1, &cc2); - - if (cc_is_rp(cc1) && cc_is_rp(cc2)) { - /* Debug accessory */ - new_cc_state = PD_CC_DFP_DEBUG_ACC; - } else if (cc_is_rp(cc1) || cc_is_rp(cc2)) { - new_cc_state = PD_CC_DFP_ATTACHED; - } else { - /* No connection any more */ - set_state(port, PD_STATE_SNK_DISCONNECTED); - timeout = 5*MSEC; - break; - } - - timeout = 20*MSEC; - - /* Debounce the cc state */ - if (new_cc_state != pd[port].cc_state) { - pd[port].cc_debounce = get_time().val + - PD_T_CC_DEBOUNCE; - pd[port].cc_state = new_cc_state; - break; - } - /* Wait for CC debounce and VBUS present */ - if (get_time().val < pd[port].cc_debounce || - !pd_is_vbus_present(port)) - break; - - if (pd_try_src_enable && - !(pd[port].flags & PD_FLAGS_TRY_SRC)) { - /* - * If TRY_SRC is enabled, but not active, - * then force attempt to connect as source. - */ - pd[port].try_src_marker = get_time().val - + PD_T_DRP_TRY; - pd[port].try_timeout = get_time().val - + PD_T_TRY_TIMEOUT; - /* Swap roles to source */ - pd_set_power_role(port, PD_ROLE_SOURCE); - tcpm_set_cc(port, TYPEC_CC_RP); - timeout = 2*MSEC; - set_state(port, PD_STATE_SRC_DISCONNECTED); - /* Set flag after the state change */ - pd[port].flags |= PD_FLAGS_TRY_SRC; - break; - } - - /* We are attached */ - if (IS_ENABLED(CONFIG_COMMON_RUNTIME)) - hook_notify(HOOK_USB_PD_CONNECT); - pd[port].polarity = get_snk_polarity(cc1, cc2); - set_polarity(port, pd[port].polarity); - /* reset message ID on connection */ - pd[port].msg_id = 0; - /* initial data role for sink is UFP */ - pd_set_data_role(port, PD_ROLE_UFP); -#if defined(CONFIG_CHARGE_MANAGER) - typec_curr = usb_get_typec_current_limit( - pd[port].polarity, cc1, cc2); - typec_set_input_current_limit( - port, typec_curr, TYPE_C_VOLTAGE); -#endif - /* If PD comm is enabled, enable TCPC RX */ - if (pd_comm_is_enabled(port)) - tcpm_set_rx_enable(port, 1); - - /* DFP is attached */ - if (new_cc_state == PD_CC_DFP_ATTACHED || - new_cc_state == PD_CC_DFP_DEBUG_ACC) { - pd[port].flags |= PD_FLAGS_CHECK_PR_ROLE | - PD_FLAGS_CHECK_DR_ROLE | - PD_FLAGS_CHECK_IDENTITY; - /* Reset cable attributes and flags */ - reset_pd_cable(port); - - if (new_cc_state == PD_CC_DFP_DEBUG_ACC) - pd[port].flags |= - PD_FLAGS_TS_DTS_PARTNER; - set_state(port, PD_STATE_SNK_DISCOVERY); - timeout = 10*MSEC; - hook_call_deferred( - &pd_usb_billboard_deferred_data, - PD_T_AME); - } - break; - case PD_STATE_SNK_HARD_RESET_RECOVER: - if (pd[port].last_state != pd[port].task_state) - pd[port].flags |= PD_FLAGS_CHECK_IDENTITY; -#ifdef CONFIG_USB_PD_VBUS_DETECT_NONE - /* - * Can't measure vbus state so this is the maximum - * recovery time for the source. - */ - if (pd[port].last_state != pd[port].task_state) - set_state_timeout(port, get_time().val + - PD_T_SAFE_0V + - PD_T_SRC_RECOVER_MAX + - PD_T_SRC_TURN_ON, - PD_STATE_SNK_DISCONNECTED); -#else - /* Wait for VBUS to go low and then high*/ - if (pd[port].last_state != pd[port].task_state) { - snk_hard_reset_vbus_off = 0; - set_state_timeout(port, - get_time().val + - PD_T_SAFE_0V, - hard_reset_count < - PD_HARD_RESET_COUNT ? - PD_STATE_HARD_RESET_SEND : - PD_STATE_SNK_DISCOVERY); - } - - if (!pd_is_vbus_present(port) && - !snk_hard_reset_vbus_off) { - /* VBUS has gone low, reset timeout */ - snk_hard_reset_vbus_off = 1; - set_state_timeout(port, - get_time().val + - PD_T_SRC_RECOVER_MAX + - PD_T_SRC_TURN_ON, - PD_STATE_SNK_DISCONNECTED); - } - if (pd_is_vbus_present(port) && - snk_hard_reset_vbus_off) { -#ifdef CONFIG_USB_PD_TCPM_TCPCI - /* - * After transmitting hard reset, TCPM writes - * to RECEIVE_MESSAGE register to enable - * PD message passing. - */ - if (pd_comm_is_enabled(port)) - tcpm_set_rx_enable(port, 1); -#endif /* CONFIG_USB_PD_TCPM_TCPCI */ - - /* VBUS went high again */ - set_state(port, PD_STATE_SNK_DISCOVERY); - timeout = 10*MSEC; - } - - /* - * Don't need to set timeout because VBUS changing - * will trigger an interrupt and wake us up. - */ -#endif - break; - case PD_STATE_SNK_DISCOVERY: - /* Wait for source cap expired only if we are enabled */ - if ((pd[port].last_state != pd[port].task_state) - && pd_comm_is_enabled(port)) { -#ifdef CONFIG_USB_PD_RESET_MIN_BATT_SOC - /* - * If the battery has not met a configured safe - * level for hard resets, refrain from starting - * reset timers as a hard reset could brown out - * the board. Note this may mean that - * high-power chargers will stay at 15W until a - * reset is sent, depending on boot timing. - */ - int batt_soc = usb_get_battery_soc(); - - if (batt_soc < CONFIG_USB_PD_RESET_MIN_BATT_SOC) - pd[port].flags |= - PD_FLAGS_SNK_WAITING_BATT; - else - pd[port].flags &= - ~PD_FLAGS_SNK_WAITING_BATT; -#endif - - if (pd[port].flags & - PD_FLAGS_SNK_WAITING_BATT) { -#ifdef CONFIG_CHARGE_MANAGER - /* - * Configure this port as dedicated for - * now, so it won't be de-selected by - * the charge manager leaving safe mode. - */ - charge_manager_update_dualrole(port, - CAP_DEDICATED); -#endif - CPRINTS("C%d: Battery low. " - "Hold reset timer", port); - /* - * If VBUS has never been low, and we timeout - * waiting for source cap, try a soft reset - * first, in case we were already in a stable - * contract before this boot. - */ - } else if (pd[port].flags & - PD_FLAGS_VBUS_NEVER_LOW) { - set_state_timeout(port, - get_time().val + - PD_T_SINK_WAIT_CAP, - PD_STATE_SOFT_RESET); - /* - * If we haven't passed hard reset counter, - * start SinkWaitCapTimer, otherwise start - * NoResponseTimer. - */ - } else if (hard_reset_count < - PD_HARD_RESET_COUNT) { - set_state_timeout(port, - get_time().val + - PD_T_SINK_WAIT_CAP, - PD_STATE_HARD_RESET_SEND); - } else if (pd_capable(port)) { - /* ErrorRecovery */ - set_state_timeout(port, - get_time().val + - PD_T_NO_RESPONSE, - PD_STATE_SNK_DISCONNECTED); - } -#if defined(CONFIG_CHARGE_MANAGER) - /* - * If we didn't come from disconnected, must - * have come from some path that did not set - * typec current limit. So, set to 0 so that - * we guarantee this is revised below. - */ - if (pd[port].last_state != - PD_STATE_SNK_DISCONNECTED_DEBOUNCE) - typec_curr = 0; -#endif - } - -#if defined(CONFIG_CHARGE_MANAGER) - timeout = PD_T_SINK_ADJ - PD_T_DEBOUNCE; - - /* Check if CC pull-up has changed */ - tcpm_get_cc(port, &cc1, &cc2); - if (typec_curr != usb_get_typec_current_limit( - pd[port].polarity, cc1, cc2)) { - /* debounce signal by requiring two reads */ - if (typec_curr_change) { - /* set new input current limit */ - typec_curr = - usb_get_typec_current_limit( - pd[port].polarity, - cc1, cc2); - typec_set_input_current_limit( - port, typec_curr, TYPE_C_VOLTAGE); - } else { - /* delay for debounce */ - timeout = PD_T_DEBOUNCE; - } - typec_curr_change = !typec_curr_change; - } else { - typec_curr_change = 0; - } -#endif - break; - case PD_STATE_SNK_REQUESTED: - /* Wait for ACCEPT or REJECT */ - if (pd[port].last_state != pd[port].task_state) { - hard_reset_count = 0; - set_state_timeout(port, - get_time().val + - PD_T_SENDER_RESPONSE, - PD_STATE_HARD_RESET_SEND); - } - break; - case PD_STATE_SNK_TRANSITION: - /* Wait for PS_RDY */ - if (pd[port].last_state != pd[port].task_state) - set_state_timeout(port, - get_time().val + - PD_T_PS_TRANSITION, - PD_STATE_HARD_RESET_SEND); - break; - case PD_STATE_SNK_READY: - timeout = 20*MSEC; - - /* - * Don't send any traffic yet until our holdoff timer - * has expired. Some devices are chatty once we reach - * the SNK_READY state and we may end up in a collision - * of messages if we try to immediately send our - * interrogations. - */ - if (get_time().val <= - pd[port].ready_state_holdoff_timer) - break; - - /* - * Don't send any PD traffic if we woke up due to - * incoming packet or if VDO response pending to avoid - * collisions. - */ - if (incoming_packet || - (pd[port].vdm_state == VDM_STATE_BUSY)) - break; - - /* Check for new power to request */ - if (pd[port].new_power_request) { - if (pd_send_request_msg(port, 0) != EC_SUCCESS) - set_state(port, PD_STATE_SOFT_RESET); - break; - } - - /* Check power role policy, which may trigger a swap */ - if (pd[port].flags & PD_FLAGS_CHECK_PR_ROLE) { - pd_check_pr_role(port, PD_ROLE_SINK, - pd[port].flags); - pd[port].flags &= ~PD_FLAGS_CHECK_PR_ROLE; - break; - } - - /* Check data role policy, which may trigger a swap */ - if (pd[port].flags & PD_FLAGS_CHECK_DR_ROLE) { - pd_check_dr_role(port, pd[port].data_role, - pd[port].flags); - pd[port].flags &= ~PD_FLAGS_CHECK_DR_ROLE; - break; - } - - /* If DFP, send discovery SVDMs */ - if (pd[port].data_role == PD_ROLE_DFP && - (pd[port].flags & PD_FLAGS_CHECK_IDENTITY)) { - pd_send_vdm(port, USB_SID_PD, - CMD_DISCOVER_IDENT, NULL, 0); - pd[port].flags &= ~PD_FLAGS_CHECK_IDENTITY; - break; - } - - /* Sent all messages, don't need to wake very often */ - timeout = 200*MSEC; - break; - case PD_STATE_SNK_SWAP_INIT: - if (pd[port].last_state != pd[port].task_state) { - res = send_control(port, PD_CTRL_PR_SWAP); - if (res < 0) { - timeout = 10*MSEC; - /* - * If failed to get goodCRC, send - * soft reset, otherwise ignore - * failure. - */ - set_state(port, res == -1 ? - PD_STATE_SOFT_RESET : - PD_STATE_SNK_READY); - break; - } - /* Wait for accept or reject */ - set_state_timeout(port, - get_time().val + - PD_T_SENDER_RESPONSE, - PD_STATE_SNK_READY); - } - break; - case PD_STATE_SNK_SWAP_SNK_DISABLE: - /* Stop drawing power */ - pd_set_input_current_limit(port, 0, 0); -#ifdef CONFIG_CHARGE_MANAGER - typec_set_input_current_limit(port, 0, 0); - charge_manager_set_ceil(port, - CEIL_REQUESTOR_PD, - CHARGE_CEIL_NONE); -#endif - set_state(port, PD_STATE_SNK_SWAP_SRC_DISABLE); - timeout = 10*MSEC; - break; - case PD_STATE_SNK_SWAP_SRC_DISABLE: - /* Wait for PS_RDY */ - if (pd[port].last_state != pd[port].task_state) - set_state_timeout(port, - get_time().val + - PD_T_PS_SOURCE_OFF, - PD_STATE_HARD_RESET_SEND); - break; - case PD_STATE_SNK_SWAP_STANDBY: - if (pd[port].last_state != pd[port].task_state) { - /* Switch to Rp and enable power supply. */ - tcpm_set_cc(port, TYPEC_CC_RP); - if (pd_set_power_supply_ready(port)) { - /* Restore Rd */ - tcpm_set_cc(port, TYPEC_CC_RD); - timeout = 10*MSEC; - set_state(port, - PD_STATE_SNK_DISCONNECTED); - break; - } - /* Wait for power supply to turn on */ - set_state_timeout( - port, - get_time().val + - PD_POWER_SUPPLY_TURN_ON_DELAY, - PD_STATE_SNK_SWAP_COMPLETE); - } - break; - case PD_STATE_SNK_SWAP_COMPLETE: - /* Send PS_RDY and change to source role */ - res = send_control(port, PD_CTRL_PS_RDY); - if (res < 0) { - /* Restore Rd */ - tcpm_set_cc(port, TYPEC_CC_RD); - pd_power_supply_reset(port); - timeout = 10 * MSEC; - set_state(port, PD_STATE_SNK_DISCONNECTED); - break; - } - - /* Don't send GET_SINK_CAP on swap */ - snk_cap_count = PD_SNK_CAP_RETRIES+1; - caps_count = 0; - pd[port].msg_id = 0; - pd_set_power_role(port, PD_ROLE_SOURCE); - pd_update_roles(port); - set_state(port, PD_STATE_SRC_DISCOVERY); - timeout = 10*MSEC; - break; -#ifdef CONFIG_USBC_VCONN_SWAP - case PD_STATE_VCONN_SWAP_SEND: - if (pd[port].last_state != pd[port].task_state) { - res = send_control(port, PD_CTRL_VCONN_SWAP); - if (res < 0) { - timeout = 10*MSEC; - /* - * If failed to get goodCRC, send - * soft reset, otherwise ignore - * failure. - */ - set_state(port, res == -1 ? - PD_STATE_SOFT_RESET : - READY_RETURN_STATE(port)); - break; - } - /* Wait for accept or reject */ - set_state_timeout(port, - get_time().val + - PD_T_SENDER_RESPONSE, - READY_RETURN_STATE(port)); - } - break; - case PD_STATE_VCONN_SWAP_INIT: - if (pd[port].last_state != pd[port].task_state) { - if (!(pd[port].flags & PD_FLAGS_VCONN_ON)) { - /* Turn VCONN on and wait for it */ - set_vconn(port, 1); - set_state_timeout(port, - get_time().val + PD_VCONN_SWAP_DELAY, - PD_STATE_VCONN_SWAP_READY); - } else { - set_state_timeout(port, - get_time().val + PD_T_VCONN_SOURCE_ON, - READY_RETURN_STATE(port)); - } - } - break; - case PD_STATE_VCONN_SWAP_READY: - if (pd[port].last_state != pd[port].task_state) { - if (!(pd[port].flags & PD_FLAGS_VCONN_ON)) { - /* VCONN is now on, send PS_RDY */ - pd_set_vconn_role(port, - PD_ROLE_VCONN_ON); - res = send_control(port, - PD_CTRL_PS_RDY); - if (res == -1) { - timeout = 10*MSEC; - /* - * If failed to get goodCRC, - * send soft reset - */ - set_state(port, - PD_STATE_SOFT_RESET); - break; - } - set_state(port, - READY_RETURN_STATE(port)); - } else { - /* Turn VCONN off and wait for it */ - set_vconn(port, 0); - pd_set_vconn_role(port, - PD_ROLE_VCONN_OFF); - set_state_timeout(port, - get_time().val + PD_VCONN_SWAP_DELAY, - READY_RETURN_STATE(port)); - } - } - break; -#endif /* CONFIG_USBC_VCONN_SWAP */ -#endif /* CONFIG_USB_PD_DUAL_ROLE */ - case PD_STATE_SOFT_RESET: - if (pd[port].last_state != pd[port].task_state) { - /* Message ID of soft reset is always 0 */ - pd[port].msg_id = 0; - res = send_control(port, PD_CTRL_SOFT_RESET); - - /* if soft reset failed, try hard reset. */ - if (res < 0) { - set_state(port, - PD_STATE_HARD_RESET_SEND); - timeout = 5*MSEC; - break; - } - - set_state_timeout( - port, - get_time().val + PD_T_SENDER_RESPONSE, - PD_STATE_HARD_RESET_SEND); - } - break; - case PD_STATE_HARD_RESET_SEND: - hard_reset_count++; - if (pd[port].last_state != pd[port].task_state) { - hard_reset_sent = 0; - pd[port].hard_reset_complete_timer = 0; - } -#ifdef CONFIG_CHARGE_MANAGER - if (pd[port].last_state == PD_STATE_SNK_DISCOVERY || - (pd[port].last_state == PD_STATE_SOFT_RESET && - (pd[port].flags & PD_FLAGS_VBUS_NEVER_LOW))) { - pd[port].flags &= ~PD_FLAGS_VBUS_NEVER_LOW; - /* - * If discovery timed out, assume that we - * have a dedicated charger attached. This - * may not be a correct assumption according - * to the specification, but it generally - * works in practice and the harmful - * effects of a wrong assumption here - * are minimal. - */ - charge_manager_update_dualrole(port, - CAP_DEDICATED); - } -#endif - - if (hard_reset_sent) - break; - - if (pd_transmit(port, TCPC_TX_HARD_RESET, 0, NULL) < - 0) { - /* - * likely a non-idle channel - * TCPCI r2.0 v1.0 4.4.15: - * the TCPC does not retry HARD_RESET - * but we can try periodically until the timer - * expires. - */ - now = get_time(); - if (pd[port].hard_reset_complete_timer == 0) { - pd[port].hard_reset_complete_timer = - now.val + - PD_T_HARD_RESET_COMPLETE; - timeout = PD_T_HARD_RESET_RETRY; - break; - } - if (now.val < - pd[port].hard_reset_complete_timer) { - CPRINTS("C%d: Retrying hard reset", - port); - timeout = PD_T_HARD_RESET_RETRY; - break; - } - /* - * PD 2.0 spec, section 6.5.11.1 - * Pretend TX_HARD_RESET succeeded after - * timeout. - */ - } - - hard_reset_sent = 1; - /* - * If we are source, delay before cutting power - * to allow sink time to get hard reset. - */ - if (pd[port].power_role == PD_ROLE_SOURCE) { - set_state_timeout(port, - get_time().val + PD_T_PS_HARD_RESET, - PD_STATE_HARD_RESET_EXECUTE); - } else { - set_state(port, PD_STATE_HARD_RESET_EXECUTE); - timeout = 10 * MSEC; - } - break; - case PD_STATE_HARD_RESET_EXECUTE: -#ifdef CONFIG_USB_PD_DUAL_ROLE - /* - * If hard reset while in the last stages of power - * swap, then we need to restore our CC resistor. - */ - if (pd[port].last_state == PD_STATE_SNK_SWAP_STANDBY) - tcpm_set_cc(port, TYPEC_CC_RD); -#endif - - /* reset our own state machine */ - pd_execute_hard_reset(port); - timeout = 10*MSEC; - break; -#ifdef CONFIG_COMMON_RUNTIME - case PD_STATE_BIST_RX: - send_bist_cmd(port); - /* Delay at least enough for partner to finish BIST */ - timeout = PD_T_BIST_RECEIVE + 20*MSEC; - /* Set to appropriate port disconnected state */ - set_state(port, DUAL_ROLE_IF_ELSE(port, - PD_STATE_SNK_DISCONNECTED, - PD_STATE_SRC_DISCONNECTED)); - break; - case PD_STATE_BIST_TX: - pd_transmit(port, TCPC_TX_BIST_MODE_2, 0, NULL); - /* Delay at least enough to finish sending BIST */ - timeout = PD_T_BIST_TRANSMIT + 20*MSEC; - /* Set to appropriate port disconnected state */ - set_state(port, DUAL_ROLE_IF_ELSE(port, - PD_STATE_SNK_DISCONNECTED, - PD_STATE_SRC_DISCONNECTED)); - break; -#endif -#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE - case PD_STATE_DRP_AUTO_TOGGLE: - { - enum pd_drp_next_states next_state; - - assert(auto_toggle_supported); - -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER - /* - * If SW decided we should be in a low power state and - * the CC lines did not change, then don't talk with the - * TCPC otherwise we might wake it up. - */ - if (pd[port].flags & PD_FLAGS_LPM_REQUESTED && - !(evt & PD_EVENT_CC)) - break; -#endif - - /* Check for connection */ - tcpm_get_cc(port, &cc1, &cc2); - - next_state = drp_auto_toggle_next_state( - &pd[port].drp_sink_time, - pd[port].power_role, - drp_state[port], - cc1, cc2); - -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER - /* - * The next state is not determined just by what is - * attached, but also depends on DRP_STATE. Regardless - * of next state, if nothing is attached, then always - * request low power mode. - */ - if (cc_is_open(cc1, cc2)) - pd[port].flags |= PD_FLAGS_LPM_REQUESTED; -#endif - if (next_state == DRP_TC_DEFAULT) { - if (PD_DEFAULT_STATE(port) == - PD_STATE_SNK_DISCONNECTED) - next_state = DRP_TC_UNATTACHED_SNK; - else - next_state = DRP_TC_UNATTACHED_SRC; - } - - if (next_state == DRP_TC_UNATTACHED_SNK) { - tcpm_set_cc(port, TYPEC_CC_RD); - pd_set_power_role(port, PD_ROLE_SINK); - timeout = 2*MSEC; - set_state(port, PD_STATE_SNK_DISCONNECTED); - } else if (next_state == DRP_TC_UNATTACHED_SRC) { - tcpm_set_cc(port, TYPEC_CC_RP); - pd_set_power_role(port, PD_ROLE_SOURCE); - timeout = 2*MSEC; - set_state(port, PD_STATE_SRC_DISCONNECTED); - } else { - /* - * We are staying in PD_STATE_DRP_AUTO_TOGGLE, - * therefore enable auto-toggle. - */ - tcpm_enable_drp_toggle(port); - pd[port].flags |= PD_FLAGS_TCPC_DRP_TOGGLE; - timeout = -1; - set_state(port, PD_STATE_DRP_AUTO_TOGGLE); - } - - break; - } -#endif - default: - break; - } - - pd[port].last_state = this_state; - - /* - * Check for state timeout, and if not check if need to adjust - * timeout value to wake up on the next state timeout. - */ - now = get_time(); - if (pd[port].timeout) { - if (now.val >= pd[port].timeout) { - set_state(port, pd[port].timeout_state); - /* On a state timeout, run next state soon */ - timeout = timeout < 10*MSEC ? timeout : 10*MSEC; - } else if (pd[port].timeout - now.val < timeout) { - timeout = pd[port].timeout - now.val; - } - } - -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER - /* Determine if we need to put the TCPC in low power mode */ - if (pd[port].flags & PD_FLAGS_LPM_REQUESTED && - !(pd[port].flags & PD_FLAGS_LPM_ENGAGED)) { - int64_t time_left; - - /* If any task prevents LPM, wait another debounce */ - if (pd[port].tasks_preventing_lpm) { - pd[port].low_power_time = - PD_LPM_DEBOUNCE_US + now.val; - } - - time_left = pd[port].low_power_time - now.val; - if (time_left <= 0) { - pd[port].flags |= PD_FLAGS_LPM_ENGAGED; - pd[port].flags |= PD_FLAGS_LPM_TRANSITION; - tcpm_enter_low_power_mode(port); - pd[port].flags &= ~PD_FLAGS_LPM_TRANSITION; - CPRINTS("TCPC p%d Enter Low Power Mode", port); - timeout = -1; - } else if (timeout < 0 || timeout > time_left) { - timeout = time_left; - } - } -#endif - - /* Check for disconnection if we're connected */ - if (!pd_is_connected(port)) - continue; -#ifdef CONFIG_USB_PD_DUAL_ROLE - if (pd_is_power_swapping(port)) - continue; -#endif - if (pd[port].power_role == PD_ROLE_SOURCE) { - /* Source: detect disconnect by monitoring CC */ - tcpm_get_cc(port, &cc1, &cc2); - if (pd[port].polarity) - cc1 = cc2; - if (cc1 == TYPEC_CC_VOLT_OPEN) { - set_state(port, PD_STATE_SRC_DISCONNECTED); - /* Debouncing */ - timeout = 10*MSEC; -#ifdef CONFIG_USB_PD_DUAL_ROLE - /* - * If Try.SRC is configured, then ATTACHED_SRC - * needs to transition to TryWait.SNK. Change - * power role to SNK and start state timer. - */ - if (pd_try_src_enable) { - /* Swap roles to sink */ - pd_set_power_role(port, PD_ROLE_SINK); - tcpm_set_cc(port, TYPEC_CC_RD); - /* Set timer for TryWait.SNK state */ - pd[port].try_src_marker = get_time().val - + PD_T_DEBOUNCE; - /* Advance to TryWait.SNK state */ - set_state(port, - PD_STATE_SNK_DISCONNECTED); - /* Mark state as TryWait.SNK */ - pd[port].flags |= PD_FLAGS_TRY_SRC; - } -#endif - } - } -#ifdef CONFIG_USB_PD_DUAL_ROLE - /* - * Sink disconnect if VBUS is low and - * 1) we are not waiting for VBUS to debounce after a power - * role swap. - * 2) we are not recovering from a hard reset. - */ - if (pd[port].power_role == PD_ROLE_SINK && - pd[port].vbus_debounce_time < get_time().val && - !pd_is_vbus_present(port) && - pd[port].task_state != PD_STATE_SNK_HARD_RESET_RECOVER && - pd[port].task_state != PD_STATE_HARD_RESET_EXECUTE) { - /* Sink: detect disconnect by monitoring VBUS */ - set_state(port, PD_STATE_SNK_DISCONNECTED); - /* set timeout small to reconnect fast */ - timeout = 5*MSEC; - } -#endif /* CONFIG_USB_PD_DUAL_ROLE */ - } -} - -#ifdef CONFIG_USB_PD_DUAL_ROLE -static void pd_chipset_resume(void) -{ - int i; - - for (i = 0; i < board_get_usb_pd_port_count(); i++) { -#ifdef CONFIG_CHARGE_MANAGER - if (charge_manager_get_active_charge_port() != i) -#endif - pd[i].flags |= PD_FLAGS_CHECK_PR_ROLE | - PD_FLAGS_CHECK_DR_ROLE; - pd_set_dual_role(i, PD_DRP_TOGGLE_ON); - } - - CPRINTS("PD:S3->S0"); -} -DECLARE_HOOK(HOOK_CHIPSET_RESUME, pd_chipset_resume, HOOK_PRIO_DEFAULT); - -static void pd_chipset_suspend(void) -{ - int i; - - for (i = 0; i < board_get_usb_pd_port_count(); i++) - pd_set_dual_role(i, PD_DRP_TOGGLE_OFF); - CPRINTS("PD:S0->S3"); -} -DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, pd_chipset_suspend, HOOK_PRIO_DEFAULT); - -static void pd_chipset_startup(void) -{ - int i; - - for (i = 0; i < board_get_usb_pd_port_count(); i++) { - pd_set_dual_role_no_wakeup(i, PD_DRP_TOGGLE_OFF); - pd[i].flags |= PD_FLAGS_CHECK_IDENTITY; - /* Reset cable attributes and flags */ - reset_pd_cable(i); - task_set_event(PD_PORT_TO_TASK_ID(i), - PD_EVENT_POWER_STATE_CHANGE | - PD_EVENT_UPDATE_DUAL_ROLE, - 0); - } - CPRINTS("PD:S5->S3"); -} -DECLARE_HOOK(HOOK_CHIPSET_STARTUP, pd_chipset_startup, HOOK_PRIO_DEFAULT); - -static void pd_chipset_shutdown(void) -{ - int i; - - for (i = 0; i < board_get_usb_pd_port_count(); i++) { - pd_set_dual_role_no_wakeup(i, PD_DRP_FORCE_SINK); - task_set_event(PD_PORT_TO_TASK_ID(i), - PD_EVENT_POWER_STATE_CHANGE | - PD_EVENT_UPDATE_DUAL_ROLE, - 0); - } - CPRINTS("PD:S3->S5"); -} -DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, pd_chipset_shutdown, HOOK_PRIO_DEFAULT); - -#endif /* CONFIG_USB_PD_DUAL_ROLE */ - -#ifdef CONFIG_USB_PD_ALT_MODE_DFP -void pd_prepare_sysjump(void) -{ - int i; - - /* Exit modes before sysjump so we can cleanly enter again later */ - for (i = 0; i < board_get_usb_pd_port_count(); i++) { - /* - * We can't be in an alternate mode if PD comm is disabled or - * the port is suspended, so no need to send the event - */ - if (!pd_comm_is_enabled(i) || - pd[i].task_state == PD_STATE_SUSPENDED) - continue; - - sysjump_task_waiting = task_get_current(); - task_set_event(PD_PORT_TO_TASK_ID(i), PD_EVENT_SYSJUMP, 0); - task_wait_event_mask(TASK_EVENT_SYSJUMP_READY, -1); - sysjump_task_waiting = TASK_ID_INVALID; - } -} -#endif - -#ifdef CONFIG_COMMON_RUNTIME - -static void pd_control_resume(int port) -{ - if (pd[port].task_state != PD_STATE_SUSPENDED) - return; - - set_state(port, PD_DEFAULT_STATE(port)); - /* - * Since we did not service interrupts while we were suspended, - * see if there is a waiting interrupt to be serviced. If the - * interrupt line isn't asserted, we won't communicate with the - * TCPC. - */ - if (IS_ENABLED(HAS_TASK_PD_INT_C0)) - schedule_deferred_pd_interrupt(port); - task_wake(PD_PORT_TO_TASK_ID(port)); -} - -/* - * (enable=1) request pd_task transition to the suspended state. hang - * around for a while until we observe the state change. this can - * take a while (like 300ms) on startup when pd_task is sleeping in - * tcpci_tcpm_init. - * - * (enable=0) force pd_task out of the suspended state and into the - * port's default state. - */ - -void pd_set_suspend(int port, int enable) -{ - int tries = 300; - - if (enable) { - pd[port].req_suspend_state = 1; - do { - task_wake(PD_PORT_TO_TASK_ID(port)); - if (pd[port].task_state == PD_STATE_SUSPENDED) - break; - msleep(1); - } while (--tries != 0); - if (!tries) - CPRINTS("TCPC p%d set_suspend failed!", port); - } else { - pd_control_resume(port); - } -} - -#ifdef CONFIG_USB_PD_TCPM_TCPCI -static uint32_t pd_ports_to_resume; -static void resume_pd_port(void) -{ - uint32_t port; - uint32_t suspended_ports = atomic_read_clear(&pd_ports_to_resume); - - while (suspended_ports) { - port = __builtin_ctz(suspended_ports); - suspended_ports &= ~BIT(port); - pd_set_suspend(port, 0); - } -} -DECLARE_DEFERRED(resume_pd_port); - -void pd_deferred_resume(int port) -{ - atomic_or(&pd_ports_to_resume, 1 << port); - hook_call_deferred(&resume_pd_port_data, 5 * SECOND); -} - -#endif /* CONFIG_USB_PD_DEFERRED_RESUME */ - -int pd_is_port_enabled(int port) -{ - switch (pd[port].task_state) { - case PD_STATE_DISABLED: - case PD_STATE_SUSPENDED: - return 0; - default: - return 1; - } -} - -#if defined(CONFIG_CMD_PD) && defined(CONFIG_CMD_PD_FLASH) -static int hex8tou32(char *str, uint32_t *val) -{ - char *ptr = str; - uint32_t tmp = 0; - - while (*ptr) { - char c = *ptr++; - if (c >= '0' && c <= '9') - tmp = (tmp << 4) + (c - '0'); - else if (c >= 'A' && c <= 'F') - tmp = (tmp << 4) + (c - 'A' + 10); - else if (c >= 'a' && c <= 'f') - tmp = (tmp << 4) + (c - 'a' + 10); - else - return EC_ERROR_INVAL; - } - if (ptr != str + 8) - return EC_ERROR_INVAL; - *val = tmp; - return EC_SUCCESS; -} - -static int remote_flashing(int argc, char **argv) -{ - int port, cnt, cmd; - uint32_t data[VDO_MAX_SIZE-1]; - char *e; - static int flash_offset[CONFIG_USB_PD_PORT_MAX_COUNT]; - - if (argc < 4 || argc > (VDO_MAX_SIZE + 4 - 1)) - return EC_ERROR_PARAM_COUNT; - - port = strtoi(argv[1], &e, 10); - if (*e || port >= board_get_usb_pd_port_count()) - return EC_ERROR_PARAM2; - - cnt = 0; - if (!strcasecmp(argv[3], "erase")) { - cmd = VDO_CMD_FLASH_ERASE; - flash_offset[port] = 0; - ccprintf("ERASE ..."); - } else if (!strcasecmp(argv[3], "reboot")) { - cmd = VDO_CMD_REBOOT; - ccprintf("REBOOT ..."); - } else if (!strcasecmp(argv[3], "signature")) { - cmd = VDO_CMD_ERASE_SIG; - ccprintf("ERASE SIG ..."); - } else if (!strcasecmp(argv[3], "info")) { - cmd = VDO_CMD_READ_INFO; - ccprintf("INFO..."); - } else if (!strcasecmp(argv[3], "version")) { - cmd = VDO_CMD_VERSION; - ccprintf("VERSION..."); - } else { - int i; - argc -= 3; - for (i = 0; i < argc; i++) - if (hex8tou32(argv[i+3], data + i)) - return EC_ERROR_INVAL; - cmd = VDO_CMD_FLASH_WRITE; - cnt = argc; - ccprintf("WRITE %d @%04x ...", argc * 4, - flash_offset[port]); - flash_offset[port] += argc * 4; - } - - pd_send_vdm(port, USB_VID_GOOGLE, cmd, data, cnt); - - /* Wait until VDM is done */ - while (pd[port].vdm_state > 0) - task_wait_event(100*MSEC); - - ccprintf("DONE %d\n", pd[port].vdm_state); - return EC_SUCCESS; -} -#endif /* defined(CONFIG_CMD_PD) && defined(CONFIG_CMD_PD_FLASH) */ - -#if defined(CONFIG_USB_PD_ALT_MODE) && !defined(CONFIG_USB_PD_ALT_MODE_DFP) -void pd_send_hpd(int port, enum hpd_event hpd) -{ - uint32_t data[1]; - int opos = pd_alt_mode(port, USB_SID_DISPLAYPORT); - if (!opos) - return; - - data[0] = VDO_DP_STATUS((hpd == hpd_irq), /* IRQ_HPD */ - (hpd != hpd_low), /* HPD_HI|LOW */ - 0, /* request exit DP */ - 0, /* request exit USB */ - 0, /* MF pref */ - 1, /* enabled */ - 0, /* power low */ - 0x2); - pd_send_vdm(port, USB_SID_DISPLAYPORT, - VDO_OPOS(opos) | CMD_ATTENTION, data, 1); - /* Wait until VDM is done. */ - while (pd[0].vdm_state > 0) - task_wait_event(USB_PD_RX_TMOUT_US * (PD_RETRY_COUNT + 1)); -} -#endif - -int pd_fetch_acc_log_entry(int port) -{ - timestamp_t timeout; - - /* Cannot send a VDM now, the host should retry */ - if (pd[port].vdm_state > 0) - return pd[port].vdm_state == VDM_STATE_BUSY ? - EC_RES_BUSY : EC_RES_UNAVAILABLE; - - pd_send_vdm(port, USB_VID_GOOGLE, VDO_CMD_GET_LOG, NULL, 0); - timeout.val = get_time().val + 75*MSEC; - - /* Wait until VDM is done */ - while ((pd[port].vdm_state > 0) && - (get_time().val < timeout.val)) - task_wait_event(10*MSEC); - - if (pd[port].vdm_state > 0) - return EC_RES_TIMEOUT; - else if (pd[port].vdm_state < 0) - return EC_RES_ERROR; - - return EC_RES_SUCCESS; -} - -#ifdef CONFIG_USB_PD_DUAL_ROLE -void pd_request_source_voltage(int port, int mv) -{ - pd_set_max_voltage(mv); - - if (pd[port].task_state == PD_STATE_SNK_READY || - pd[port].task_state == PD_STATE_SNK_TRANSITION) { - /* Set flag to send new power request in pd_task */ - pd[port].new_power_request = 1; - } else { - pd_set_power_role(port, PD_ROLE_SINK); - tcpm_set_cc(port, TYPEC_CC_RD); - set_state(port, PD_STATE_SNK_DISCONNECTED); - } - - task_wake(PD_PORT_TO_TASK_ID(port)); -} - -void pd_set_external_voltage_limit(int port, int mv) -{ - pd_set_max_voltage(mv); - - if (pd[port].task_state == PD_STATE_SNK_READY || - pd[port].task_state == PD_STATE_SNK_TRANSITION) { - /* Set flag to send new power request in pd_task */ - pd[port].new_power_request = 1; - task_wake(PD_PORT_TO_TASK_ID(port)); - } -} - -void pd_update_contract(int port) -{ - if ((pd[port].task_state >= PD_STATE_SRC_NEGOCIATE) && - (pd[port].task_state <= PD_STATE_SRC_GET_SINK_CAP)) { - pd[port].flags |= PD_FLAGS_UPDATE_SRC_CAPS; - task_wake(PD_PORT_TO_TASK_ID(port)); - } -} - -#endif /* CONFIG_USB_PD_DUAL_ROLE */ - -#ifdef CONFIG_USBC_PPC -static void pd_send_hard_reset(int port) -{ - task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SEND_HARD_RESET, 0); -} - -static uint32_t port_oc_reset_req; - -static void re_enable_ports(void) -{ - uint32_t ports = atomic_read_clear(&port_oc_reset_req); - - while (ports) { - int port = __fls(ports); - - ports &= ~BIT(port); - - /* - * Let the board know that the overcurrent is - * over since we're going to attempt re-enabling - * the port. - */ - board_overcurrent_event(port, 0); - - pd_send_hard_reset(port); - /* - * TODO(b/117854867): PD3.0 to send an alert message - * indicating OCP after explicit contract. - */ - } -} -DECLARE_DEFERRED(re_enable_ports); - -void pd_handle_overcurrent(int port) -{ - /* Keep track of the overcurrent events. */ - CPRINTS("C%d: overcurrent!", port); -#ifdef CONFIG_USB_PD_LOGGING - pd_log_event(PD_EVENT_PS_FAULT, PD_LOG_PORT_SIZE(port, 0), PS_FAULT_OCP, - NULL); -#endif /* defined(CONFIG_USB_PD_LOGGING) */ - ppc_add_oc_event(port); - /* Let the board specific code know about the OC event. */ - board_overcurrent_event(port, 1); - - /* Wait 1s before trying to re-enable the port. */ - atomic_or(&port_oc_reset_req, BIT(port)); - hook_call_deferred(&re_enable_ports_data, SECOND); -} -#endif /* defined(CONFIG_USBC_PPC) */ - -static int command_pd(int argc, char **argv) -{ - int port; - char *e; - - if (argc < 2) - return EC_ERROR_PARAM_COUNT; - - if (!strcasecmp(argv[1], "dump")) { - if (argc >= 3) { -#ifdef CONFIG_USB_PD_DEBUG_LEVEL - return EC_ERROR_PARAM2; -#else - int level = strtoi(argv[2], &e, 10); - if (*e) - return EC_ERROR_PARAM2; - debug_level = level; -#endif - } - ccprintf("debug=%d\n", debug_level); - - return EC_SUCCESS; - } - -#ifdef CONFIG_CMD_PD -#ifdef CONFIG_CMD_PD_DEV_DUMP_INFO - else if (!strncasecmp(argv[1], "rwhashtable", 3)) { - int i; - struct ec_params_usb_pd_rw_hash_entry *p; - for (i = 0; i < RW_HASH_ENTRIES; i++) { - p = &rw_hash_table[i]; - pd_dev_dump_info(p->dev_id, p->dev_rw_hash); - } - return EC_SUCCESS; - } -#endif /* CONFIG_CMD_PD_DEV_DUMP_INFO */ -#ifdef CONFIG_USB_PD_TRY_SRC - else if (!strncasecmp(argv[1], "trysrc", 6)) { - int enable; - - if (argc >= 3) { - enable = strtoi(argv[2], &e, 10); - if (*e) - return EC_ERROR_PARAM3; - pd_try_src_enable = enable ? 1 : 0; - } - - ccprintf("Try.SRC %s\n", pd_try_src_enable ? "on" : "off"); - return EC_SUCCESS; - } -#endif -#endif - /* command: pd <port> <subcmd> [args] */ - port = strtoi(argv[1], &e, 10); - if (argc < 3) - return EC_ERROR_PARAM_COUNT; - if (*e || port >= board_get_usb_pd_port_count()) - return EC_ERROR_PARAM2; -#if defined(CONFIG_CMD_PD) && defined(CONFIG_USB_PD_DUAL_ROLE) - - if (!strcasecmp(argv[2], "tx")) { - set_state(port, PD_STATE_SNK_DISCOVERY); - task_wake(PD_PORT_TO_TASK_ID(port)); - } else if (!strcasecmp(argv[2], "bist_rx")) { - set_state(port, PD_STATE_BIST_RX); - task_wake(PD_PORT_TO_TASK_ID(port)); - } else if (!strcasecmp(argv[2], "bist_tx")) { - if (*e) - return EC_ERROR_PARAM3; - set_state(port, PD_STATE_BIST_TX); - task_wake(PD_PORT_TO_TASK_ID(port)); - } else if (!strcasecmp(argv[2], "charger")) { - pd_set_power_role(port, PD_ROLE_SOURCE); - tcpm_set_cc(port, TYPEC_CC_RP); - set_state(port, PD_STATE_SRC_DISCONNECTED); - task_wake(PD_PORT_TO_TASK_ID(port)); - } else if (!strncasecmp(argv[2], "dev", 3)) { - int max_volt; - if (argc >= 4) - max_volt = strtoi(argv[3], &e, 10) * 1000; - else - max_volt = pd_get_max_voltage(); - - pd_request_source_voltage(port, max_volt); - ccprintf("max req: %dmV\n", max_volt); - } else if (!strcasecmp(argv[2], "disable")) { - pd_comm_enable(port, 0); - ccprintf("Port C%d disable\n", port); - return EC_SUCCESS; - } else if (!strcasecmp(argv[2], "enable")) { - pd_comm_enable(port, 1); - ccprintf("Port C%d enabled\n", port); - return EC_SUCCESS; - } else if (!strncasecmp(argv[2], "hard", 4)) { - set_state(port, PD_STATE_HARD_RESET_SEND); - task_wake(PD_PORT_TO_TASK_ID(port)); - } else if (!strncasecmp(argv[2], "info", 4)) { - int i; - ccprintf("Hash "); - for (i = 0; i < PD_RW_HASH_SIZE / 4; i++) - ccprintf("%08x ", pd[port].dev_rw_hash[i]); - ccprintf("\nImage %s\n", system_image_copy_t_to_string( - (enum system_image_copy_t)pd[port].current_image)); - } else if (!strncasecmp(argv[2], "soft", 4)) { - set_state(port, PD_STATE_SOFT_RESET); - task_wake(PD_PORT_TO_TASK_ID(port)); - } else if (!strncasecmp(argv[2], "swap", 4)) { - if (argc < 4) - return EC_ERROR_PARAM_COUNT; - - if (!strncasecmp(argv[3], "power", 5)) - pd_request_power_swap(port); - else if (!strncasecmp(argv[3], "data", 4)) - pd_request_data_swap(port); -#ifdef CONFIG_USBC_VCONN_SWAP - else if (!strncasecmp(argv[3], "vconn", 5)) - pd_request_vconn_swap(port); -#endif - else - return EC_ERROR_PARAM3; - } else if (!strncasecmp(argv[2], "ping", 4)) { - int enable; - - if (argc > 3) { - enable = strtoi(argv[3], &e, 10); - if (*e) - return EC_ERROR_PARAM3; - pd_ping_enable(port, enable); - } - - ccprintf("Pings %s\n", - (pd[port].flags & PD_FLAGS_PING_ENABLED) ? - "on" : "off"); - } else if (!strncasecmp(argv[2], "vdm", 3)) { - if (argc < 4) - return EC_ERROR_PARAM_COUNT; - - if (!strncasecmp(argv[3], "ping", 4)) { - uint32_t enable; - if (argc < 5) - return EC_ERROR_PARAM_COUNT; - enable = strtoi(argv[4], &e, 10); - if (*e) - return EC_ERROR_PARAM4; - pd_send_vdm(port, USB_VID_GOOGLE, VDO_CMD_PING_ENABLE, - &enable, 1); - } else if (!strncasecmp(argv[3], "curr", 4)) { - pd_send_vdm(port, USB_VID_GOOGLE, VDO_CMD_CURRENT, - NULL, 0); - } else if (!strncasecmp(argv[3], "vers", 4)) { - pd_send_vdm(port, USB_VID_GOOGLE, VDO_CMD_VERSION, - NULL, 0); - } else { - return EC_ERROR_PARAM_COUNT; - } -#if defined(CONFIG_CMD_PD) && defined(CONFIG_CMD_PD_FLASH) - } else if (!strncasecmp(argv[2], "flash", 4)) { - return remote_flashing(argc, argv); -#endif -#if defined(CONFIG_CMD_PD) && defined(CONFIG_USB_PD_DUAL_ROLE) - } else if (!strcasecmp(argv[2], "dualrole")) { - if (argc < 4) { - ccprintf("dual-role toggling: "); - switch (drp_state[port]) { - case PD_DRP_TOGGLE_ON: - ccprintf("on\n"); - break; - case PD_DRP_TOGGLE_OFF: - ccprintf("off\n"); - break; - case PD_DRP_FREEZE: - ccprintf("freeze\n"); - break; - case PD_DRP_FORCE_SINK: - ccprintf("force sink\n"); - break; - case PD_DRP_FORCE_SOURCE: - ccprintf("force source\n"); - break; - } - } else { - if (!strcasecmp(argv[3], "on")) - pd_set_dual_role(port, PD_DRP_TOGGLE_ON); - else if (!strcasecmp(argv[3], "off")) - pd_set_dual_role(port, PD_DRP_TOGGLE_OFF); - else if (!strcasecmp(argv[3], "freeze")) - pd_set_dual_role(port, PD_DRP_FREEZE); - else if (!strcasecmp(argv[3], "sink")) - pd_set_dual_role(port, PD_DRP_FORCE_SINK); - else if (!strcasecmp(argv[3], "source")) - pd_set_dual_role(port, - PD_DRP_FORCE_SOURCE); - else - return EC_ERROR_PARAM4; - } - return EC_SUCCESS; -#endif - } else -#endif - if (!strncasecmp(argv[2], "state", 5)) { - ccprintf("Port C%d CC%d, %s - Role: %s-%s%s " - "State: %d(%s), Flags: 0x%04x\n", - port, pd[port].polarity + 1, - pd_comm_is_enabled(port) ? "Ena" : "Dis", - pd[port].power_role == PD_ROLE_SOURCE ? "SRC" : "SNK", - pd[port].data_role == PD_ROLE_DFP ? "DFP" : "UFP", - (pd[port].flags & PD_FLAGS_VCONN_ON) ? "-VC" : "", - pd[port].task_state, - debug_level > 0 ? - pd_state_names[pd[port].task_state] : "", - pd[port].flags); - } else { - return EC_ERROR_PARAM1; - } - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(pd, command_pd, - "dump" -#ifdef CONFIG_USB_PD_TRY_SRC - "|trysrc" -#endif - " [0|1|2]" -#ifdef CONFIG_CMD_PD_DEV_DUMP_INFO - "|rwhashtable" -#endif - "\n\t<port> state" -#ifdef CONFIG_USB_PD_DUAL_ROLE - "|tx|bist_rx|bist_tx|charger|dev" - "\n\t<port> disable|enable|soft|info|hard|ping" - "\n\t<port> dualrole [on|off|freeze|sink|source]" - "\n\t<port> swap [power|data|vconn]" - "\n\t<port> vdm [ping|curr|vers]" -#ifdef CONFIG_CMD_PD_FLASH - "\n\t<port> flash [erase|reboot|signature|info|version]" -#endif /* CONFIG_CMD_PD_FLASH */ -#endif /* CONFIG_USB_PD_DUAL_ROLE */ - , - "USB PD"); - -#ifdef HAS_TASK_HOSTCMD - -static enum ec_status hc_pd_ports(struct host_cmd_handler_args *args) -{ - struct ec_response_usb_pd_ports *r = args->response; - r->num_ports = board_get_usb_pd_port_count(); - - args->response_size = sizeof(*r); - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_USB_PD_PORTS, - hc_pd_ports, - EC_VER_MASK(0)); - -#ifdef CONFIG_USB_PD_DUAL_ROLE -static const enum pd_dual_role_states dual_role_map[USB_PD_CTRL_ROLE_COUNT] = { - [USB_PD_CTRL_ROLE_TOGGLE_ON] = PD_DRP_TOGGLE_ON, - [USB_PD_CTRL_ROLE_TOGGLE_OFF] = PD_DRP_TOGGLE_OFF, - [USB_PD_CTRL_ROLE_FORCE_SINK] = PD_DRP_FORCE_SINK, - [USB_PD_CTRL_ROLE_FORCE_SOURCE] = PD_DRP_FORCE_SOURCE, - [USB_PD_CTRL_ROLE_FREEZE] = PD_DRP_FREEZE, -}; -#endif - -#ifdef CONFIG_USBC_SS_MUX -static const enum typec_mux typec_mux_map[USB_PD_CTRL_MUX_COUNT] = { - [USB_PD_CTRL_MUX_NONE] = TYPEC_MUX_NONE, - [USB_PD_CTRL_MUX_USB] = TYPEC_MUX_USB, - [USB_PD_CTRL_MUX_AUTO] = TYPEC_MUX_DP, - [USB_PD_CTRL_MUX_DP] = TYPEC_MUX_DP, - [USB_PD_CTRL_MUX_DOCK] = TYPEC_MUX_DOCK, -}; -#endif - -__attribute__((weak)) uint8_t board_get_dp_pin_mode(int port) -{ - return 0; -} - -static enum ec_status hc_usb_pd_control(struct host_cmd_handler_args *args) -{ - const struct ec_params_usb_pd_control *p = args->params; - struct ec_response_usb_pd_control_v2 *r_v2 = args->response; - struct ec_response_usb_pd_control_v1 *r_v1 = args->response; - struct ec_response_usb_pd_control *r = args->response; - - if (p->port >= board_get_usb_pd_port_count()) - return EC_RES_INVALID_PARAM; - - if (p->role >= USB_PD_CTRL_ROLE_COUNT || - p->mux >= USB_PD_CTRL_MUX_COUNT) - return EC_RES_INVALID_PARAM; - - if (p->role != USB_PD_CTRL_ROLE_NO_CHANGE) -#ifdef CONFIG_USB_PD_DUAL_ROLE - pd_set_dual_role(p->port, dual_role_map[p->role]); -#else - return EC_RES_INVALID_PARAM; -#endif - -#ifdef CONFIG_USBC_SS_MUX - if (p->mux != USB_PD_CTRL_MUX_NO_CHANGE) - usb_mux_set(p->port, typec_mux_map[p->mux], - typec_mux_map[p->mux] == TYPEC_MUX_NONE ? - USB_SWITCH_DISCONNECT : - USB_SWITCH_CONNECT, - pd_get_polarity(p->port)); -#endif /* CONFIG_USBC_SS_MUX */ - - if (p->swap == USB_PD_CTRL_SWAP_DATA) - pd_request_data_swap(p->port); -#ifdef CONFIG_USB_PD_DUAL_ROLE - else if (p->swap == USB_PD_CTRL_SWAP_POWER) - pd_request_power_swap(p->port); -#ifdef CONFIG_USBC_VCONN_SWAP - else if (p->swap == USB_PD_CTRL_SWAP_VCONN) - pd_request_vconn_swap(p->port); -#endif -#endif - - switch (args->version) { - case 0: - r->enabled = pd_comm_is_enabled(p->port); - r->role = pd[p->port].power_role; - r->polarity = pd[p->port].polarity; - r->state = pd[p->port].task_state; - args->response_size = sizeof(*r); - break; - case 1: - case 2: - r_v2->enabled = - (pd_comm_is_enabled(p->port) ? - PD_CTRL_RESP_ENABLED_COMMS : 0) | - (pd_is_connected(p->port) ? - PD_CTRL_RESP_ENABLED_CONNECTED : 0) | - ((pd[p->port].flags & PD_FLAGS_PREVIOUS_PD_CONN) ? - PD_CTRL_RESP_ENABLED_PD_CAPABLE : 0); - r_v2->role = - (pd[p->port].power_role ? PD_CTRL_RESP_ROLE_POWER : 0) | - (pd[p->port].data_role ? PD_CTRL_RESP_ROLE_DATA : 0) | - ((pd[p->port].flags & PD_FLAGS_VCONN_ON) ? - PD_CTRL_RESP_ROLE_VCONN : 0) | - ((pd[p->port].flags & PD_FLAGS_PARTNER_DR_POWER) ? - PD_CTRL_RESP_ROLE_DR_POWER : 0) | - ((pd[p->port].flags & PD_FLAGS_PARTNER_DR_DATA) ? - PD_CTRL_RESP_ROLE_DR_DATA : 0) | - ((pd[p->port].flags & PD_FLAGS_PARTNER_USB_COMM) ? - PD_CTRL_RESP_ROLE_USB_COMM : 0) | - ((pd[p->port].flags & PD_FLAGS_PARTNER_EXTPOWER) ? - PD_CTRL_RESP_ROLE_EXT_POWERED : 0); - r_v2->polarity = pd[p->port].polarity; - - if (debug_level > 0) - strzcpy(r_v2->state, - pd_state_names[pd[p->port].task_state], - sizeof(r_v2->state)); - else - r_v2->state[0] = '\0'; - - r_v2->cc_state = pd[p->port].cc_state; - r_v2->dp_mode = board_get_dp_pin_mode(p->port); - r_v2->cable_type = get_usb_pd_mux_cable_type(p->port); - - if (args->version == 1) - args->response_size = sizeof(*r_v1); - else - args->response_size = sizeof(*r_v2); - break; - default: - return EC_RES_INVALID_PARAM; - } - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_USB_PD_CONTROL, - hc_usb_pd_control, - EC_VER_MASK(0) | EC_VER_MASK(1) | EC_VER_MASK(2)); - -#ifdef CONFIG_HOSTCMD_FLASHPD -static enum ec_status hc_remote_flash(struct host_cmd_handler_args *args) -{ - const struct ec_params_usb_pd_fw_update *p = args->params; - int port = p->port; - const uint32_t *data = &(p->size) + 1; - int i, size, rv = EC_RES_SUCCESS; - timestamp_t timeout; - - if (port >= board_get_usb_pd_port_count()) - return EC_RES_INVALID_PARAM; - - if (p->size + sizeof(*p) > args->params_size) - return EC_RES_INVALID_PARAM; - -#if defined(CONFIG_BATTERY_PRESENT_CUSTOM) || \ - defined(CONFIG_BATTERY_PRESENT_GPIO) - /* - * Do not allow PD firmware update if no battery and this port - * is sinking power, because we will lose power. - */ - if (battery_is_present() != BP_YES && - charge_manager_get_active_charge_port() == port) - return EC_RES_UNAVAILABLE; -#endif - - /* - * Busy still with a VDM that host likely generated. 1 deep VDM queue - * so just return for retry logic on host side to deal with. - */ - if (pd[port].vdm_state > 0) - return EC_RES_BUSY; - - switch (p->cmd) { - case USB_PD_FW_REBOOT: - pd_send_vdm(port, USB_VID_GOOGLE, VDO_CMD_REBOOT, NULL, 0); - - /* - * Return immediately to free pending i2c bus. Host needs to - * manage this delay. - */ - return EC_RES_SUCCESS; - - case USB_PD_FW_FLASH_ERASE: - pd_send_vdm(port, USB_VID_GOOGLE, VDO_CMD_FLASH_ERASE, NULL, 0); - - /* - * Return immediately. Host needs to manage delays here which - * can be as long as 1.2 seconds on 64KB RW flash. - */ - return EC_RES_SUCCESS; - - case USB_PD_FW_ERASE_SIG: - pd_send_vdm(port, USB_VID_GOOGLE, VDO_CMD_ERASE_SIG, NULL, 0); - timeout.val = get_time().val + 500*MSEC; - break; - - case USB_PD_FW_FLASH_WRITE: - /* Data size must be a multiple of 4 */ - if (!p->size || p->size % 4) - return EC_RES_INVALID_PARAM; - - size = p->size / 4; - for (i = 0; i < size; i += VDO_MAX_SIZE - 1) { - pd_send_vdm(port, USB_VID_GOOGLE, VDO_CMD_FLASH_WRITE, - data + i, MIN(size - i, VDO_MAX_SIZE - 1)); - timeout.val = get_time().val + 500*MSEC; - - /* Wait until VDM is done */ - while ((pd[port].vdm_state > 0) && - (get_time().val < timeout.val)) - task_wait_event(10*MSEC); - - if (pd[port].vdm_state > 0) - return EC_RES_TIMEOUT; - } - return EC_RES_SUCCESS; - - default: - return EC_RES_INVALID_PARAM; - break; - } - - /* Wait until VDM is done or timeout */ - while ((pd[port].vdm_state > 0) && (get_time().val < timeout.val)) - task_wait_event(50*MSEC); - - if ((pd[port].vdm_state > 0) || - (pd[port].vdm_state == VDM_STATE_ERR_TMOUT)) - rv = EC_RES_TIMEOUT; - else if (pd[port].vdm_state < 0) - rv = EC_RES_ERROR; - - return rv; -} -DECLARE_HOST_COMMAND(EC_CMD_USB_PD_FW_UPDATE, - hc_remote_flash, - EC_VER_MASK(0)); -#endif /* CONFIG_HOSTCMD_FLASHPD */ - -#ifdef CONFIG_HOSTCMD_RWHASHPD -static enum ec_status -hc_remote_rw_hash_entry(struct host_cmd_handler_args *args) -{ - int i, idx = 0, found = 0; - const struct ec_params_usb_pd_rw_hash_entry *p = args->params; - static int rw_hash_next_idx; - - if (!p->dev_id) - return EC_RES_INVALID_PARAM; - - for (i = 0; i < RW_HASH_ENTRIES; i++) { - if (p->dev_id == rw_hash_table[i].dev_id) { - idx = i; - found = 1; - break; - } - } - if (!found) { - idx = rw_hash_next_idx; - rw_hash_next_idx = rw_hash_next_idx + 1; - if (rw_hash_next_idx == RW_HASH_ENTRIES) - rw_hash_next_idx = 0; - } - memcpy(&rw_hash_table[idx], p, sizeof(*p)); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_USB_PD_RW_HASH_ENTRY, - hc_remote_rw_hash_entry, - EC_VER_MASK(0)); -#endif /* CONFIG_HOSTCMD_RWHASHPD */ - -static enum ec_status hc_remote_pd_dev_info(struct host_cmd_handler_args *args) -{ - const uint8_t *port = args->params; - struct ec_params_usb_pd_rw_hash_entry *r = args->response; - - if (*port >= board_get_usb_pd_port_count()) - return EC_RES_INVALID_PARAM; - - r->dev_id = pd[*port].dev_id; - - if (r->dev_id) { - memcpy(r->dev_rw_hash, pd[*port].dev_rw_hash, - PD_RW_HASH_SIZE); - } - - r->current_image = pd[*port].current_image; - - args->response_size = sizeof(*r); - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_USB_PD_DEV_INFO, - hc_remote_pd_dev_info, - EC_VER_MASK(0)); - -#ifndef CONFIG_USB_PD_TCPC -#ifdef CONFIG_EC_CMD_PD_CHIP_INFO -static enum ec_status hc_remote_pd_chip_info(struct host_cmd_handler_args *args) -{ - const struct ec_params_pd_chip_info *p = args->params; - struct ec_response_pd_chip_info_v1 *info; - - if (p->port >= board_get_usb_pd_port_count()) - return EC_RES_INVALID_PARAM; - - if (tcpm_get_chip_info(p->port, p->live, &info)) - return EC_RES_ERROR; - - /* - * Take advantage of the fact that v0 and v1 structs have the - * same layout for v0 data. (v1 just appends data) - */ - args->response_size = - args->version ? sizeof(struct ec_response_pd_chip_info_v1) - : sizeof(struct ec_response_pd_chip_info); - - memcpy(args->response, info, args->response_size); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_PD_CHIP_INFO, - hc_remote_pd_chip_info, - EC_VER_MASK(0) | EC_VER_MASK(1)); -#endif -#endif - -#ifdef CONFIG_USB_PD_ALT_MODE_DFP -static enum ec_status hc_remote_pd_set_amode(struct host_cmd_handler_args *args) -{ - const struct ec_params_usb_pd_set_mode_request *p = args->params; - - if ((p->port >= board_get_usb_pd_port_count()) || - (!p->svid) || (!p->opos)) - return EC_RES_INVALID_PARAM; - - switch (p->cmd) { - case PD_EXIT_MODE: - if (pd_dfp_exit_mode(p->port, p->svid, p->opos)) - pd_send_vdm(p->port, p->svid, - CMD_EXIT_MODE | VDO_OPOS(p->opos), NULL, 0); - else { - CPRINTF("Failed exit mode\n"); - return EC_RES_ERROR; - } - break; - case PD_ENTER_MODE: - if (pd_dfp_enter_mode(p->port, p->svid, p->opos)) - pd_send_vdm(p->port, p->svid, CMD_ENTER_MODE | - VDO_OPOS(p->opos), NULL, 0); - break; - default: - return EC_RES_INVALID_PARAM; - } - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_USB_PD_SET_AMODE, - hc_remote_pd_set_amode, - EC_VER_MASK(0)); -#endif /* CONFIG_USB_PD_ALT_MODE_DFP */ - -#endif /* HAS_TASK_HOSTCMD */ - -#ifdef CONFIG_CMD_PD_CONTROL - -static enum ec_status pd_control(struct host_cmd_handler_args *args) -{ - static int pd_control_disabled[CONFIG_USB_PD_PORT_MAX_COUNT]; - const struct ec_params_pd_control *cmd = args->params; - int enable = 0; - - if (cmd->chip >= board_get_usb_pd_port_count()) - return EC_RES_INVALID_PARAM; - - /* Always allow disable command */ - if (cmd->subcmd == PD_CONTROL_DISABLE) { - pd_control_disabled[cmd->chip] = 1; - return EC_RES_SUCCESS; - } - - if (pd_control_disabled[cmd->chip]) - return EC_RES_ACCESS_DENIED; - - if (cmd->subcmd == PD_SUSPEND) { - /* - * The AP is requesting to suspend PD traffic on the EC so it - * can perform a firmware upgrade. If Vbus is present on the - * connector (it is either a source or sink), then we will - * prevent the upgrade if there is not enough battery to finish - * the upgrade. We cannot rely on the EC's active charger data - * as the EC just rebooted into RW and has not necessarily - * picked the active charger yet. - */ -#ifdef HAS_TASK_CHARGER - if (pd_is_vbus_present(cmd->chip)) { - struct batt_params batt = { 0 }; - /* - * The charger task has not re-initialized, so we need - * to ask the battery directly. - */ - battery_get_params(&batt); - if (batt.remaining_capacity < - MIN_BATTERY_FOR_TCPC_UPGRADE_MAH || - batt.flags & BATT_FLAG_BAD_REMAINING_CAPACITY) { - CPRINTS("C%d: Cannot suspend for upgrade, not " - "enough battery (%dmAh)!", - cmd->chip, batt.remaining_capacity); - return EC_RES_BUSY; - } - } -#else - if (pd_is_vbus_present(cmd->chip)) { - CPRINTS("C%d: Cannot suspend for upgrade, Vbus " - "present!", - cmd->chip); - return EC_RES_BUSY; - } -#endif - enable = 0; - } else if (cmd->subcmd == PD_RESUME) { - enable = 1; - } else if (cmd->subcmd == PD_RESET) { -#ifdef HAS_TASK_PDCMD - board_reset_pd_mcu(); -#else - return EC_RES_INVALID_COMMAND; -#endif - } else if (cmd->subcmd == PD_CHIP_ON && board_set_tcpc_power_mode) { - board_set_tcpc_power_mode(cmd->chip, 1); - return EC_RES_SUCCESS; - } else { - return EC_RES_INVALID_COMMAND; - } - - pd_comm_enable(cmd->chip, enable); - pd_set_suspend(cmd->chip, !enable); - - return EC_RES_SUCCESS; -} - -DECLARE_HOST_COMMAND(EC_CMD_PD_CONTROL, pd_control, EC_VER_MASK(0)); -#endif /* CONFIG_CMD_PD_CONTROL */ - -#endif /* CONFIG_COMMON_RUNTIME */ diff --git a/common/usb_pd_tcpc.c b/common/usb_pd_tcpc.c deleted file mode 100644 index 2e1cbf73d7..0000000000 --- a/common/usb_pd_tcpc.c +++ /dev/null @@ -1,1463 +0,0 @@ -/* 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 "adc.h" -#include "common.h" -#include "config.h" -#include "console.h" -#include "crc.h" -#include "ec_commands.h" -#include "gpio.h" -#include "hooks.h" -#include "host_command.h" -#include "registers.h" -#include "system.h" -#include "task.h" -#include "tcpci.h" -#include "tcpm.h" -#include "timer.h" -#include "util.h" -#include "usb_pd.h" -#include "usb_pd_config.h" -#include "usb_pd_tcpm.h" - -#ifdef CONFIG_COMMON_RUNTIME -#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) -#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) - -/* - * Debug log level - higher number == more log - * Level 0: Log state transitions - * Level 1: Level 0, plus packet info - * Level 2: Level 1, plus ping packet and packet dump on error - * - * Note that higher log level causes timing changes and thus may affect - * performance. - */ -static int debug_level; - -static struct mutex pd_crc_lock; -#else -#define CPRINTF(format, args...) -static const int debug_level; -#endif - -/* Encode 5 bits using Biphase Mark Coding */ -#define BMC(x) ((x & 1 ? 0x001 : 0x3FF) \ - ^ (x & 2 ? 0x004 : 0x3FC) \ - ^ (x & 4 ? 0x010 : 0x3F0) \ - ^ (x & 8 ? 0x040 : 0x3C0) \ - ^ (x & 16 ? 0x100 : 0x300)) - -/* 4b/5b + Bimark Phase encoding */ -static const uint16_t bmc4b5b[] = { -/* 0 = 0000 */ BMC(0x1E) /* 11110 */, -/* 1 = 0001 */ BMC(0x09) /* 01001 */, -/* 2 = 0010 */ BMC(0x14) /* 10100 */, -/* 3 = 0011 */ BMC(0x15) /* 10101 */, -/* 4 = 0100 */ BMC(0x0A) /* 01010 */, -/* 5 = 0101 */ BMC(0x0B) /* 01011 */, -/* 6 = 0110 */ BMC(0x0E) /* 01110 */, -/* 7 = 0111 */ BMC(0x0F) /* 01111 */, -/* 8 = 1000 */ BMC(0x12) /* 10010 */, -/* 9 = 1001 */ BMC(0x13) /* 10011 */, -/* A = 1010 */ BMC(0x16) /* 10110 */, -/* B = 1011 */ BMC(0x17) /* 10111 */, -/* C = 1100 */ BMC(0x1A) /* 11010 */, -/* D = 1101 */ BMC(0x1B) /* 11011 */, -/* E = 1110 */ BMC(0x1C) /* 11100 */, -/* F = 1111 */ BMC(0x1D) /* 11101 */, -/* Sync-1 K-code 11000 Startsynch #1 */ -/* Sync-2 K-code 10001 Startsynch #2 */ -/* RST-1 K-code 00111 Hard Reset #1 */ -/* RST-2 K-code 11001 Hard Reset #2 */ -/* EOP K-code 01101 EOP End Of Packet */ -/* Reserved Error 00000 */ -/* Reserved Error 00001 */ -/* Reserved Error 00010 */ -/* Reserved Error 00011 */ -/* Reserved Error 00100 */ -/* Reserved Error 00101 */ -/* Reserved Error 00110 */ -/* Reserved Error 01000 */ -/* Reserved Error 01100 */ -/* Reserved Error 10000 */ -/* Reserved Error 11111 */ -}; - -static const uint8_t dec4b5b[] = { -/* Error */ 0x10 /* 00000 */, -/* Error */ 0x10 /* 00001 */, -/* Error */ 0x10 /* 00010 */, -/* Error */ 0x10 /* 00011 */, -/* Error */ 0x10 /* 00100 */, -/* Error */ 0x10 /* 00101 */, -/* Error */ 0x10 /* 00110 */, -/* RST-1 */ 0x13 /* 00111 K-code: Hard Reset #1 */, -/* Error */ 0x10 /* 01000 */, -/* 1 = 0001 */ 0x01 /* 01001 */, -/* 4 = 0100 */ 0x04 /* 01010 */, -/* 5 = 0101 */ 0x05 /* 01011 */, -/* Error */ 0x10 /* 01100 */, -/* EOP */ 0x15 /* 01101 K-code: EOP End Of Packet */, -/* 6 = 0110 */ 0x06 /* 01110 */, -/* 7 = 0111 */ 0x07 /* 01111 */, -/* Error */ 0x10 /* 10000 */, -/* Sync-2 */ 0x12 /* 10001 K-code: Startsynch #2 */, -/* 8 = 1000 */ 0x08 /* 10010 */, -/* 9 = 1001 */ 0x09 /* 10011 */, -/* 2 = 0010 */ 0x02 /* 10100 */, -/* 3 = 0011 */ 0x03 /* 10101 */, -/* A = 1010 */ 0x0A /* 10110 */, -/* B = 1011 */ 0x0B /* 10111 */, -/* Sync-1 */ 0x11 /* 11000 K-code: Startsynch #1 */, -/* RST-2 */ 0x14 /* 11001 K-code: Hard Reset #2 */, -/* C = 1100 */ 0x0C /* 11010 */, -/* D = 1101 */ 0x0D /* 11011 */, -/* E = 1110 */ 0x0E /* 11100 */, -/* F = 1111 */ 0x0F /* 11101 */, -/* 0 = 0000 */ 0x00 /* 11110 */, -/* Error */ 0x10 /* 11111 */, -}; - -/* Start of Packet sequence : three Sync-1 K-codes, then one Sync-2 K-code */ -#define PD_SOP (PD_SYNC1 | (PD_SYNC1<<5) | (PD_SYNC1<<10) | (PD_SYNC2<<15)) -#define PD_SOP_PRIME (PD_SYNC1 | (PD_SYNC1<<5) | \ - (PD_SYNC3<<10) | (PD_SYNC3<<15)) -#define PD_SOP_PRIME_PRIME (PD_SYNC1 | (PD_SYNC3<<5) | \ - (PD_SYNC1<<10) | (PD_SYNC3<<15)) - -/* Hard Reset sequence : three RST-1 K-codes, then one RST-2 K-code */ -#define PD_HARD_RESET (PD_RST1 | (PD_RST1 << 5) |\ - (PD_RST1 << 10) | (PD_RST2 << 15)) - -/* - * Polarity based on 'DFP Perspective' (see table USB Type-C Cable and Connector - * Specification) - * - * CC1 CC2 STATE POSITION - * ---------------------------------------- - * open open NC N/A - * Rd open UFP attached 1 - * open Rd UFP attached 2 - * open Ra pwr cable no UFP N/A - * Ra open pwr cable no UFP N/A - * Rd Ra pwr cable & UFP 1 - * Ra Rd pwr cable & UFP 2 - * Rd Rd dbg accessory N/A - * Ra Ra audio accessory N/A - * - * Note, V(Rd) > V(Ra) - */ -#ifndef PD_SRC_RD_THRESHOLD -#define PD_SRC_RD_THRESHOLD PD_SRC_DEF_RD_THRESH_MV -#endif -#ifndef PD_SRC_VNC -#define PD_SRC_VNC PD_SRC_DEF_VNC_MV -#endif - -#ifndef CC_RA -#define CC_RA(port, cc, sel) (cc < PD_SRC_RD_THRESHOLD) -#endif -#define CC_RD(cc) ((cc >= PD_SRC_RD_THRESHOLD) && (cc < PD_SRC_VNC)) -#ifndef CC_NC -#define CC_NC(port, cc, sel) (cc >= PD_SRC_VNC) -#endif - -/* - * Polarity based on 'UFP Perspective'. - * - * CC1 CC2 STATE POSITION - * ---------------------------------------- - * open open NC N/A - * Rp open DFP attached 1 - * open Rp DFP attached 2 - * Rp Rp Accessory attached N/A - */ -#ifndef PD_SNK_VA -#define PD_SNK_VA PD_SNK_VA_MV -#endif - -#define CC_RP(cc) (cc >= PD_SNK_VA) - -/* - * Type C power source charge current limits are identified by their cc - * voltage (set by selecting the proper Rd resistor). Any voltage below - * TYPE_C_SRC_500_THRESHOLD will not be identified as a type C charger. - */ -#define TYPE_C_SRC_500_THRESHOLD PD_SRC_RD_THRESHOLD -#define TYPE_C_SRC_1500_THRESHOLD 660 /* mV */ -#define TYPE_C_SRC_3000_THRESHOLD 1230 /* mV */ - -/* Convert TCPC Alert register to index into pd.alert[] */ -#define ALERT_REG_TO_INDEX(reg) (reg - TCPC_REG_ALERT) - -/* PD transmit errors */ -enum pd_tx_errors { - PD_TX_ERR_GOODCRC = -1, /* Failed to receive goodCRC */ - PD_TX_ERR_DISABLED = -2, /* Attempted transmit even though disabled */ - PD_TX_ERR_INV_ACK = -4, /* Received different packet instead of gCRC */ - PD_TX_ERR_COLLISION = -5 /* Collision detected during transmit */ -}; - -/* PD Header with SOP* encoded in bits 31 - 28 */ -union pd_header_sop { - uint16_t pd_header; - uint32_t head; -}; - -/* - * If TCPM is not on this chip, and PD low power is defined, then use low - * power task delay logic. - */ -#if !defined(CONFIG_USB_POWER_DELIVERY) && defined(CONFIG_USB_PD_LOW_POWER) -#define TCPC_LOW_POWER -#endif - -/* - * Receive message buffer size. Buffer physical size is RX_BUFFER_SIZE + 1, - * but only RX_BUFFER_SIZE of that memory is used to store messages that can - * be retrieved from TCPM. The last slot is a temporary buffer for collecting - * a message before deciding whether or not to keep it. - */ -#ifdef CONFIG_USB_POWER_DELIVERY -#define RX_BUFFER_SIZE 1 -#else -#define RX_BUFFER_SIZE 2 -#endif - -static struct pd_port_controller { - /* current port power role (SOURCE or SINK) */ - uint8_t power_role; - /* current port data role (DFP or UFP) */ - uint8_t data_role; - /* Port polarity : 0 => CC1 is CC line, 1 => CC2 is CC line */ - uint8_t polarity; - /* Our CC pull resistor setting */ - uint8_t cc_pull; - /* CC status */ - uint8_t cc_status[2]; - /* TCPC alert status */ - uint16_t alert; - uint16_t alert_mask; - /* RX enabled */ - uint8_t rx_enabled; - /* Power status */ - uint8_t power_status; - uint8_t power_status_mask; - -#ifdef TCPC_LOW_POWER - /* Timestamp beyond which we allow low power task sampling */ - timestamp_t low_power_ts; -#endif - - /* Last received */ - int rx_head[RX_BUFFER_SIZE+1]; - uint32_t rx_payload[RX_BUFFER_SIZE+1][7]; - int rx_buf_head, rx_buf_tail; - - /* Next transmit */ - enum tcpm_transmit_type tx_type; - uint16_t tx_head; - uint32_t tx_payload[7]; - const uint32_t *tx_data; -} pd[CONFIG_USB_PD_PORT_MAX_COUNT]; - -static int rx_buf_is_full(int port) -{ - /* - * TODO: Refactor these to use the incrementing-counter idiom instead of - * the wrapping-counter idiom to reclaim the last buffer entry. - * - * Buffer is full if the tail is 1 ahead of head. - */ - int diff = pd[port].rx_buf_tail - pd[port].rx_buf_head; - return (diff == 1) || (diff == -RX_BUFFER_SIZE); -} - -int rx_buf_is_empty(int port) -{ - /* Buffer is empty if the head and tail are the same */ - return pd[port].rx_buf_tail == pd[port].rx_buf_head; -} - -void rx_buf_clear(int port) -{ - pd[port].rx_buf_tail = pd[port].rx_buf_head; -} - -static void rx_buf_increment(int port, int *buf_ptr) -{ - *buf_ptr = *buf_ptr == RX_BUFFER_SIZE ? 0 : *buf_ptr + 1; -} - -static inline int encode_short(int port, int off, uint16_t val16) -{ - off = pd_write_sym(port, off, bmc4b5b[(val16 >> 0) & 0xF]); - off = pd_write_sym(port, off, bmc4b5b[(val16 >> 4) & 0xF]); - off = pd_write_sym(port, off, bmc4b5b[(val16 >> 8) & 0xF]); - return pd_write_sym(port, off, bmc4b5b[(val16 >> 12) & 0xF]); -} - -int encode_word(int port, int off, uint32_t val32) -{ - off = encode_short(port, off, (val32 >> 0) & 0xFFFF); - return encode_short(port, off, (val32 >> 16) & 0xFFFF); -} - -/* prepare a 4b/5b-encoded PD message to send */ -int prepare_message(int port, uint16_t header, uint8_t cnt, - const uint32_t *data) -{ - int off, i; - /* 64-bit preamble */ - off = pd_write_preamble(port); -#if defined(CONFIG_USB_TYPEC_VPD) || defined(CONFIG_USB_TYPEC_CTVPD) - /* Start Of Packet Prime: 2x Sync-1 + 2x Sync-3 */ - off = pd_write_sym(port, off, BMC(PD_SYNC1)); - off = pd_write_sym(port, off, BMC(PD_SYNC1)); - off = pd_write_sym(port, off, BMC(PD_SYNC3)); - off = pd_write_sym(port, off, BMC(PD_SYNC3)); -#else - /* Start Of Packet: 3x Sync-1 + 1x Sync-2 */ - off = pd_write_sym(port, off, BMC(PD_SYNC1)); - off = pd_write_sym(port, off, BMC(PD_SYNC1)); - off = pd_write_sym(port, off, BMC(PD_SYNC1)); - off = pd_write_sym(port, off, BMC(PD_SYNC2)); -#endif - /* header */ - off = encode_short(port, off, header); - -#ifdef CONFIG_COMMON_RUNTIME - mutex_lock(&pd_crc_lock); -#endif - - crc32_init(); - crc32_hash16(header); - /* data payload */ - for (i = 0; i < cnt; i++) { - off = encode_word(port, off, data[i]); - crc32_hash32(data[i]); - } - /* CRC */ - off = encode_word(port, off, crc32_result()); - -#ifdef CONFIG_COMMON_RUNTIME - mutex_unlock(&pd_crc_lock); -#endif - - /* End Of Packet */ - off = pd_write_sym(port, off, BMC(PD_EOP)); - /* Ensure that we have a final edge */ - return pd_write_last_edge(port, off); -} - -static int send_hard_reset(int port) -{ - int off; - - if (debug_level >= 1) - CPRINTF("C%d Send hard reset\n", port); - - /* 64-bit preamble */ - off = pd_write_preamble(port); - /* Hard-Reset: 3x RST-1 + 1x RST-2 */ - off = pd_write_sym(port, off, BMC(PD_RST1)); - off = pd_write_sym(port, off, BMC(PD_RST1)); - off = pd_write_sym(port, off, BMC(PD_RST1)); - off = pd_write_sym(port, off, BMC(PD_RST2)); - /* Ensure that we have a final edge */ - off = pd_write_last_edge(port, off); - /* Transmit the packet */ - if (pd_start_tx(port, pd[port].polarity, off) < 0) - return PD_TX_ERR_COLLISION; - pd_tx_done(port, pd[port].polarity); - /* Keep RX monitoring on */ - pd_rx_enable_monitoring(port); - return 0; -} - -static int send_validate_message(int port, uint16_t header, - const uint32_t *data) -{ - int r; - static uint32_t payload[7]; - uint8_t expected_msg_id = PD_HEADER_ID(header); - uint8_t cnt = PD_HEADER_CNT(header); - int retries = PD_HEADER_TYPE(header) == PD_DATA_SOURCE_CAP ? - 0 : PD_RETRY_COUNT; - - /* retry 3 times if we are not getting a valid answer */ - for (r = 0; r <= retries; r++) { - int bit_len, head; - /* write the encoded packet in the transmission buffer */ - bit_len = prepare_message(port, header, cnt, data); - /* Transmit the packet */ - if (pd_start_tx(port, pd[port].polarity, bit_len) < 0) { - /* - * Collision detected, return immediately so we can - * respond to what we have received. - */ - return PD_TX_ERR_COLLISION; - } - pd_tx_done(port, pd[port].polarity); - /* - * If this is the first attempt, leave RX monitoring off, - * and do a blocking read of the channel until timeout or - * packet received. If we failed the first try, enable - * interrupt and yield to other tasks, so that we don't - * starve them. - */ - if (r) { - pd_rx_enable_monitoring(port); - /* Wait for message receive timeout */ - if (task_wait_event(USB_PD_RX_TMOUT_US) == - TASK_EVENT_TIMER) - continue; - /* - * Make sure we woke up due to rx recd, otherwise - * we need to manually start - */ - if (!pd_rx_started(port)) { - pd_rx_disable_monitoring(port); - pd_rx_start(port); - } - } else { - /* starting waiting for GoodCrc */ - pd_rx_start(port); - } - /* read the incoming packet if any */ - head = pd_analyze_rx(port, payload); - pd_rx_complete(port); - /* keep RX monitoring on to avoid collisions */ - pd_rx_enable_monitoring(port); - if (head > 0) { /* we got a good packet, analyze it */ - int type = PD_HEADER_TYPE(head); - int nb = PD_HEADER_CNT(head); - uint8_t id = PD_HEADER_ID(head); - if (type == PD_CTRL_GOOD_CRC && nb == 0 && - id == expected_msg_id) { - /* got the GoodCRC we were expecting */ - /* do not catch last edges as a new packet */ - udelay(20); - return bit_len; - } else { - /* - * we have received a good packet - * but not the expected GoodCRC, - * the other side is trying to contact us, - * bail out immediately so we can get the retry. - */ - return PD_TX_ERR_INV_ACK; - } - } - } - /* we failed all the re-transmissions */ - if (debug_level >= 1) - CPRINTF("TX NOACK%d %04x/%d\n", port, header, cnt); - return PD_TX_ERR_GOODCRC; -} - -static void send_goodcrc(int port, int id) -{ - uint16_t header = PD_HEADER(PD_CTRL_GOOD_CRC, pd[port].power_role, - pd[port].data_role, id, 0, 0, 0); - int bit_len = prepare_message(port, header, 0, NULL); - - if (pd_start_tx(port, pd[port].polarity, bit_len) < 0) - /* another packet recvd before we could send goodCRC */ - return; - pd_tx_done(port, pd[port].polarity); - /* Keep RX monitoring on */ - pd_rx_enable_monitoring(port); -} - -#if 0 -/* TODO: when/how do we trigger this ? */ -static int analyze_rx_bist(int port); - -void bist_mode_2_rx(int port) -{ - int analyze_bist = 0; - int num_bits; - timestamp_t start_time; - - /* monitor for incoming packet */ - pd_rx_enable_monitoring(port); - - /* loop until we start receiving data */ - start_time.val = get_time().val; - while ((get_time().val - start_time.val) < (500*MSEC)) { - task_wait_event(10*MSEC); - /* incoming packet ? */ - if (pd_rx_started(port)) { - analyze_bist = 1; - break; - } - } - - if (analyze_bist) { - /* - * once we start receiving bist data, analyze 40 bytes - * every 10 msec. Continue analyzing until BIST data - * is no longer received. The standard limits the max - * BIST length to 60 msec. - */ - start_time.val = get_time().val; - while ((get_time().val - start_time.val) - < (PD_T_BIST_RECEIVE)) { - num_bits = analyze_rx_bist(port); - pd_rx_complete(port); - /* - * If no data was received, then analyze_rx_bist() - * will return a -1 and there is no need to stay - * in this mode - */ - if (num_bits == -1) - break; - msleep(10); - pd_rx_enable_monitoring(port); - } - } else { - CPRINTF("BIST RX TO\n"); - } -} -#endif - -static void bist_mode_2_tx(int port) -{ - int bit; - - CPRINTF("BIST 2: p%d\n", port); - /* - * build context buffer with 5 bytes, where the data is - * alternating 1's and 0's. - */ - bit = pd_write_sym(port, 0, BMC(0x15)); - bit = pd_write_sym(port, bit, BMC(0x0a)); - bit = pd_write_sym(port, bit, BMC(0x15)); - bit = pd_write_sym(port, bit, BMC(0x0a)); - - /* start a circular DMA transfer */ - pd_tx_set_circular_mode(port); - pd_start_tx(port, pd[port].polarity, bit); - - task_wait_event(PD_T_BIST_TRANSMIT); - - /* clear dma circular mode, will also stop dma */ - pd_tx_clear_circular_mode(port); - /* finish and cleanup transmit */ - pd_tx_done(port, pd[port].polarity); -} - -static inline int decode_short(int port, int off, uint16_t *val16) -{ - uint32_t w; - int end; - - end = pd_dequeue_bits(port, off, 20, &w); - -#if 0 /* DEBUG */ - CPRINTS("%d-%d: %05x %x:%x:%x:%x", - off, end, w, - dec4b5b[(w >> 15) & 0x1f], dec4b5b[(w >> 10) & 0x1f], - dec4b5b[(w >> 5) & 0x1f], dec4b5b[(w >> 0) & 0x1f]); -#endif - *val16 = dec4b5b[w & 0x1f] | - (dec4b5b[(w >> 5) & 0x1f] << 4) | - (dec4b5b[(w >> 10) & 0x1f] << 8) | - (dec4b5b[(w >> 15) & 0x1f] << 12); - return end; -} - -static inline int decode_word(int port, int off, uint32_t *val32) -{ - off = decode_short(port, off, (uint16_t *)val32); - return decode_short(port, off, ((uint16_t *)val32 + 1)); -} - -#ifdef CONFIG_COMMON_RUNTIME -#if 0 -/* - * TODO: when/how do we trigger this ? Could add custom vendor command - * to TCPCI to enter bist verification? Is there an easier way? - */ -static int count_set_bits(int n) -{ - int count = 0; - while (n) { - n &= (n - 1); - count++; - } - return count; -} - -static int analyze_rx_bist(int port) -{ - int i = 0, bit = -1; - uint32_t w, match; - int invalid_bits = 0; - int bits_analyzed = 0; - static int total_invalid_bits; - - /* dequeue bits until we see a full byte of alternating 1's and 0's */ - while (i < 10 && (bit < 0 || (w != 0xaa && w != 0x55))) - bit = pd_dequeue_bits(port, i++, 8, &w); - - /* if we didn't find any bytes that match criteria, display error */ - if (i == 10) { - CPRINTF("invalid pattern\n"); - return -1; - } - /* - * now we know what matching byte we are looking for, dequeue a bunch - * more data and count how many bits differ from expectations. - */ - match = w; - bit = i - 1; - for (i = 0; i < 40; i++) { - bit = pd_dequeue_bits(port, bit, 8, &w); - if (i && (i % 20 == 0)) - CPRINTF("\n"); - CPRINTF("%02x ", w); - bits_analyzed += 8; - invalid_bits += count_set_bits(w ^ match); - } - - total_invalid_bits += invalid_bits; - - CPRINTF("\nInvalid: %d/%d\n", - invalid_bits, total_invalid_bits); - return bits_analyzed; -} -#endif -#endif - -int pd_analyze_rx(int port, uint32_t *payload) -{ - int bit; - char *msg = "---"; - uint32_t val = 0; - union pd_header_sop phs; - uint32_t pcrc, ccrc; - int p, cnt; - uint32_t eop; - - pd_init_dequeue(port); - - /* Detect preamble */ - bit = pd_find_preamble(port); - if (bit == PD_RX_ERR_HARD_RESET || bit == PD_RX_ERR_CABLE_RESET) { - /* Hard reset or cable reset */ - return bit; - } else if (bit < 0) { - msg = "Preamble"; - goto packet_err; - } - - /* Find the Start Of Packet sequence */ - while (bit > 0) { - bit = pd_dequeue_bits(port, bit, 20, &val); -#if defined(CONFIG_USB_TYPEC_VPD) || defined(CONFIG_USB_TYPEC_CTVPD) - if (val == PD_SOP_PRIME) { - break; - } else if (val == PD_SOP) { - CPRINTF("SOP\n"); - return PD_RX_ERR_UNSUPPORTED_SOP; - } else if (val == PD_SOP_PRIME_PRIME) { - CPRINTF("SOP''\n"); - return PD_RX_ERR_UNSUPPORTED_SOP; - } -#else /* CONFIG_USB_TYPEC_VPD || CONFIG_USB_TYPEC_CTVPD */ -#ifdef CONFIG_USB_PD_DECODE_SOP - if (val == PD_SOP || val == PD_SOP_PRIME || - val == PD_SOP_PRIME_PRIME) - break; -#else - if (val == PD_SOP) { - break; - } else if (val == PD_SOP_PRIME) { - CPRINTF("SOP'\n"); - return PD_RX_ERR_UNSUPPORTED_SOP; - } else if (val == PD_SOP_PRIME_PRIME) { - CPRINTF("SOP''\n"); - return PD_RX_ERR_UNSUPPORTED_SOP; - } -#endif /* CONFIG_USB_PD_DECODE_SOP */ -#endif /* CONFIG_USB_TYPEC_VPD || CONFIG_USB_TYPEC_CTVPD */ - } - if (bit < 0) { -#ifdef CONFIG_USB_PD_DECODE_SOP - if (val == PD_SOP) - msg = "SOP"; - else if (val == PD_SOP_PRIME) - msg = "SOP'"; - else if (val == PD_SOP_PRIME_PRIME) - msg = "SOP''"; - else - msg = "SOP*"; -#else - msg = "SOP"; -#endif - goto packet_err; - } - - phs.head = 0; - - /* read header */ - bit = decode_short(port, bit, &phs.pd_header); - -#ifdef CONFIG_COMMON_RUNTIME - mutex_lock(&pd_crc_lock); -#endif - - crc32_init(); - crc32_hash16(phs.pd_header); - cnt = PD_HEADER_CNT(phs.pd_header); - -#ifdef CONFIG_USB_PD_DECODE_SOP - /* Encode message address */ - if (val == PD_SOP) { - phs.head |= PD_HEADER_SOP(PD_MSG_SOP); - } else if (val == PD_SOP_PRIME) { - phs.head |= PD_HEADER_SOP(PD_MSG_SOPP); - } else if (val == PD_SOP_PRIME_PRIME) { - phs.head |= PD_HEADER_SOP(PD_MSG_SOPPP); - } else { - msg = "SOP*"; - goto packet_err; - } -#endif - - /* read payload data */ - for (p = 0; p < cnt && bit > 0; p++) { - bit = decode_word(port, bit, payload+p); - crc32_hash32(payload[p]); - } - ccrc = crc32_result(); - -#ifdef CONFIG_COMMON_RUNTIME - mutex_unlock(&pd_crc_lock); -#endif - - if (bit < 0) { - msg = "len"; - goto packet_err; - } - - /* check transmitted CRC */ - bit = decode_word(port, bit, &pcrc); - if (bit < 0 || pcrc != ccrc) { - msg = "CRC"; - if (pcrc != ccrc) - bit = PD_RX_ERR_CRC; - if (debug_level >= 1) - CPRINTF("CRC%d %08x <> %08x\n", port, pcrc, ccrc); - goto packet_err; - } - - /* - * Check EOP. EOP is 5 bits, but last bit may not be able to - * be dequeued, depending on ending state of CC line, so stop - * at 4 bits (assumes last bit is 0). - */ - bit = pd_dequeue_bits(port, bit, 4, &eop); - if (bit < 0 || eop != PD_EOP) { - msg = "EOP"; - goto packet_err; - } - - return phs.head; -packet_err: - if (debug_level >= 2) - pd_dump_packet(port, msg); - else - CPRINTF("RXERR%d %s\n", port, msg); - return bit; -} - -static void handle_request(int port, uint16_t head) -{ - int cnt = PD_HEADER_CNT(head); - - if (PD_HEADER_TYPE(head) != PD_CTRL_GOOD_CRC || cnt) - send_goodcrc(port, PD_HEADER_ID(head)); - else - /* keep RX monitoring on to avoid collisions */ - pd_rx_enable_monitoring(port); -} - -/* Convert CC voltage to CC status */ -static int cc_voltage_to_status(int port, int cc_volt, int cc_sel) -{ - /* If we have a pull-up, then we are source, check for Rd. */ - if (pd[port].cc_pull == TYPEC_CC_RP) { - if (CC_NC(port, cc_volt, cc_sel)) - return TYPEC_CC_VOLT_OPEN; - else if (CC_RA(port, cc_volt, cc_sel)) - return TYPEC_CC_VOLT_RA; - else - return TYPEC_CC_VOLT_RD; - /* If we have a pull-down, then we are sink, check for Rp. */ - } -#ifdef CONFIG_USB_PD_DUAL_ROLE - else if (pd[port].cc_pull == TYPEC_CC_RD) { - if (cc_volt >= TYPE_C_SRC_3000_THRESHOLD) - return TYPEC_CC_VOLT_RP_3_0; - else if (cc_volt >= TYPE_C_SRC_1500_THRESHOLD) - return TYPEC_CC_VOLT_RP_1_5; - else if (CC_RP(cc_volt)) - return TYPEC_CC_VOLT_RP_DEF; - else - return TYPEC_CC_VOLT_OPEN; - } -#endif - /* If we are open, then always return 0 */ - else - return 0; -} - -static void alert(int port, int mask) -{ - /* Always update the Alert status register */ - pd[port].alert |= mask; - /* - * Only send interrupt to TCPM if corresponding - * bit in the alert_enable register is set. - */ - if (pd[port].alert_mask & mask) - tcpc_alert(port); -} - -int tcpc_run(int port, int evt) -{ - int cc, i, res; - - /* incoming packet ? */ - if (pd_rx_started(port) && pd[port].rx_enabled) { - /* Get message and place at RX buffer head */ - res = pd[port].rx_head[pd[port].rx_buf_head] = - pd_analyze_rx(port, - pd[port].rx_payload[pd[port].rx_buf_head]); - pd_rx_complete(port); - - /* - * If there is space in buffer, then increment head to keep - * the message and send goodCRC. If this is a hard reset, - * send alert regardless of rx buffer status. Else if there is - * no space in buffer, then do not send goodCRC and drop - * message. - */ - if (res > 0 && !rx_buf_is_full(port)) { - rx_buf_increment(port, &pd[port].rx_buf_head); - handle_request(port, res); - alert(port, TCPC_REG_ALERT_RX_STATUS); - } else if (res == PD_RX_ERR_HARD_RESET) { - alert(port, TCPC_REG_ALERT_RX_HARD_RST); - } - } - - /* outgoing packet ? */ - if ((evt & PD_EVENT_TX) && pd[port].rx_enabled) { - switch (pd[port].tx_type) { -#if defined(CONFIG_USB_TYPEC_VPD) || defined(CONFIG_USB_TYPEC_CTVPD) - case TCPC_TX_SOP_PRIME: -#else - case TCPC_TX_SOP: -#endif - res = send_validate_message(port, - pd[port].tx_head, - pd[port].tx_data); - break; - case TCPC_TX_BIST_MODE_2: - bist_mode_2_tx(port); - res = 0; - break; - case TCPC_TX_HARD_RESET: - res = send_hard_reset(port); - break; - default: - res = PD_TX_ERR_DISABLED; - break; - } - - /* send appropriate alert for tx completion */ - if (res >= 0) - alert(port, TCPC_REG_ALERT_TX_SUCCESS); - else if (res == PD_TX_ERR_GOODCRC) - alert(port, TCPC_REG_ALERT_TX_FAILED); - else - alert(port, TCPC_REG_ALERT_TX_DISCARDED); - } else { - /* If we have nothing to transmit, then sample CC lines */ - - /* CC pull changed, wait 1ms for CC voltage to stabilize */ - if (evt & PD_EVENT_CC) - usleep(MSEC); - - /* check CC lines */ - for (i = 0; i < 2; i++) { - /* read CC voltage */ - cc = pd_adc_read(port, i); - - /* convert voltage to status, and check status change */ - cc = cc_voltage_to_status(port, cc, i); - if (pd[port].cc_status[i] != cc) { - pd[port].cc_status[i] = cc; - alert(port, TCPC_REG_ALERT_CC_STATUS); - } - } - } - - /* make sure PD monitoring is enabled to wake on PD RX */ - if (pd[port].rx_enabled) - pd_rx_enable_monitoring(port); - -#ifdef TCPC_LOW_POWER - /* - * If we are presenting Rd with no connection, and timestamp is - * past the low power timestamp, then we don't need to sample - * CC lines as often. In this case, our connection delay should not - * actually increased because we will get an interrupt on VBUS detect. - */ - return (get_time().val >= pd[port].low_power_ts.val && - pd[port].cc_pull == TYPEC_CC_RD && - cc_is_open(pd[port].cc_status[0], pd[port].cc_status[1])) - ? 200 * MSEC - : 10 * MSEC; -#else - return 10*MSEC; -#endif -} - -#if !defined(CONFIG_USB_POWER_DELIVERY) && !defined(CONFIG_USB_SM_FRAMEWORK) -void pd_task(void *u) -{ - int port = TASK_ID_TO_PD_PORT(task_get_current()); - int timeout = 10*MSEC; - int evt; - - /* initialize phy task */ - tcpc_init(port); - - /* we are now initialized */ - pd[port].power_status &= ~TCPC_REG_POWER_STATUS_UNINIT; - - while (1) { - /* wait for next event/packet or timeout expiration */ - evt = task_wait_event(timeout); - - /* run phy task once */ - timeout = tcpc_run(port, evt); - } -} -#endif - -void pd_rx_event(int port) -{ - task_set_event(PD_PORT_TO_TASK_ID(port), TASK_EVENT_WAKE, 0); -} - -int tcpc_alert_status(int port, int *alert) -{ - /* return the value of the TCPC Alert register */ - uint16_t ret = pd[port].alert; - *alert = ret; - return EC_SUCCESS; -} - -int tcpc_alert_status_clear(int port, uint16_t mask) -{ - /* - * If the RX status alert is attempting to be cleared, then increment - * rx buffer tail pointer. if the RX buffer is not empty, then keep - * the RX status alert active. - */ - if (mask & TCPC_REG_ALERT_RX_STATUS) { - if (!rx_buf_is_empty(port)) { - rx_buf_increment(port, &pd[port].rx_buf_tail); - if (!rx_buf_is_empty(port)) - /* buffer is not empty, keep alert active */ - mask &= ~TCPC_REG_ALERT_RX_STATUS; - } - } - - /* clear only the bits specified by the TCPM */ - pd[port].alert &= ~mask; -#ifndef CONFIG_USB_POWER_DELIVERY - /* Set Alert# inactive if all alert bits clear */ - if (!pd[port].alert) - tcpc_alert_clear(port); -#endif - return EC_SUCCESS; -} - -int tcpc_alert_mask_set(int port, uint16_t mask) -{ - /* Update the alert mask as specificied by the TCPM */ - pd[port].alert_mask = mask; - return EC_SUCCESS; -} - -int tcpc_set_cc(int port, int pull) -{ - /* If CC pull resistor not changing, then nothing to do */ - if (pd[port].cc_pull == pull) - return EC_SUCCESS; - - /* Change CC pull resistor */ - pd[port].cc_pull = pull; -#ifdef CONFIG_USB_PD_DUAL_ROLE - pd_set_host_mode(port, pull == TYPEC_CC_RP); -#endif - -#ifdef TCPC_LOW_POWER - /* - * Reset the low power timestamp every time CC termination toggles, - * because we only want to go into low power mode when we are not - * dual-role toggling. - */ - pd[port].low_power_ts.val = get_time().val + - 2*(PD_T_DRP_SRC + PD_T_DRP_SNK); -#endif - - /* - * Before CC pull can be changed and the task can read the new - * status, we should set the CC status to open, in case TCPM - * asks before it is known for sure. - */ - pd[port].cc_status[0] = TYPEC_CC_VOLT_OPEN; - pd[port].cc_status[1] = pd[port].cc_status[0]; - - /* Wake the PD phy task with special CC event mask */ - /* TODO: use top case if no TCPM on same CPU */ -#ifdef CONFIG_USB_POWER_DELIVERY - tcpc_run(port, PD_EVENT_CC); -#else - task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_CC, 0); -#endif - return EC_SUCCESS; -} - -int tcpc_get_cc(int port, enum tcpc_cc_voltage_status *cc1, - enum tcpc_cc_voltage_status *cc2) -{ - *cc2 = pd[port].cc_status[1]; - *cc1 = pd[port].cc_status[0]; - - return EC_SUCCESS; -} - -int board_select_rp_value(int port, int rp) __attribute__((weak)); - -int tcpc_select_rp_value(int port, int rp) -{ - if (board_select_rp_value) - return board_select_rp_value(port, rp); - else - return EC_ERROR_UNIMPLEMENTED; -} - -int tcpc_set_polarity(int port, int polarity) -{ - pd[port].polarity = polarity; - pd_select_polarity(port, pd[port].polarity); - - return EC_SUCCESS; -} - -#ifdef CONFIG_USB_PD_TCPC_TRACK_VBUS -static int tcpc_set_power_status(int port, int vbus_present) -{ - /* Update VBUS present bit */ - if (vbus_present) - pd[port].power_status |= TCPC_REG_POWER_STATUS_VBUS_PRES; - else - pd[port].power_status &= ~TCPC_REG_POWER_STATUS_VBUS_PRES; - - /* Set bit Port Power Status bit in Alert register */ - if (pd[port].power_status_mask & TCPC_REG_POWER_STATUS_VBUS_PRES) - alert(port, TCPC_REG_ALERT_POWER_STATUS); - - return EC_SUCCESS; -} -#endif /* CONFIG_USB_PD_TCPC_TRACK_VBUS */ - -int tcpc_set_power_status_mask(int port, uint8_t mask) -{ - pd[port].power_status_mask = mask; - return EC_SUCCESS; -} - -int tcpc_set_vconn(int port, int enable) -{ -#ifdef CONFIG_USBC_VCONN - pd_set_vconn(port, pd[port].polarity, enable); -#endif - return EC_SUCCESS; -} - -int tcpc_set_rx_enable(int port, int enable) -{ -#if defined(CONFIG_LOW_POWER_IDLE) && !defined(CONFIG_USB_POWER_DELIVERY) - int i; -#endif - pd[port].rx_enabled = enable; - - if (!enable) - pd_rx_disable_monitoring(port); - -#if defined(CONFIG_LOW_POWER_IDLE) && !defined(CONFIG_USB_POWER_DELIVERY) - /* If any PD port is connected, then disable deep sleep */ - for (i = 0; i < board_get_usb_pd_port_count(); ++i) - if (pd[i].rx_enabled) - break; - - if (i == board_get_usb_pd_port_count()) - enable_sleep(SLEEP_MASK_USB_PD); - else - disable_sleep(SLEEP_MASK_USB_PD); -#endif - return EC_SUCCESS; -} - -int tcpc_transmit(int port, enum tcpm_transmit_type type, uint16_t header, - const uint32_t *data) -{ - /* Store data to transmit and wake task to send it */ - pd[port].tx_type = type; - pd[port].tx_head = header; - pd[port].tx_data = data; - /* TODO: use top case if no TCPM on same CPU */ -#ifdef CONFIG_USB_POWER_DELIVERY - tcpc_run(port, PD_EVENT_TX); -#else - task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_TX, 0); -#endif - return EC_SUCCESS; -} - -int tcpc_set_msg_header(int port, int power_role, int data_role) -{ - pd[port].power_role = power_role; - pd[port].data_role = data_role; - - return EC_SUCCESS; -} - -int tcpc_get_message(int port, uint32_t *payload, int *head) -{ - /* Get message at tail of RX buffer */ - int idx = pd[port].rx_buf_tail; - - memcpy(payload, pd[port].rx_payload[idx], - sizeof(pd[port].rx_payload[idx])); - *head = pd[port].rx_head[idx]; - return EC_SUCCESS; -} - -void tcpc_pre_init(void) -{ - int i; - - /* Mark as uninitialized */ - for (i = 0; i < board_get_usb_pd_port_count(); i++) - pd[i].power_status |= TCPC_REG_POWER_STATUS_UNINIT | - TCPC_REG_POWER_STATUS_VBUS_DET; -} -/* Must be prioritized above i2c init */ -DECLARE_HOOK(HOOK_INIT, tcpc_pre_init, HOOK_PRIO_INIT_I2C - 1); - -void tcpc_init(int port) -{ - int i; - - if (port >= board_get_usb_pd_port_count()) - return; - - /* Initialize physical layer */ - pd_hw_init(port, PD_ROLE_DEFAULT(port)); - pd[port].cc_pull = PD_ROLE_DEFAULT(port) == - PD_ROLE_SOURCE ? TYPEC_CC_RP : TYPEC_CC_RD; -#ifdef TCPC_LOW_POWER - /* Don't use low power immediately after boot */ - pd[port].low_power_ts.val = get_time().val + SECOND; -#endif - - /* make sure PD monitoring is disabled initially */ - pd[port].rx_enabled = 0; - - /* make initial readings of CC voltages */ - for (i = 0; i < 2; i++) { - pd[port].cc_status[i] = cc_voltage_to_status(port, - pd_adc_read(port, i), - i); - } - -#ifdef CONFIG_USB_PD_TCPC_TRACK_VBUS -#if CONFIG_USB_PD_PORT_MAX_COUNT >= 2 - tcpc_set_power_status(port, !gpio_get_level(port ? - GPIO_USB_C1_VBUS_WAKE_L : - GPIO_USB_C0_VBUS_WAKE_L)); -#else - tcpc_set_power_status(port, !gpio_get_level(GPIO_USB_C0_VBUS_WAKE_L)); -#endif /* CONFIG_USB_PD_PORT_MAX_COUNT >= 2 */ -#endif /* CONFIG_USB_PD_TCPC_TRACK_VBUS */ - - /* set default alert and power mask register values */ - pd[port].alert_mask = TCPC_REG_ALERT_MASK_ALL; - pd[port].power_status_mask = TCPC_REG_POWER_STATUS_MASK_ALL; - - /* set power status alert since the UNINIT bit has been set */ - alert(port, TCPC_REG_ALERT_POWER_STATUS); -} - -#ifdef CONFIG_USB_PD_TCPC_TRACK_VBUS -void pd_vbus_evt_p0(enum gpio_signal signal) -{ - tcpc_set_power_status(TASK_ID_TO_PD_PORT(TASK_ID_PD_C0), - !gpio_get_level(GPIO_USB_C0_VBUS_WAKE_L)); - task_wake(TASK_ID_PD_C0); -} - -#if CONFIG_USB_PD_PORT_MAX_COUNT >= 2 -void pd_vbus_evt_p1(enum gpio_signal signal) -{ - if (board_get_usb_pd_port_count() == 1) - return; - - tcpc_set_power_status(TASK_ID_TO_PD_PORT(TASK_ID_PD_C1), - !gpio_get_level(GPIO_USB_C1_VBUS_WAKE_L)); - task_wake(TASK_ID_PD_C1); -} -#endif /* PD_PORT_COUNT >= 2 */ -#endif /* CONFIG_USB_PD_TCPC_TRACK_VBUS */ - -#ifndef CONFIG_USB_POWER_DELIVERY -static void tcpc_i2c_write(int port, int reg, int len, uint8_t *payload) -{ - uint16_t alert; - - /* If we are not yet initialized, ignore any write command */ - if (pd[port].power_status & TCPC_REG_POWER_STATUS_UNINIT) - return; - - switch (reg) { - case TCPC_REG_ROLE_CTRL: - tcpc_set_cc(port, TCPC_REG_ROLE_CTRL_CC1(payload[1])); - break; - case TCPC_REG_POWER_CTRL: - tcpc_set_vconn(port, TCPC_REG_POWER_CTRL_VCONN(payload[1])); - break; - case TCPC_REG_TCPC_CTRL: - tcpc_set_polarity(port, - TCPC_REG_TCPC_CTRL_POLARITY(payload[1])); - break; - case TCPC_REG_MSG_HDR_INFO: - tcpc_set_msg_header(port, - TCPC_REG_MSG_HDR_INFO_PROLE(payload[1]), - TCPC_REG_MSG_HDR_INFO_DROLE(payload[1])); - break; - case TCPC_REG_ALERT: - alert = payload[1]; - alert |= (payload[2] << 8); - /* clear alert bits specified by the TCPM */ - tcpc_alert_status_clear(port, alert); - break; - case TCPC_REG_ALERT_MASK: - alert = payload[1]; - alert |= (payload[2] << 8); - tcpc_alert_mask_set(port, alert); - break; - case TCPC_REG_RX_DETECT: - tcpc_set_rx_enable(port, payload[1] & - TCPC_REG_RX_DETECT_SOP_HRST_MASK); - break; - case TCPC_REG_POWER_STATUS_MASK: - tcpc_set_power_status_mask(port, payload[1]); - break; - case TCPC_REG_TX_HDR: - pd[port].tx_head = (payload[2] << 8) | payload[1]; - break; - case TCPC_REG_TX_DATA: - memcpy(pd[port].tx_payload, &payload[1], len - 1); - break; - case TCPC_REG_TRANSMIT: - tcpc_transmit(port, TCPC_REG_TRANSMIT_TYPE(payload[1]), - pd[port].tx_head, pd[port].tx_payload); - break; - } -} - -static int tcpc_i2c_read(int port, int reg, uint8_t *payload) -{ - enum tcpc_cc_voltage_status cc1, cc2; - int alert; - - switch (reg) { - case TCPC_REG_VENDOR_ID: - *(uint16_t *)payload = USB_VID_GOOGLE; - return 2; - case TCPC_REG_CC_STATUS: - tcpc_get_cc(port, &cc1, &cc2); - payload[0] = TCPC_REG_CC_STATUS_SET( - pd[port].cc_pull == TYPEC_CC_RD, - pd[port].cc_status[0], pd[port].cc_status[1]); - return 1; - case TCPC_REG_ROLE_CTRL: - payload[0] = TCPC_REG_ROLE_CTRL_SET(0, 0, - pd[port].cc_pull, - pd[port].cc_pull); - return 1; - case TCPC_REG_TCPC_CTRL: - payload[0] = TCPC_REG_TCPC_CTRL_SET(pd[port].polarity); - return 1; - case TCPC_REG_MSG_HDR_INFO: - payload[0] = TCPC_REG_MSG_HDR_INFO_SET(pd[port].data_role, - pd[port].power_role); - return 1; - case TCPC_REG_RX_DETECT: - payload[0] = pd[port].rx_enabled ? - TCPC_REG_RX_DETECT_SOP_HRST_MASK : 0; - return 1; - case TCPC_REG_ALERT: - tcpc_alert_status(port, &alert); - payload[0] = alert & 0xff; - payload[1] = (alert >> 8) & 0xff; - return 2; - case TCPC_REG_ALERT_MASK: - payload[0] = pd[port].alert_mask & 0xff; - payload[1] = (pd[port].alert_mask >> 8) & 0xff; - return 2; - case TCPC_REG_RX_BYTE_CNT: - payload[0] = 3 + 4 * - PD_HEADER_CNT(pd[port].rx_head[pd[port].rx_buf_tail]); - return 1; - case TCPC_REG_RX_HDR: - payload[0] = pd[port].rx_head[pd[port].rx_buf_tail] & 0xff; - payload[1] = - (pd[port].rx_head[pd[port].rx_buf_tail] >> 8) & 0xff; - return 2; - case TCPC_REG_RX_DATA: - memcpy(payload, pd[port].rx_payload[pd[port].rx_buf_tail], - sizeof(pd[port].rx_payload[pd[port].rx_buf_tail])); - return sizeof(pd[port].rx_payload[pd[port].rx_buf_tail]); - case TCPC_REG_POWER_STATUS: - payload[0] = pd[port].power_status; - return 1; - case TCPC_REG_POWER_STATUS_MASK: - payload[0] = pd[port].power_status_mask; - return 1; - case TCPC_REG_TX_HDR: - payload[0] = pd[port].tx_head & 0xff; - payload[1] = (pd[port].tx_head >> 8) & 0xff; - return 2; - case TCPC_REG_TX_DATA: - memcpy(payload, pd[port].tx_payload, - sizeof(pd[port].tx_payload)); - return sizeof(pd[port].tx_payload); - default: - return 0; - } -} - -void tcpc_i2c_process(int read, int port, int len, uint8_t *payload, - void (*send_response)(int)) -{ - int i, reg; - - if (debug_level >= 1) { - CPRINTF("tcpci p%d: ", port); - for (i = 0; i < len; i++) - CPRINTF("0x%02x ", payload[i]); - CPRINTF("\n"); - } - - /* length must always be at least 1 */ - if (len == 0) { - /* - * if this is a read, we must call send_response() for - * i2c transaction to finishe properly - */ - if (read) - (*send_response)(0); - } - - /* if this is a write, length must be at least 2 */ - if (!read && len < 2) - return; - - /* register is always first byte */ - reg = payload[0]; - - /* perform read or write */ - if (read) { - len = tcpc_i2c_read(port, reg, payload); - (*send_response)(len); - } else { - tcpc_i2c_write(port, reg, len, payload); - } -} -#endif - -#ifdef CONFIG_COMMON_RUNTIME -static int command_tcpc(int argc, char **argv) -{ - int port; - char *e; - - if (argc < 2) - return EC_ERROR_PARAM_COUNT; - - if (!strcasecmp(argv[1], "dump")) { - int level; - - if (argc < 3) - ccprintf("lvl: %d\n", debug_level); - else { - level = strtoi(argv[2], &e, 10); - if (*e) - return EC_ERROR_PARAM2; - debug_level = level; - } - return EC_SUCCESS; - } - - /* command: pd <port> <subcmd> [args] */ - port = strtoi(argv[1], &e, 10); - if (argc < 3) - return EC_ERROR_PARAM_COUNT; - if (*e || port >= board_get_usb_pd_port_count()) - return EC_ERROR_PARAM2; - - if (!strcasecmp(argv[2], "clock")) { - int freq; - - if (argc < 4) - return EC_ERROR_PARAM2; - - freq = strtoi(argv[3], &e, 10); - if (*e) - return EC_ERROR_PARAM2; - pd_set_clock(port, freq); - ccprintf("set TX frequency to %d Hz\n", freq); - return EC_SUCCESS; - } else if (!strncasecmp(argv[2], "state", 5)) { - ccprintf("Port C%d, %s - CC:%d, CC0:%d, CC1:%d\n" - "Alert: 0x%02x Mask: 0x%04x\n" - "Power Status: 0x%02x Mask: 0x%02x\n", port, - pd[port].rx_enabled ? "Ena" : "Dis", - pd[port].cc_pull, - pd[port].cc_status[0], pd[port].cc_status[1], - pd[port].alert, pd[port].alert_mask, - pd[port].power_status, pd[port].power_status_mask); - } - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(tcpc, command_tcpc, - "dump [0|1]\n\t<port> [clock|state]", - "Type-C Port Controller"); -#endif diff --git a/common/usb_port_power_dumb.c b/common/usb_port_power_dumb.c deleted file mode 100644 index 5cdf6005a6..0000000000 --- a/common/usb_port_power_dumb.c +++ /dev/null @@ -1,162 +0,0 @@ -/* Copyright 2012 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. - */ - -/* USB charging control module for Chrome EC */ - -#include "chipset.h" -#include "common.h" -#include "console.h" -#include "gpio.h" -#include "hooks.h" -#include "host_command.h" -#include "system.h" -#include "usb_charge.h" -#include "util.h" - -#define CPUTS(outstr) cputs(CC_USBCHARGE, outstr) -#define CPRINTS(format, args...) cprints(CC_USBCHARGE, format, ## args) - -#define USB_SYSJUMP_TAG 0x5550 /* "UP" - Usb Port */ -#define USB_HOOK_VERSION 1 - -static uint8_t charge_mode[USB_PORT_COUNT]; -extern const int usb_port_enable[USB_PORT_COUNT]; - -static void usb_port_set_enabled(int port_id, int en) -{ - gpio_set_level(usb_port_enable[port_id], en); - charge_mode[port_id] = en; -} - -static void usb_port_all_ports_on(void) -{ - int i; - for (i = 0; i < USB_PORT_COUNT; i++) - usb_port_set_enabled(i, 1); -} - -static void usb_port_all_ports_off(void) -{ - int i; - for (i = 0; i < USB_PORT_COUNT; i++) - usb_port_set_enabled(i, 0); -} - -/*****************************************************************************/ -/* Host commands */ - -int usb_charge_set_mode(int port_id, enum usb_charge_mode mode, - enum usb_suspend_charge inhibit_charge) -{ - CPRINTS("USB port p%d %d", port_id, mode); - - if (port_id < 0 || port_id >= USB_PORT_COUNT) - return EC_ERROR_INVAL; - - switch (mode) { - case USB_CHARGE_MODE_DISABLED: - usb_port_set_enabled(port_id, 0); - break; - case USB_CHARGE_MODE_ENABLED: - usb_port_set_enabled(port_id, 1); - break; - default: - return EC_ERROR_UNKNOWN; - } - - return EC_SUCCESS; -} - -static enum ec_status -usb_port_command_set_mode(struct host_cmd_handler_args *args) -{ - const struct ec_params_usb_charge_set_mode *p = args->params; - - if (usb_charge_set_mode(p->usb_port_id, p->mode, - p->inhibit_charge) != EC_SUCCESS) - return EC_RES_ERROR; - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_USB_CHARGE_SET_MODE, - usb_port_command_set_mode, - EC_VER_MASK(0)); - -/*****************************************************************************/ -/* Console commands */ - -static int command_set_mode(int argc, char **argv) -{ - int port_id = -1; - int mode = -1; - int i; - char *e; - - switch (argc) { - case 3: - port_id = strtoi(argv[1], &e, 0); - if (*e || port_id < 0 || port_id >= USB_PORT_COUNT) - return EC_ERROR_PARAM1; - - if (!parse_bool(argv[2], &mode)) - return EC_ERROR_PARAM2; - - usb_port_set_enabled(port_id, mode); - /* fallthrough */ - case 1: - for (i = 0; i < USB_PORT_COUNT; i++) - ccprintf("Port %d: %s\n", - i, charge_mode[i] ? "on" : "off"); - return EC_SUCCESS; - } - - return EC_ERROR_PARAM_COUNT; -} -DECLARE_CONSOLE_COMMAND(usbchargemode, command_set_mode, - "[<port> <on | off>]", - "Set USB charge mode"); - - -/*****************************************************************************/ -/* Hooks */ - -static void usb_port_preserve_state(void) -{ - system_add_jump_tag(USB_SYSJUMP_TAG, USB_HOOK_VERSION, - sizeof(charge_mode), charge_mode); -} -DECLARE_HOOK(HOOK_SYSJUMP, usb_port_preserve_state, HOOK_PRIO_DEFAULT); - -static void usb_port_init(void) -{ - const uint8_t *prev; - int version, size, i; - - prev = (const uint8_t *)system_get_jump_tag(USB_SYSJUMP_TAG, - &version, &size); - if (!prev || version != USB_HOOK_VERSION || - size != sizeof(charge_mode)) { - usb_port_all_ports_off(); - return; - } - - for (i = 0; i < USB_PORT_COUNT; i++) - usb_port_set_enabled(i, prev[i]); -} -DECLARE_HOOK(HOOK_INIT, usb_port_init, HOOK_PRIO_DEFAULT); - -static void usb_port_resume(void) -{ - /* Turn on USB ports on as we go into S0 from S3 or S5. */ - usb_port_all_ports_on(); -} -DECLARE_HOOK(HOOK_CHIPSET_RESUME, usb_port_resume, HOOK_PRIO_DEFAULT); - -static void usb_port_shutdown(void) -{ - /* Turn on USB ports off as we go back to S5. */ - usb_port_all_ports_off(); -} -DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, usb_port_shutdown, HOOK_PRIO_DEFAULT); diff --git a/common/usb_port_power_smart.c b/common/usb_port_power_smart.c deleted file mode 100644 index 59474ff7fe..0000000000 --- a/common/usb_port_power_smart.c +++ /dev/null @@ -1,263 +0,0 @@ -/* Copyright 2012 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. - */ - -/* USB charging control module for Chrome EC */ - -#include "chipset.h" -#include "common.h" -#include "console.h" -#include "gpio.h" -#include "hooks.h" -#include "host_command.h" -#include "system.h" -#include "usb_charge.h" -#include "util.h" - -#define CPUTS(outstr) cputs(CC_USBCHARGE, outstr) -#define CPRINTS(format, args...) cprints(CC_USBCHARGE, format, ## args) - -#define USB_SYSJUMP_TAG 0x5550 /* "UP" - Usb Port */ -#define USB_HOOK_VERSION 1 - -#ifndef CONFIG_USB_PORT_POWER_SMART_DEFAULT_MODE -#define CONFIG_USB_PORT_POWER_SMART_DEFAULT_MODE USB_CHARGE_MODE_SDP2 -#endif - -struct charge_mode_t { - uint8_t mode:7; - uint8_t inhibit_charging_in_suspend:1; -} __pack; - -static struct charge_mode_t charge_mode[CONFIG_USB_PORT_POWER_SMART_PORT_COUNT]; - -/* GPIOs to enable/disable USB ports. Board specific. */ -extern const int usb_port_enable[CONFIG_USB_PORT_POWER_SMART_PORT_COUNT]; - -#ifdef CONFIG_USB_PORT_POWER_SMART_CDP_SDP_ONLY -/* - * If we only support CDP and SDP, the control signals are hard-wired so - * there's nothing to do. The only to do is set ILIM_SEL. - */ -static void usb_charge_set_control_mode(int port_id, int mode) {} -#else /* !defined(CONFIG_USB_PORT_POWER_SMART_CDP_SDP_ONLY) */ -static void usb_charge_set_control_mode(int port_id, int mode) -{ -#ifdef CONFIG_USB_PORT_POWER_SMART_SIMPLE - /* - * One single shared control signal, so the last mode set to either - * port wins. Also, only CTL1 can be set; the other pins are - * hard-wired. - */ - gpio_or_ioex_set_level(GPIO_USB_CTL1, mode & 0x4); -#else - if (port_id == 0) { - gpio_or_ioex_set_level(GPIO_USB1_CTL1, mode & 0x4); - gpio_or_ioex_set_level(GPIO_USB1_CTL2, mode & 0x2); - gpio_or_ioex_set_level(GPIO_USB1_CTL3, mode & 0x1); - } else { - gpio_or_ioex_set_level(GPIO_USB2_CTL1, mode & 0x4); - gpio_or_ioex_set_level(GPIO_USB2_CTL2, mode & 0x2); - gpio_or_ioex_set_level(GPIO_USB2_CTL3, mode & 0x1); - } -#endif /* defined(CONFIG_USB_PORT_POWER_SMART_SIMPLE) */ -} -#endif /* defined(CONFIG_USB_PORT_POWER_SMART_CDP_SDP_ONLY) */ - -static void usb_charge_set_enabled(int port_id, int en) -{ - ASSERT(port_id < CONFIG_USB_PORT_POWER_SMART_PORT_COUNT); - gpio_or_ioex_set_level(usb_port_enable[port_id], en); -} - -static void usb_charge_set_ilim(int port_id, int sel) -{ - int ilim_sel; - -#if defined(CONFIG_USB_PORT_POWER_SMART_SIMPLE) || \ - defined(CONFIG_USB_PORT_POWER_SMART_INVERTED) - /* ILIM_SEL is inverted. */ - sel = !sel; -#endif - - ilim_sel = GPIO_USB1_ILIM_SEL; -#if !defined(CONFIG_USB_PORT_POWER_SMART_SIMPLE) && \ - CONFIG_USB_PORT_POWER_SMART_PORT_COUNT == 2 - if (port_id != 0) - ilim_sel = GPIO_USB2_ILIM_SEL; -#endif - - gpio_or_ioex_set_level(ilim_sel, sel); -} - -static void usb_charge_all_ports_ctrl(enum usb_charge_mode mode) -{ - int i; - - for (i = 0; i < CONFIG_USB_PORT_POWER_SMART_PORT_COUNT; i++) - usb_charge_set_mode(i, mode, USB_ALLOW_SUSPEND_CHARGE); -} - -int usb_charge_set_mode(int port_id, enum usb_charge_mode mode, - enum usb_suspend_charge inhibit_charge) -{ - CPRINTS("USB charge p%d m%d i%d", port_id, mode, inhibit_charge); - - if (port_id >= CONFIG_USB_PORT_POWER_SMART_PORT_COUNT) - return EC_ERROR_INVAL; - - if (mode == USB_CHARGE_MODE_DEFAULT) - mode = CONFIG_USB_PORT_POWER_SMART_DEFAULT_MODE; - - switch (mode) { - case USB_CHARGE_MODE_DISABLED: - usb_charge_set_enabled(port_id, 0); - break; - case USB_CHARGE_MODE_SDP2: - usb_charge_set_control_mode(port_id, 7); - usb_charge_set_ilim(port_id, 0); - usb_charge_set_enabled(port_id, 1); - break; - case USB_CHARGE_MODE_CDP: - usb_charge_set_control_mode(port_id, 7); - usb_charge_set_ilim(port_id, 1); - usb_charge_set_enabled(port_id, 1); - break; -#ifndef CONFIG_USB_PORT_POWER_SMART_CDP_SDP_ONLY - case USB_CHARGE_MODE_DCP_SHORT: - usb_charge_set_control_mode(port_id, 4); - usb_charge_set_enabled(port_id, 1); - break; -#endif /* !defined(CONFIG_USB_PORT_POWER_SMART_CDP_SDP_ONLY) */ - default: - return EC_ERROR_UNKNOWN; - } - - charge_mode[port_id].mode = mode; - charge_mode[port_id].inhibit_charging_in_suspend = inhibit_charge; - - return EC_SUCCESS; -} - -/*****************************************************************************/ -/* Console commands */ - -static int command_set_mode(int argc, char **argv) -{ - int port_id = -1; - int mode = -1, inhibit_charge = 0; - char *e; - int i; - - if (argc == 1) { - for (i = 0; i < CONFIG_USB_PORT_POWER_SMART_PORT_COUNT; i++) - ccprintf("Port %d: %d,%d\n", i, charge_mode[i].mode, - charge_mode[i].inhibit_charging_in_suspend); - return EC_SUCCESS; - } - - if (argc != 3 && argc != 4) - return EC_ERROR_PARAM_COUNT; - - port_id = strtoi(argv[1], &e, 0); - if (*e || port_id < 0 || - port_id >= CONFIG_USB_PORT_POWER_SMART_PORT_COUNT) - return EC_ERROR_PARAM1; - - mode = strtoi(argv[2], &e, 0); - if (*e || mode < 0 || mode >= USB_CHARGE_MODE_COUNT) - return EC_ERROR_PARAM2; - - if (argc == 4) { - inhibit_charge = strtoi(argv[3], &e, 0); - if (*e || (inhibit_charge != 0 && inhibit_charge != 1)) - return EC_ERROR_PARAM3; - } - - return usb_charge_set_mode(port_id, mode, inhibit_charge); -} -DECLARE_CONSOLE_COMMAND(usbchargemode, command_set_mode, - "[<port> <0 | 1 | 2 | 3> [<0 | 1>]]", - "Set USB charge mode"); - -/*****************************************************************************/ -/* Host commands */ - -static enum ec_status -usb_charge_command_set_mode(struct host_cmd_handler_args *args) -{ - const struct ec_params_usb_charge_set_mode *p = args->params; - - if (usb_charge_set_mode(p->usb_port_id, p->mode, - p->inhibit_charge) != EC_SUCCESS) - return EC_RES_ERROR; - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_USB_CHARGE_SET_MODE, - usb_charge_command_set_mode, - EC_VER_MASK(0)); - -/*****************************************************************************/ -/* Hooks */ - -static void usb_charge_preserve_state(void) -{ - system_add_jump_tag(USB_SYSJUMP_TAG, USB_HOOK_VERSION, - sizeof(charge_mode), charge_mode); -} -DECLARE_HOOK(HOOK_SYSJUMP, usb_charge_preserve_state, HOOK_PRIO_DEFAULT); - -static void usb_charge_init(void) -{ - const struct charge_mode_t *prev; - int version, size, i; - - prev = (const struct charge_mode_t *)system_get_jump_tag(USB_SYSJUMP_TAG, - &version, &size); - - if (!prev || version != USB_HOOK_VERSION || - size != sizeof(charge_mode)) { - usb_charge_all_ports_ctrl(USB_CHARGE_MODE_DISABLED); - return; - } - - for (i = 0; i < CONFIG_USB_PORT_POWER_SMART_PORT_COUNT; i++) - usb_charge_set_mode(i, prev[i].mode, - prev[i].inhibit_charging_in_suspend); -} -DECLARE_HOOK(HOOK_INIT, usb_charge_init, HOOK_PRIO_DEFAULT); - -static void usb_charge_resume(void) -{ - int i; - - /* Turn on USB ports on as we go into S0 from S3 or S5. */ - for (i = 0; i < CONFIG_USB_PORT_POWER_SMART_PORT_COUNT; i++) - usb_charge_set_mode(i, - CONFIG_USB_PORT_POWER_SMART_DEFAULT_MODE, - charge_mode[i].inhibit_charging_in_suspend); -} -DECLARE_HOOK(HOOK_CHIPSET_RESUME, usb_charge_resume, HOOK_PRIO_DEFAULT); - -static void usb_charge_suspend(void) -{ - int i; - - /* - * Inhibit charging during suspend if the inhibit_charging_in_suspend - * is set to 1. - */ - for (i = 0; i < CONFIG_USB_PORT_POWER_SMART_PORT_COUNT; i++) - if (charge_mode[i].inhibit_charging_in_suspend) - usb_charge_set_enabled(i, 0 /* disabled */); -} -DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, usb_charge_suspend, HOOK_PRIO_DEFAULT); - -static void usb_charge_shutdown(void) -{ - /* Turn on USB ports off as we go back to S5. */ - usb_charge_all_ports_ctrl(USB_CHARGE_MODE_DISABLED); -} -DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, usb_charge_shutdown, HOOK_PRIO_DEFAULT); diff --git a/common/usbc/build.mk b/common/usbc/build.mk deleted file mode 100644 index 69e540ed67..0000000000 --- a/common/usbc/build.mk +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright 2019 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. - -# Build for USB Type-C and Power Delivery - -# Note that this variable includes the trailing "/" -_usbc_dir:=$(dir $(lastword $(MAKEFILE_LIST))) - -ifneq ($(CONFIG_USB_SM_FRAMEWORK),) -all-obj-$(CONFIG_USB_SM_FRAMEWORK)+=$(_usbc_dir)usb_sm.o -all-obj-$(CONFIG_USB_TYPEC_SM)+=$(_usbc_dir)usbc_task.o -all-obj-$(CONFIG_USB_PRL_SM)+=$(_usbc_dir)usb_prl_sm.o -ifneq ($(CONFIG_USB_PE_SM),) -all-obj-$(CONFIG_USB_TYPEC_VPD)+=$(_usbc_dir)usb_pe_ctvpd_sm.o -all-obj-$(CONFIG_USB_TYPEC_CTVPD)+=$(_usbc_dir)usb_pe_ctvpd_sm.o -all-obj-$(CONFIG_USB_TYPEC_DRP_ACC_TRYSRC)+=$(_usbc_dir)usb_pe_drp_sm.o -all-obj-$(CONFIG_TEST_USB_PE_SM)+=$(_usbc_dir)usb_pe_drp_sm.o -endif -all-obj-$(CONFIG_USB_TYPEC_VPD)+=$(_usbc_dir)usb_tc_vpd_sm.o -all-obj-$(CONFIG_USB_TYPEC_CTVPD)+=$(_usbc_dir)usb_tc_ctvpd_sm.o -all-obj-$(CONFIG_USB_TYPEC_DRP_ACC_TRYSRC)+=\ - $(_usbc_dir)usb_tc_drp_acc_trysrc_sm.o -endif diff --git a/common/usbc/usb_pe_ctvpd_sm.c b/common/usbc/usb_pe_ctvpd_sm.c deleted file mode 100644 index 3e78ebcbb5..0000000000 --- a/common/usbc/usb_pe_ctvpd_sm.c +++ /dev/null @@ -1,217 +0,0 @@ -/* Copyright 2019 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 "common.h" -#include "console.h" -#include "task.h" -#include "util.h" -#include "usb_pd.h" -#include "usb_pd_tcpm.h" -#include "usb_pe_sm.h" -#include "usb_prl_sm.h" -#include "usb_tc_sm.h" -#include "usb_emsg.h" -#include "usb_sm.h" - -/* USB Policy Engine Charge-Through VCONN Powered Device module */ - -/* Policy Engine Flags */ -#define PE_FLAGS_MSG_RECEIVED BIT(0) - -/** - * This is the PE Port object that contains information needed to - * implement a VCONN and Charge-Through VCONN Powered Device. - */ -static struct policy_engine { - /* state machine context */ - struct sm_ctx ctx; - /* port flags, see PE_FLAGS_* */ - uint32_t flags; -} pe[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/* List of all policy-engine-level states */ -enum usb_pe_state { - PE_REQUEST, -}; - -/* Forward declare the full list of states. This is indexed by usb_pe_states */ -static const struct usb_state pe_states[]; - -static void set_state_pe(const int port, enum usb_pe_state new_state) -{ - set_state(port, &pe[port].ctx, &pe_states[new_state]); -} - -static void pe_init(int port) -{ - const struct sm_ctx cleared = {}; - - pe[port].flags = 0; - pe[port].ctx = cleared; - set_state_pe(port, PE_REQUEST); -} - -void pe_run(int port, int evt, int en) -{ - static enum sm_local_state local_state[CONFIG_USB_PD_PORT_MAX_COUNT]; - - switch (local_state[port]) { - case SM_PAUSED: - if (!en) - break; - /* fall through */ - case SM_INIT: - pe_init(port); - local_state[port] = SM_RUN; - /* fall through */ - case SM_RUN: - if (en) - run_state(port, &pe[port].ctx); - else - local_state[port] = SM_PAUSED; - break; - } -} - -void pe_message_received(int port) -{ - pe[port].flags |= PE_FLAGS_MSG_RECEIVED; - task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SM, 0); -} - -/** - * NOTE: - * The Charge-Through Vconn Powered Device's Policy Engine is very - * simple and no implementation is needed for the following functions - * that might be called by the Protocol Layer. - */ - -void pe_hard_reset_sent(int port) -{ - /* No implementation needed by this policy engine */ -} - -void pe_got_hard_reset(int port) -{ - /* No implementation needed by this policy engine */ -} - -void pe_report_error(int port, enum pe_error e) -{ - /* No implementation needed by this policy engine */ -} - -void pe_got_soft_reset(int port) -{ - /* No implementation needed by this policy engine */ -} - -void pe_message_sent(int port) -{ - /* No implementation needed by this policy engine */ -} - -static void pe_request_run(const int port) -{ - uint32_t *payload = (uint32_t *)emsg[port].buf; - uint32_t header = emsg[port].header; - uint32_t vdo = payload[0]; - - if (pe[port].flags & PE_FLAGS_MSG_RECEIVED) { - pe[port].flags &= ~PE_FLAGS_MSG_RECEIVED; - - /* - * Only support Structured VDM Discovery - * Identity message - */ - - if (PD_HEADER_TYPE(header) != PD_DATA_VENDOR_DEF) - return; - - if (PD_HEADER_CNT(header) == 0) - return; - - if (!PD_VDO_SVDM(vdo)) - return; - - if (PD_VDO_CMD(vdo) != CMD_DISCOVER_IDENT) - return; - -#ifdef CONFIG_USB_TYPEC_CTVPD - /* - * We have a valid DISCOVER IDENTITY message. - * Attempt to reset support timer - */ - tc_reset_support_timer(port); -#endif - /* Prepare to send ACK */ - - /* VDM Header */ - payload[0] = VDO( - USB_VID_GOOGLE, - 1, /* Structured VDM */ - VDO_SVDM_VERS(1) | - VDO_CMDT(CMDT_RSP_ACK) | - CMD_DISCOVER_IDENT); - - /* ID Header VDO */ - payload[1] = VDO_IDH( - 0, /* Not a USB Host */ - 1, /* Capable of being enumerated as USB Device */ - IDH_PTYPE_VPD, - 0, /* Modal Operation Not Supported */ - USB_VID_GOOGLE); - - /* Cert State VDO */ - payload[2] = 0; - - /* Product VDO */ - payload[3] = VDO_PRODUCT( - CONFIG_USB_PID, - USB_BCD_DEVICE); - - /* VPD VDO */ - payload[4] = VDO_VPD( - VPD_HW_VERSION, - VPD_FW_VERSION, - VPD_MAX_VBUS_20V, - VPD_VBUS_IMP(VPD_VBUS_IMPEDANCE), - VPD_GND_IMP(VPD_GND_IMPEDANCE), -#ifdef CONFIG_USB_TYPEC_CTVPD - VPD_CTS_SUPPORTED -#else - VPD_CTS_NOT_SUPPORTED -#endif - ); - - /* 20 bytes, 5 data objects */ - emsg[port].len = 20; - - /* Set to highest revision supported by both ports. */ - prl_set_rev(port, TCPC_TX_SOP_PRIME, - (PD_HEADER_REV(header) > PD_REV30) ? - PD_REV30 : PD_HEADER_REV(header)); - /* Send the ACK */ - prl_send_data_msg(port, TCPC_TX_SOP_PRIME, - PD_DATA_VENDOR_DEF); - } -} - -/* All policy-engine-level states. */ -static const struct usb_state pe_states[] = { - [PE_REQUEST] = { - .run = pe_request_run, - }, -}; - -#ifdef TEST_BUILD -const struct test_sm_data test_pe_sm_data[] = { - { - .base = pe_states, - .size = ARRAY_SIZE(pe_states), - }, -}; -const int test_pe_sm_data_size = ARRAY_SIZE(test_pe_sm_data); -#endif diff --git a/common/usbc/usb_pe_drp_sm.c b/common/usbc/usb_pe_drp_sm.c deleted file mode 100644 index cf9945abdc..0000000000 --- a/common/usbc/usb_pe_drp_sm.c +++ /dev/null @@ -1,5244 +0,0 @@ -/* Copyright 2019 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 "atomic.h" -#include "battery.h" -#include "battery_smart.h" -#include "charge_manager.h" -#include "charge_state.h" -#include "common.h" -#include "console.h" -#include "hooks.h" -#include "host_command.h" -#include "task.h" -#include "tcpm.h" -#include "util.h" -#include "usb_common.h" -#include "usb_pd.h" -#include "usb_pd_tcpm.h" -#include "usb_pe_sm.h" -#include "usb_prl_sm.h" -#include "usb_tc_sm.h" -#include "usb_emsg.h" -#include "usb_sm.h" -#include "usbc_ppc.h" - -/* - * USB Policy Engine Sink / Source module - * - * Based on Revision 3.0, Version 1.2 of - * the USB Power Delivery Specification. - */ - -#ifdef CONFIG_COMMON_RUNTIME -#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) -#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) -#endif - -#define PE_SET_FLAG(port, flag) atomic_or(&pe[port].flags, (flag)) -#define PE_CLR_FLAG(port, flag) atomic_clear(&pe[port].flags, (flag)) -#define PE_CHK_FLAG(port, flag) (pe[port].flags & (flag)) - -/* - * These macros SET, CLEAR, and CHECK, a DPM (Device Policy Manager) - * Request. The Requests are listed in usb_pe_sm.h. - */ -#define PE_SET_DPM_REQUEST(port, req) (pe[port].dpm_request |= (req)) -#define PE_CLR_DPM_REQUEST(port, req) (pe[port].dpm_request &= ~(req)) -#define PE_CHK_DPM_REQUEST(port, req) (pe[port].dpm_request & (req)) - -/* - * Policy Engine Layer Flags - */ - -/* At least one successful PD communication packet received from port partner */ -#define PE_FLAGS_PD_CONNECTION BIT(0) -/* Accept message received from port partner */ -#define PE_FLAGS_ACCEPT BIT(1) -/* Power Supply Ready message received from port partner */ -#define PE_FLAGS_PS_READY BIT(2) -/* Protocol Error was determined based on error recovery current state */ -#define PE_FLAGS_PROTOCOL_ERROR BIT(3) -/* Set if we are in Modal Operation */ -#define PE_FLAGS_MODAL_OPERATION BIT(4) -/* A message we requested to be sent has been transmitted */ -#define PE_FLAGS_TX_COMPLETE BIT(5) -/* A message sent by a port partner has been received */ -#define PE_FLAGS_MSG_RECEIVED BIT(6) -/* A hard reset has been requested but has not been sent, not currently used */ -#define PE_FLAGS_HARD_RESET_PENDING BIT(7) -/* Port partner sent a Wait message. Wait before we resend our message */ -#define PE_FLAGS_WAIT BIT(8) -/* An explicit contract is in place with our port partner */ -#define PE_FLAGS_EXPLICIT_CONTRACT BIT(9) -/* Waiting for Sink Capabailities timed out. Used for retry error handling */ -#define PE_FLAGS_SNK_WAIT_CAP_TIMEOUT BIT(10) -/* Power Supply voltage/current transition timed out */ -#define PE_FLAGS_PS_TRANSITION_TIMEOUT BIT(11) -/* Flag to note current Atomic Message Sequence is interruptible */ -#define PE_FLAGS_INTERRUPTIBLE_AMS BIT(12) -/* Flag to note Power Supply reset has completed */ -#define PE_FLAGS_PS_RESET_COMPLETE BIT(13) -/* Flag to note a Structured Vendor Defined Message should be sent */ -#define PE_FLAGS_SEND_SVDM BIT(14) -/* VCONN swap operation has completed */ -#define PE_FLAGS_VCONN_SWAP_COMPLETE BIT(15) -/* Flag to note no more discover identity messages should be sent */ -#define PE_FLAGS_DISCOVER_PORT_IDENTITY_DONE BIT(16) -/* Flag to note Swap Source Start timer should be set at PE_SRC_Startup entry */ -#define PE_FLAGS_RUN_SOURCE_START_TIMER BIT(17) -/* Flag to note Port Discovery port partner replied with BUSY */ -#define PE_FLAGS_VDM_REQUEST_BUSY BIT(18) -/* Flag to note Port Discovery port partner replied with NAK */ -#define PE_FLAGS_VDM_REQUEST_NAKED BIT(19) -/* Flag to note FRS/PRS context in shared state machine path */ -#define PE_FLAGS_FAST_ROLE_SWAP_PATH BIT(20) -/* Flag to note if FRS listening is enabled */ -#define PE_FLAGS_FAST_ROLE_SWAP_ENABLED BIT(21) -/* Flag to note TCPC passed on FRS signal from port partner */ -#define PE_FLAGS_FAST_ROLE_SWAP_SIGNALED BIT(22) -/* For PD2.0, triggers a DR SWAP from UFP to DFP before sending a DiscID msg */ -#define PE_FLAGS_DR_SWAP_TO_DFP BIT(23) -/* Flag to trigger a message resend after receiving a WAIT from port partner */ -#define PE_FLAGS_WAITING_DR_SWAP BIT(24) -/* FLAG to track if port partner is dualrole capable */ -#define PE_FLAGS_PORT_PARTNER_IS_DUALROLE BIT(25) - -/* 6.7.3 Hard Reset Counter */ -#define N_HARD_RESET_COUNT 2 - -/* 6.7.4 Capabilities Counter */ -#define N_CAPS_COUNT 25 - -/* 6.7.5 Discover Identity Counter */ -/* - * NOTE: The Protocol Layer tries to send a message 4 time before giving up, - * so a Discover Identity message will be sent 4*5 = 20 times. - */ -#define N_DISCOVER_IDENTITY_COUNT 5 -/* - * ChromeOS policy: - * For PD2.0, We must be DFP before sending Discover Identity message - * to the port partner. Attempt to DR SWAP from UFP to DFP - * N_DR_SWAP_ATTEMPT_COUNT times before giving up on sending a - * Discover Identity message. - */ -#define N_DR_SWAP_ATTEMPT_COUNT 5 - -#define TIMER_DISABLED 0xffffffffffffffff /* Unreachable time in future */ - -/* - * Function pointer to a Structured Vendor Defined Message (SVDM) response - * function defined in the board's usb_pd_policy.c file. - */ -typedef int (*svdm_rsp_func)(int port, uint32_t *payload); - -/* List of all Policy Engine level states */ -enum usb_pe_state { - /* Normal States */ - PE_SRC_STARTUP, - PE_SRC_DISCOVERY, - PE_SRC_SEND_CAPABILITIES, - PE_SRC_NEGOTIATE_CAPABILITY, - PE_SRC_TRANSITION_SUPPLY, - PE_SRC_READY, - PE_SRC_DISABLED, - PE_SRC_CAPABILITY_RESPONSE, - PE_SRC_HARD_RESET, - PE_SRC_HARD_RESET_RECEIVED, - PE_SRC_TRANSITION_TO_DEFAULT, - PE_SNK_STARTUP, - PE_SNK_DISCOVERY, - PE_SNK_WAIT_FOR_CAPABILITIES, - PE_SNK_EVALUATE_CAPABILITY, - PE_SNK_SELECT_CAPABILITY, - PE_SNK_READY, - PE_SNK_HARD_RESET, - PE_SNK_TRANSITION_TO_DEFAULT, - PE_SNK_GIVE_SINK_CAP, - PE_SNK_GET_SOURCE_CAP, - PE_SNK_TRANSITION_SINK, - PE_SEND_SOFT_RESET, - PE_SOFT_RESET, - PE_SEND_NOT_SUPPORTED, - PE_SRC_PING, - PE_GIVE_BATTERY_CAP, - PE_GIVE_BATTERY_STATUS, - PE_DRS_EVALUATE_SWAP, - PE_DRS_CHANGE, - PE_DRS_SEND_SWAP, - PE_PRS_SRC_SNK_EVALUATE_SWAP, - PE_PRS_SRC_SNK_TRANSITION_TO_OFF, - PE_PRS_SRC_SNK_WAIT_SOURCE_ON, - PE_PRS_SRC_SNK_SEND_SWAP, - PE_PRS_SNK_SRC_EVALUATE_SWAP, - PE_PRS_SNK_SRC_TRANSITION_TO_OFF, - PE_PRS_SNK_SRC_ASSERT_RP, - PE_PRS_SNK_SRC_SOURCE_ON, - PE_PRS_SNK_SRC_SEND_SWAP, - PE_FRS_SNK_SRC_START_AMS, - PE_VCS_EVALUATE_SWAP, - PE_VCS_SEND_SWAP, - PE_VCS_WAIT_FOR_VCONN_SWAP, - PE_VCS_TURN_ON_VCONN_SWAP, - PE_VCS_TURN_OFF_VCONN_SWAP, - PE_VCS_SEND_PS_RDY_SWAP, - PE_DO_PORT_DISCOVERY, - PE_VDM_REQUEST, - PE_VDM_ACKED, - PE_VDM_RESPONSE, - PE_HANDLE_CUSTOM_VDM_REQUEST, - PE_WAIT_FOR_ERROR_RECOVERY, - PE_BIST, - PE_DR_SNK_GET_SINK_CAP, - - /* Super States */ - PE_PRS_FRS_SHARED, -}; - -/* Forward declare the full list of states. This is indexed by usb_pe_state */ -static const struct usb_state pe_states[]; - -#ifdef CONFIG_COMMON_RUNTIME -/* List of human readable state names for console debugging */ -static const char * const pe_state_names[] = { - [PE_SRC_STARTUP] = "PE_SRC_Startup", - [PE_SRC_DISCOVERY] = "PE_SRC_Discovery", - [PE_SRC_SEND_CAPABILITIES] = "PE_SRC_Send_Capabilities", - [PE_SRC_NEGOTIATE_CAPABILITY] = "PE_SRC_Negotiate_Capability", - [PE_SRC_TRANSITION_SUPPLY] = "PE_SRC_Transition_Supply", - [PE_SRC_READY] = "PE_SRC_Ready", - [PE_SRC_DISABLED] = "PE_SRC_Disabled", - [PE_SRC_CAPABILITY_RESPONSE] = "PE_SRC_Capability_Response", - [PE_SRC_HARD_RESET] = "PE_SRC_Hard_Reset", - [PE_SRC_HARD_RESET_RECEIVED] = "PE_SRC_Hard_Reset_Received", - [PE_SRC_TRANSITION_TO_DEFAULT] = "PE_SRC_Transition_to_default", - [PE_SNK_STARTUP] = "PE_SNK_Startup", - [PE_SNK_DISCOVERY] = "PE_SNK_Discovery", - [PE_SNK_WAIT_FOR_CAPABILITIES] = "PE_SNK_Wait_for_Capabilities", - [PE_SNK_EVALUATE_CAPABILITY] = "PE_SNK_Evaluate_Capability", - [PE_SNK_SELECT_CAPABILITY] = "PE_SNK_Select_Capability", - [PE_SNK_READY] = "PE_SNK_Ready", - [PE_SNK_HARD_RESET] = "PE_SNK_Hard_Reset", - [PE_SNK_TRANSITION_TO_DEFAULT] = "PE_SNK_Transition_to_default", - [PE_SNK_GIVE_SINK_CAP] = "PE_SNK_Give_Sink_Cap", - [PE_SNK_GET_SOURCE_CAP] = "PE_SNK_Get_Source_Cap", - [PE_SNK_TRANSITION_SINK] = "PE_SNK_Transition_Sink", - [PE_SEND_SOFT_RESET] = "PE_Send_Soft_Reset", - [PE_SOFT_RESET] = "PE_Soft_Reset", - [PE_SEND_NOT_SUPPORTED] = "PE_Send_Not_Supported", - [PE_SRC_PING] = "PE_SRC_Ping", - [PE_GIVE_BATTERY_CAP] = "PE_Give_Battery_Cap", - [PE_GIVE_BATTERY_STATUS] = "PE_Give_Battery_Status", - [PE_DRS_EVALUATE_SWAP] = "PE_DRS_Evaluate_Swap", - [PE_DRS_CHANGE] = "PE_DRS_Change", - [PE_DRS_SEND_SWAP] = "PE_DRS_Send_Swap", - [PE_PRS_SRC_SNK_EVALUATE_SWAP] = "PE_PRS_SRC_SNK_Evaluate_Swap", - [PE_PRS_SRC_SNK_TRANSITION_TO_OFF] = "PE_PRS_SRC_SNK_Transition_To_Off", - [PE_PRS_SRC_SNK_WAIT_SOURCE_ON] = "PE_PRS_SRC_SNK_Wait_Source_On", - [PE_PRS_SRC_SNK_SEND_SWAP] = "PE_PRS_SRC_SNK_Send_Swap", - [PE_PRS_SNK_SRC_EVALUATE_SWAP] = "PE_PRS_SNK_SRC_Evaluate_Swap", - [PE_PRS_SNK_SRC_TRANSITION_TO_OFF] = "PE_PRS_SNK_SRC_Transition_To_Off", - [PE_PRS_SNK_SRC_ASSERT_RP] = "PE_PRS_SNK_SRC_Assert_Rp", - [PE_PRS_SNK_SRC_SOURCE_ON] = "PE_PRS_SNK_SRC_Source_On", - [PE_PRS_SNK_SRC_SEND_SWAP] = "PE_PRS_SNK_SRC_Send_Swap", - [PE_FRS_SNK_SRC_START_AMS] = "PE_FRS_SNK_SRC_Start_Ams", - [PE_VCS_EVALUATE_SWAP] = "PE_VCS_Evaluate_Swap", - [PE_VCS_SEND_SWAP] = "PE_VCS_Send_Swap", - [PE_VCS_WAIT_FOR_VCONN_SWAP] = "PE_VCS_Wait_For_Vconn_Swap", - [PE_VCS_TURN_ON_VCONN_SWAP] = "PE_VCS_Turn_On_Vconn_Swap", - [PE_VCS_TURN_OFF_VCONN_SWAP] = "PE_VCS_Turn_Off_Vconn_Swap", - [PE_VCS_SEND_PS_RDY_SWAP] = "PE_VCS_Send_Ps_Rdy_Swap", - [PE_DO_PORT_DISCOVERY] = "PE_Do_Port_Discovery", - [PE_VDM_REQUEST] = "PE_VDM_Request", - [PE_VDM_ACKED] = "PE_VDM_Acked", - [PE_VDM_RESPONSE] = "PE_VDM_Response", - [PE_HANDLE_CUSTOM_VDM_REQUEST] = "PE_Handle_Custom_Vdm_Request", - [PE_WAIT_FOR_ERROR_RECOVERY] = "PE_Wait_For_Error_Recovery", - [PE_BIST] = "PE_Bist", - [PE_DR_SNK_GET_SINK_CAP] = "PE_DR_SNK_Get_Sink_Cap", -}; -#endif - -/* - * NOTE: - * DO_PORT_DISCOVERY_START is not actually a vdm command. It is used - * to start the port partner discovery proccess. - */ -enum vdm_cmd { - DO_PORT_DISCOVERY_START, - DISCOVER_IDENTITY, - DISCOVER_SVIDS, - DISCOVER_MODES, - ENTER_MODE, - EXIT_MODE, - ATTENTION, -}; - -enum port_partner { - PORT, - CABLE, -}; - -/* - * This enum is used to implement a state machine consisting of at most - * 3 states, inside a Policy Engine State. - */ -enum sub_state { - PE_SUB0, - PE_SUB1, - PE_SUB2 -}; - -static enum sm_local_state local_state[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/* - * Policy Engine State Machine Object - */ -static struct policy_engine { - /* state machine context */ - struct sm_ctx ctx; - /* current port power role (SOURCE or SINK) */ - enum pd_power_role power_role; - /* current port data role (DFP or UFP) */ - enum pd_data_role data_role; - /* saved data and power roles while communicating with a cable plug */ - enum pd_data_role saved_data_role; - enum pd_power_role saved_power_role; - /* state machine flags */ - uint32_t flags; - /* Device Policy Manager Request */ - uint32_t dpm_request; - /* state timeout timer */ - uint64_t timeout; - /* last requested voltage PDO index */ - int requested_idx; - - /* Current limit / voltage based on the last request message */ - uint32_t curr_limit; - uint32_t supply_voltage; - - /* state specific state machine variable */ - enum sub_state sub; - - /* VDO */ - - /* PD_VDO_INVALID is used when there is an invalid VDO */ - int32_t active_cable_vdo1; - int32_t active_cable_vdo2; - int32_t passive_cable_vdo; - int32_t ama_vdo; - int32_t vpd_vdo; - /* alternate mode policy*/ - struct pd_policy am_policy; - - /* VDM */ - enum port_partner partner_type; - uint32_t vdm_cmd; - uint32_t vdm_cnt; - uint32_t vdm_data[VDO_HDR_SIZE + VDO_MAX_SIZE]; - - /* Timers */ - - /* - * The NoResponseTimer is used by the Policy Engine in a Source - * to determine that its Port Partner is not responding after a - * Hard Reset. - */ - uint64_t no_response_timer; - - /* - * Prior to a successful negotiation, a Source Shall use the - * SourceCapabilityTimer to periodically send out a - * Source_Capabilities Message. - */ - uint64_t source_cap_timer; - - /* - * This timer is started when a request for a new Capability has been - * accepted and will timeout after PD_T_PS_TRANSITION if a PS_RDY - * Message has not been received. - */ - uint64_t ps_transition_timer; - - /* - * This timer is used to ensure that a Message requesting a response - * (e.g. Get_Source_Cap Message) is responded to within a bounded time - * of PD_T_SENDER_RESPONSE. - */ - uint64_t sender_response_timer; - - /* - * This timer is used during an Explicit Contract when discovering - * whether a Port Partner is PD Capable using SOP. - */ - uint64_t discover_port_identity_timer; - - /* - * This timer is used in a Source to ensure that the Sink has had - * sufficient time to process Hard Reset Signaling before turning - * off its power supply to VBUS. - */ - uint64_t ps_hard_reset_timer; - - /* - * This timer is used to ensure that the time before the next Sink - * Request Message, after a Wait Message has been received from the - * Source in response to a Sink Request Message. - */ - uint64_t sink_request_timer; - - /* - * This timer combines the PSSourceOffTimer and PSSourceOnTimer timers. - * For PSSourceOffTimer, when this DRP device is currently acting as a - * Sink, this timer times out on a PS_RDY Message during a Power Role - * Swap sequence. - * - * For PSSourceOnTimer, when this DRP device is currently acting as a - * Source that has just stopped sourcing power and is waiting to start - * sinking power to timeout on a PS_RDY Message during a Power Role - * Swap. - */ - uint64_t ps_source_timer; - - /* - * This timer is used by a UUT to ensure that a Continuous BIST Mode - * (i.e. BIST Carrier Mode) is exited in a timely fashion. - */ - uint64_t bist_cont_mode_timer; - - /* - * This timer is used by the new Source, after a Power Role Swap or - * Fast Role Swap, to ensure that it does not send Source_Capabilities - * Message before the new Sink is ready to receive the - * Source_Capabilities Message. - */ - uint64_t swap_source_start_timer; - - /* - * This timer is used by the Initiator’s Policy Engine to ensure that - * a Structured VDM Command request needing a response (e.g. Discover - * Identity Command request) is responded to within a bounded time of - * tVDMSenderResponse. - */ - uint64_t vdm_response_timer; - - /* - * This timer is used during a VCONN Swap. - */ - uint64_t vconn_on_timer; - - /* Counters */ - - /* - * This counter is used to retry the Hard Reset whenever there is no - * response from the remote device. - */ - uint32_t hard_reset_counter; - - /* - * This counter is used to count the number of Source_Capabilities - * Messages which have been sent by a Source at power up or after a - * Hard Reset. - */ - uint32_t caps_counter; - - /* - * This counter maintains a count of Discover Identity Messages sent - * to a port partner. - */ - uint32_t discover_port_identity_counter; - /* - * For PD2.0, we need to be a DFP before sending a discovery identity - * messaage to our port partner. This counter keeps track of how - * many attempts to DR SWAP from UFP to DFP. - */ - uint32_t dr_swap_attempt_counter; - - /* Last received source cap */ - uint32_t src_caps[PDO_MAX_OBJECTS]; - int src_cap_cnt; - -} pe[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/* - * As a sink, this is the max voltage (in millivolts) we can request - * before getting source caps - */ -static unsigned int max_request_mv = PD_MAX_VOLTAGE_MV; - -/* - * Private VDM utility functions - */ -#ifdef CONFIG_USB_PD_ALT_MODE_DFP -static int validate_mode_request(struct svdm_amode_data *modep, - uint16_t svid, int opos); -static void dfp_consume_attention(int port, uint32_t *payload); -static void dfp_consume_identity(int port, int cnt, uint32_t *payload); -static void dfp_consume_svids(int port, int cnt, uint32_t *payload); -static int dfp_discover_modes(int port, uint32_t *payload); -static void dfp_consume_modes(int port, int cnt, uint32_t *payload); -static int get_mode_idx(int port, uint16_t svid); -static struct svdm_amode_data *get_modep(int port, uint16_t svid); -#endif - -test_export_static enum usb_pe_state get_state_pe(const int port); -test_export_static void set_state_pe(const int port, - const enum usb_pe_state new_state); - -static void pe_init(int port) -{ - pe[port].flags = 0; - pe[port].dpm_request = 0; - pe[port].source_cap_timer = TIMER_DISABLED; - pe[port].no_response_timer = TIMER_DISABLED; - pe[port].data_role = tc_get_data_role(port); - - tc_pd_connection(port, 0); - - if (tc_get_power_role(port) == PD_ROLE_SOURCE) - set_state_pe(port, PE_SRC_STARTUP); - else - set_state_pe(port, PE_SNK_STARTUP); -} - -int pe_is_running(int port) -{ - return local_state[port] == SM_RUN; -} - -void pe_run(int port, int evt, int en) -{ - switch (local_state[port]) { - case SM_PAUSED: - if (!en) - break; - /* fall through */ - case SM_INIT: - pe_init(port); - local_state[port] = SM_RUN; - /* fall through */ - case SM_RUN: - if (!en) { - local_state[port] = SM_PAUSED; - /* - * While we are paused, exit all states and wait until - * initialized again. - */ - set_state(port, &pe[port].ctx, NULL); - break; - } - - /* - * Check for Fast Role Swap signal - * This is not a typical pattern for adding state changes. - * I added this here because FRS SIGNALED can happen at any - * state once we are listening for the signal and we want to - * make sure to handle it immediately. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_SIGNALED)) { - PE_CLR_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_SIGNALED); - set_state_pe(port, PE_FRS_SNK_SRC_START_AMS); - } - - /* Run state machine */ - run_state(port, &pe[port].ctx); - break; - } -} - -int pe_is_explicit_contract(int port) -{ - return PE_CHK_FLAG(port, PE_FLAGS_EXPLICIT_CONTRACT); -} - -void pe_message_received(int port) -{ - /* This should only be called from the PD task */ - assert(port == TASK_ID_TO_PD_PORT(task_get_current())); - - PE_SET_FLAG(port, PE_FLAGS_MSG_RECEIVED); -} - -void pe_hard_reset_sent(int port) -{ - /* This should only be called from the PD task */ - assert(port == TASK_ID_TO_PD_PORT(task_get_current())); - - PE_CLR_FLAG(port, PE_FLAGS_HARD_RESET_PENDING); -} - -void pe_got_hard_reset(int port) -{ - /* This should only be called from the PD task */ - assert(port == TASK_ID_TO_PD_PORT(task_get_current())); - - /* - * Transition from any state to the PE_SRC_Hard_Reset_Received or - * PE_SNK_Transition_to_default state when: - * 1) Hard Reset Signaling is detected. - */ - pe[port].power_role = tc_get_power_role(port); - - if (pe[port].power_role == PD_ROLE_SOURCE) - set_state_pe(port, PE_SRC_HARD_RESET_RECEIVED); - else - set_state_pe(port, PE_SNK_TRANSITION_TO_DEFAULT); -} - -/* - * pe_got_frs_signal - * - * Called by the handler that detects the FRS signal in order to - * switch PE states to complete the FRS that the hardware has - * started. - */ -void pe_got_frs_signal(int port) -{ - PE_SET_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_SIGNALED); -} - -/* - * PE_Set_FRS_Enable - * - * This function should be called every time an explicit contract - * is disabled, to disable FRS. - * - * Enabling an explicit contract is not enough to enable FRS, it - * also requires a Sink Capability power requirement from a Source - * that supports FRS so we can determine if this is something we - * can handle. - */ -static void pe_set_frs_enable(int port, int enable) -{ - /* This should only be called from the PD task */ - assert(port == TASK_ID_TO_PD_PORT(task_get_current())); - - if (IS_ENABLED(CONFIG_USB_TYPEC_PD_FAST_ROLE_SWAP)) { - int current = PE_CHK_FLAG(port, - PE_FLAGS_FAST_ROLE_SWAP_ENABLED); - - /* Request an FRS change, only if the state has changed */ - if (!!current != !!enable) { - tcpm_set_frs_enable(port, enable); - - if (enable) - PE_SET_FLAG(port, - PE_FLAGS_FAST_ROLE_SWAP_ENABLED); - else - PE_CLR_FLAG(port, - PE_FLAGS_FAST_ROLE_SWAP_ENABLED); - } - } -} - -static void pe_invalidate_explicit_contract(int port) -{ - pe_set_frs_enable(port, 0); - PE_CLR_FLAG(port, PE_FLAGS_EXPLICIT_CONTRACT); -} - -void pe_report_error(int port, enum pe_error e) -{ - /* This should only be called from the PD task */ - assert(port == TASK_ID_TO_PD_PORT(task_get_current())); - - /* - * Generate Hard Reset if Protocol Error occurred - * while in PE_Send_Soft_Reset state. - */ - if (get_state_pe(port) == PE_SEND_SOFT_RESET) { - if (pe[port].power_role == PD_ROLE_SINK) - set_state_pe(port, PE_SNK_HARD_RESET); - else - set_state_pe(port, PE_SRC_HARD_RESET); - return; - } - - if (get_state_pe(port) == PE_SRC_SEND_CAPABILITIES || - get_state_pe(port) == PE_SRC_TRANSITION_SUPPLY || - get_state_pe(port) == PE_PRS_SRC_SNK_WAIT_SOURCE_ON || - get_state_pe(port) == PE_SRC_DISABLED || - get_state_pe(port) == PE_SRC_DISCOVERY || - get_state_pe(port) == PE_VDM_REQUEST) { - PE_SET_FLAG(port, PE_FLAGS_PROTOCOL_ERROR); - return; - } - - /* - * See section 8.3.3.4.1.1 PE_SRC_Send_Soft_Reset State: - * - * The PE_Send_Soft_Reset state shall be entered from - * any state when a Protocol Error is detected by - * Protocol Layer during a Non-Interruptible AMS or when - * Message has not been sent after retries. When an explicit - * contract is not in effect. Otherwise go to PE_Snk/Src_Ready - */ - if (!PE_CHK_FLAG(port, PE_FLAGS_EXPLICIT_CONTRACT) && - (!PE_CHK_FLAG(port, PE_FLAGS_INTERRUPTIBLE_AMS) - || (e == ERR_TCH_XMIT))) { - set_state_pe(port, PE_SEND_SOFT_RESET); - } - /* - * Transition to PE_Snk_Ready or PE_Src_Ready by a Protocol - * Error during an Interruptible AMS. - */ - else { - PE_SET_FLAG(port, PE_FLAGS_PROTOCOL_ERROR); - if (pe[port].power_role == PD_ROLE_SINK) - set_state_pe(port, PE_SNK_READY); - else - set_state_pe(port, PE_SRC_READY); - } -} - -void pe_got_soft_reset(int port) -{ - /* This should only be called from the PD task */ - assert(port == TASK_ID_TO_PD_PORT(task_get_current())); - - /* - * The PE_SRC_Soft_Reset state Shall be entered from any state when a - * Soft_Reset Message is received from the Protocol Layer. - */ - set_state_pe(port, PE_SOFT_RESET); -} - -void pe_dpm_request(int port, enum pe_dpm_request req) -{ - if (get_state_pe(port) == PE_SRC_READY || - get_state_pe(port) == PE_SNK_READY) - PE_SET_DPM_REQUEST(port, req); -} - -void pe_vconn_swap_complete(int port) -{ - /* This should only be called from the PD task */ - assert(port == TASK_ID_TO_PD_PORT(task_get_current())); - - PE_SET_FLAG(port, PE_FLAGS_VCONN_SWAP_COMPLETE); -} - -void pe_ps_reset_complete(int port) -{ - /* This should only be called from the PD task */ - assert(port == TASK_ID_TO_PD_PORT(task_get_current())); - - PE_SET_FLAG(port, PE_FLAGS_PS_RESET_COMPLETE); -} - -void pe_message_sent(int port) -{ - /* This should only be called from the PD task */ - assert(port == TASK_ID_TO_PD_PORT(task_get_current())); - - PE_SET_FLAG(port, PE_FLAGS_TX_COMPLETE); -} - -void pe_send_vdm(int port, uint32_t vid, int cmd, const uint32_t *data, - int count) -{ - pe[port].partner_type = PORT; - - /* Copy VDM Header */ - pe[port].vdm_data[0] = VDO(vid, ((vid & USB_SID_PD) == USB_SID_PD) ? - 1 : (PD_VDO_CMD(cmd) <= CMD_ATTENTION), - VDO_SVDM_VERS(1) | cmd); - - /* Copy Data after VDM Header */ - memcpy((pe[port].vdm_data + 1), data, count); - - pe[port].vdm_cnt = count + 1; - - PE_SET_FLAG(port, PE_FLAGS_SEND_SVDM); - task_wake(PD_PORT_TO_TASK_ID(port)); -} - -void pe_exit_dp_mode(int port) -{ - /* This should only be called from the PD task */ - assert(port == TASK_ID_TO_PD_PORT(task_get_current())); - - if (IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) { - int opos = pd_alt_mode(port, USB_SID_DISPLAYPORT); - - if (opos <= 0) - return; - - CPRINTS("C%d Exiting DP mode", port); - if (!pd_dfp_exit_mode(port, USB_SID_DISPLAYPORT, opos)) - return; - - pe_send_vdm(port, USB_SID_DISPLAYPORT, - CMD_EXIT_MODE | VDO_OPOS(opos), NULL, 0); - } -} - -/* - * Private functions - */ - -/* Set the TypeC state machine to a new state. */ -test_export_static void set_state_pe(const int port, - const enum usb_pe_state new_state) -{ - set_state(port, &pe[port].ctx, &pe_states[new_state]); -} - -/* Get the current TypeC state. */ -test_export_static enum usb_pe_state get_state_pe(const int port) -{ - return pe[port].ctx.current - &pe_states[0]; -} - -/* Get the previous TypeC state. */ -static enum usb_pe_state get_last_state_pe(const int port) -{ - return pe[port].ctx.previous - &pe_states[0]; -} - -static void print_current_state(const int port) -{ - const char *mode = ""; - - if (PE_CHK_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_PATH)) - mode = " FRS-MODE"; - - CPRINTS("C%d: %s%s", port, pe_state_names[get_state_pe(port)], mode); -} - -static void send_source_cap(int port) -{ -#if defined(CONFIG_USB_PD_DYNAMIC_SRC_CAP) || \ - defined(CONFIG_USB_PD_MAX_SINGLE_SOURCE_CURRENT) - const uint32_t *src_pdo; - const int src_pdo_cnt = charge_manager_get_source_pdo(&src_pdo, port); -#else - const uint32_t *src_pdo = pd_src_pdo; - const int src_pdo_cnt = pd_src_pdo_cnt; -#endif - - if (src_pdo_cnt == 0) { - /* No source capabilities defined, sink only */ - prl_send_ctrl_msg(port, TCPC_TX_SOP, PD_CTRL_REJECT); - } - - emsg[port].len = src_pdo_cnt * 4; - memcpy(emsg[port].buf, (uint8_t *)src_pdo, emsg[port].len); - - prl_send_data_msg(port, TCPC_TX_SOP, PD_DATA_SOURCE_CAP); -} - -/* - * Request desired charge voltage from source. - */ -static void pe_send_request_msg(int port) -{ - uint32_t rdo; - uint32_t curr_limit; - uint32_t supply_voltage; - int charging; - int max_request_allowed; - - if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) - charging = (charge_manager_get_active_charge_port() == port); - else - charging = 1; - - if (IS_ENABLED(CONFIG_USB_PD_CHECK_MAX_REQUEST_ALLOWED)) - max_request_allowed = pd_is_max_request_allowed(); - else - max_request_allowed = 1; - - /* Build and send request RDO */ - /* - * If this port is not actively charging or we are not allowed to - * request the max voltage, then select vSafe5V - */ - pd_build_request(pe[port].src_cap_cnt, pe[port].src_caps, - pe[port].vpd_vdo, &rdo, &curr_limit, - &supply_voltage, charging && max_request_allowed ? - PD_REQUEST_MAX : PD_REQUEST_VSAFE5V, max_request_mv); - - CPRINTF("C%d Req [%d] %dmV %dmA", port, RDO_POS(rdo), - supply_voltage, curr_limit); - if (rdo & RDO_CAP_MISMATCH) - CPRINTF(" Mismatch"); - CPRINTF("\n"); - - pe[port].curr_limit = curr_limit; - pe[port].supply_voltage = supply_voltage; - - emsg[port].len = 4; - - memcpy(emsg[port].buf, (uint8_t *)&rdo, emsg[port].len); - prl_send_data_msg(port, TCPC_TX_SOP, PD_DATA_REQUEST); -} - -static void pe_update_pdo_flags(int port, uint32_t pdo) -{ -#ifdef CONFIG_CHARGE_MANAGER -#ifdef CONFIG_USB_PD_ALT_MODE_DFP - int charge_allowlisted = - (tc_get_power_role(port) == PD_ROLE_SINK && - pd_charge_from_device(pd_get_identity_vid(port), - pd_get_identity_pid(port))); -#else - const int charge_allowlisted = 0; -#endif -#endif - - /* can only parse PDO flags if type is fixed */ - if ((pdo & PDO_TYPE_MASK) != PDO_TYPE_FIXED) - return; - - if (pdo & PDO_FIXED_DUAL_ROLE) - tc_partner_dr_power(port, 1); - else - tc_partner_dr_power(port, 0); - - if (pdo & PDO_FIXED_EXTERNAL) - tc_partner_extpower(port, 1); - else - tc_partner_extpower(port, 0); - - if (pdo & PDO_FIXED_COMM_CAP) - tc_partner_usb_comm(port, 1); - else - tc_partner_usb_comm(port, 0); - - if (pdo & PDO_FIXED_DATA_SWAP) - tc_partner_dr_data(port, 1); - else - tc_partner_dr_data(port, 0); - -#ifdef CONFIG_CHARGE_MANAGER - /* - * Treat device as a dedicated charger (meaning we should charge - * from it) if it does not support power swap, or if it is externally - * powered, or if we are a sink and the device identity matches a - * charging allowlist. - */ - if (!(pdo & PDO_FIXED_DUAL_ROLE) || (pdo & PDO_FIXED_EXTERNAL) || - charge_allowlisted) { - PE_CLR_FLAG(port, PE_FLAGS_PORT_PARTNER_IS_DUALROLE); - charge_manager_update_dualrole(port, CAP_DEDICATED); - } else { - PE_SET_FLAG(port, PE_FLAGS_PORT_PARTNER_IS_DUALROLE); - charge_manager_update_dualrole(port, CAP_DUALROLE); - } -#endif -} - -int pd_is_port_partner_dualrole(int port) -{ - return PE_CHK_FLAG(port, PE_FLAGS_PORT_PARTNER_IS_DUALROLE); -} - -int pd_board_check_request(uint32_t rdo, int pdo_cnt) -{ - int idx = RDO_POS(rdo); - - /* Check for invalid index */ - return (!idx || idx > pdo_cnt) ? - EC_ERROR_INVAL : EC_SUCCESS; -} - -static void pe_prl_execute_hard_reset(int port) -{ - prl_execute_hard_reset(port); -} - -/* - * This function must only be called from the PE_SNK_READY entry and - * PE_SRC_READY entry State. - */ -static void pe_attempt_port_discovery(int port) -{ - if (!PE_CHK_FLAG(port, PE_FLAGS_MODAL_OPERATION | - PE_FLAGS_DISCOVER_PORT_IDENTITY_DONE) && - pe[port].discover_port_identity_counter <= - N_DISCOVER_IDENTITY_COUNT) { - /* - * If we are operating as PD2.0 version, make sure we are - * DFP before sending Discover Identity message. - */ - if (prl_get_rev(port, TCPC_TX_SOP) == PD_REV20 && - pe[port].data_role == PD_ROLE_UFP) { - /* - * If we are UFP and DR SWAP fails - * N_DR_SWAP_ATTEMPT_COUNT number of times, give up - * port discovery. Also give up if the Port Partner - * rejected the DR_SWAP. - */ - if ((pe[port].dr_swap_attempt_counter >= - N_DR_SWAP_ATTEMPT_COUNT) || - (pe[port].dr_swap_attempt_counter > 0 && - !PE_CHK_FLAG(port, PE_FLAGS_WAITING_DR_SWAP))) { - PE_SET_FLAG(port, - PE_FLAGS_DISCOVER_PORT_IDENTITY_DONE); - pe[port].discover_port_identity_timer = - TIMER_DISABLED; - } else { - PE_SET_FLAG(port, PE_FLAGS_DR_SWAP_TO_DFP); - pe[port].discover_port_identity_timer = - get_time().val + PD_T_DISCOVER_IDENTITY; - } - } else { - pe[port].discover_port_identity_timer = - get_time().val + PD_T_DISCOVER_IDENTITY; - } - } else { - PE_SET_FLAG(port, PE_FLAGS_DISCOVER_PORT_IDENTITY_DONE); - pe[port].discover_port_identity_timer = TIMER_DISABLED; - } - - /* - * For PD2.0, add some jitter of up to 100ms before sending a message. - * Some devices are chatty once we reach the SRC_READY state and we may - * end up in a collision of messages if we try to immediately send our - * interrogations. - */ - if (prl_get_rev(port, TCPC_TX_SOP) == PD_REV20) { - if (pe[port].discover_port_identity_timer != TIMER_DISABLED) - pe[port].discover_port_identity_timer += - (get_time().le.lo % (100 * MSEC)); - } - - /* Clear the PE_FLAGS_WAITING_DR_SWAP flag if it was set. */ - PE_CLR_FLAG(port, PE_FLAGS_WAITING_DR_SWAP); -} - -/* - * This function must only be called from the PE_SNK_READY run and - * PE_SRC_READY run State. - */ -static void pe_start_port_discovery(int port) -{ - if (PE_CHK_FLAG(port, PE_FLAGS_DR_SWAP_TO_DFP)) { - PE_CLR_FLAG(port, PE_FLAGS_DR_SWAP_TO_DFP); - pe[port].dr_swap_attempt_counter++; - set_state_pe(port, PE_DRS_SEND_SWAP); - } else { - pe[port].discover_port_identity_counter++; - pe[port].vdm_cmd = DO_PORT_DISCOVERY_START; - PE_CLR_FLAG(port, PE_FLAGS_VDM_REQUEST_NAKED | - PE_FLAGS_VDM_REQUEST_BUSY); - set_state_pe(port, PE_DO_PORT_DISCOVERY); - } -} - -/** - * PE_SRC_Startup - */ -static void pe_src_startup_entry(int port) -{ - print_current_state(port); - - /* Initialize VDOs to default values */ - pe[port].active_cable_vdo1 = PD_VDO_INVALID; - pe[port].active_cable_vdo2 = PD_VDO_INVALID; - pe[port].passive_cable_vdo = PD_VDO_INVALID; - pe[port].ama_vdo = PD_VDO_INVALID; - pe[port].vpd_vdo = PD_VDO_INVALID; - - /* Reset CapsCounter */ - pe[port].caps_counter = 0; - - /* Reset the protocol layer */ - prl_reset(port); - - /* Set initial data role */ - pe[port].data_role = tc_get_data_role(port); - - /* Set initial power role */ - pe[port].power_role = PD_ROLE_SOURCE; - - /* Clear explicit contract. */ - pe_invalidate_explicit_contract(port); - - /* Clear port discovery flags */ - PE_CLR_FLAG(port, PE_FLAGS_DISCOVER_PORT_IDENTITY_DONE); - pe[port].discover_port_identity_counter = 0; - - /* Reset dr swap attempt counter */ - pe[port].dr_swap_attempt_counter = 0; - - if (PE_CHK_FLAG(port, PE_FLAGS_RUN_SOURCE_START_TIMER)) { - PE_CLR_FLAG(port, PE_FLAGS_RUN_SOURCE_START_TIMER); - - /* Start SwapSourceStartTimer */ - pe[port].swap_source_start_timer = get_time().val + - PD_T_SWAP_SOURCE_START; - } else { - /* - * SwapSourceStartTimer delay is not needed, so trigger now. - * We can't use set_state_pe here, since we need to ensure that - * the protocol layer is running again (done in run function). - */ - pe[port].swap_source_start_timer = get_time().val; - } -} - -static void pe_src_startup_run(int port) -{ - /* Wait until protocol layer is running */ - if (!prl_is_running(port)) - return; - - if (get_time().val > pe[port].swap_source_start_timer) - set_state_pe(port, PE_SRC_SEND_CAPABILITIES); -} - -/** - * PE_SRC_Discovery - */ -static void pe_src_discovery_entry(int port) -{ - print_current_state(port); - - /* - * Initialize and run the SourceCapabilityTimer in order - * to trigger sending a Source_Capabilities Message. - * - * The SourceCapabilityTimer Shall continue to run during - * identity discover and Shall Not be initialized on re-entry - * to PE_SRC_Discovery. - */ - if (get_last_state_pe(port) != PE_VDM_REQUEST) - pe[port].source_cap_timer = - get_time().val + PD_T_SEND_SOURCE_CAP; -} - -static void pe_src_discovery_run(int port) -{ - /* - * Transition to the PE_SRC_Send_Capabilities state when: - * 1) The SourceCapabilityTimer times out and - * CapsCounter ≤ nCapsCount. - * - * Transition to the PE_SRC_Disabled state when: - * 1) The Port Partners are not presently PD Connected - * 2) And the SourceCapabilityTimer times out - * 3) And CapsCounter > nCapsCount. - */ - if (get_time().val > pe[port].source_cap_timer) { - if (pe[port].caps_counter <= N_CAPS_COUNT) { - set_state_pe(port, PE_SRC_SEND_CAPABILITIES); - return; - } else if (!PE_CHK_FLAG(port, PE_FLAGS_PD_CONNECTION)) { - set_state_pe(port, PE_SRC_DISABLED); - return; - } - } - - /* - * Transition to the PE_SRC_Disabled state when: - * 1) The Port Partners have not been PD Connected. - * 2) And the NoResponseTimer times out. - * 3) And the HardResetCounter > nHardResetCount. - */ - if (!PE_CHK_FLAG(port, PE_FLAGS_PD_CONNECTION) && - get_time().val > pe[port].no_response_timer && - pe[port].hard_reset_counter > N_HARD_RESET_COUNT) { - set_state_pe(port, PE_SRC_DISABLED); - return; - } -} - -/** - * PE_SRC_Send_Capabilities - */ -static void pe_src_send_capabilities_entry(int port) -{ - print_current_state(port); - - /* Send PD Capabilities message */ - send_source_cap(port); - - /* Increment CapsCounter */ - pe[port].caps_counter++; - - /* Stop sender response timer */ - pe[port].sender_response_timer = TIMER_DISABLED; - - /* - * Clear PE_FLAGS_INTERRUPTIBLE_AMS flag if it was set - * in the src_discovery state - */ - PE_CLR_FLAG(port, PE_FLAGS_INTERRUPTIBLE_AMS); -} - -static void pe_src_send_capabilities_run(int port) -{ - /* - * If a GoodCRC Message is received then the Policy Engine Shall: - * 1) Stop the NoResponseTimer. - * 2) Reset the HardResetCounter and CapsCounter to zero. - * 3) Initialize and run the SenderResponseTimer. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE) && - pe[port].sender_response_timer == TIMER_DISABLED) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - - /* Stop the NoResponseTimer */ - pe[port].no_response_timer = TIMER_DISABLED; - - /* Reset the HardResetCounter to zero */ - pe[port].hard_reset_counter = 0; - - /* Reset the CapsCounter to zero */ - pe[port].caps_counter = 0; - - /* Initialize and run the SenderResponseTimer */ - pe[port].sender_response_timer = get_time().val + - PD_T_SENDER_RESPONSE; - } - - /* - * Transition to the PE_SRC_Negotiate_Capability state when: - * 1) A Request Message is received from the Sink - */ - if (pe[port].sender_response_timer != TIMER_DISABLED && - PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - /* - * Request Message Received? - */ - if (PD_HEADER_CNT(emsg[port].header) > 0 && - PD_HEADER_TYPE(emsg[port].header) == PD_DATA_REQUEST) { - - /* - * Set to highest revision supported by both - * ports. - */ - prl_set_rev(port, TCPC_TX_SOP, - (PD_HEADER_REV(emsg[port].header) > PD_REV30) ? - PD_REV30 : PD_HEADER_REV(emsg[port].header)); - - /* We are PD connected */ - PE_SET_FLAG(port, PE_FLAGS_PD_CONNECTION); - tc_pd_connection(port, 1); - - /* - * Handle the Sink Request in - * PE_SRC_Negotiate_Capability state - */ - set_state_pe(port, PE_SRC_NEGOTIATE_CAPABILITY); - return; - } - - /* - * We have a Protocol Error. - * PE_SNK/SRC_READY if explicit contract - * PE_SEND_SOFT_RESET otherwise - */ - if (PE_CHK_FLAG(port, PE_FLAGS_EXPLICIT_CONTRACT)) - if (pe[port].power_role == PD_ROLE_SINK) - set_state_pe(port, PE_SNK_READY); - else - set_state_pe(port, PE_SRC_READY); - else - set_state_pe(port, PE_SEND_SOFT_RESET); - return; - } - - /* - * Transition to the PE_SRC_Discovery state when: - * 1) The Protocol Layer indicates that the Message has not been sent - * and we are presently not Connected - * - * NOTE: The PE_FLAGS_PROTOCOL_ERROR is set if a GoodCRC Message - * is not received. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_PROTOCOL_ERROR) && - !PE_CHK_FLAG(port, PE_FLAGS_PD_CONNECTION)) { - PE_CLR_FLAG(port, PE_FLAGS_PROTOCOL_ERROR); - - set_state_pe(port, PE_SRC_DISCOVERY); - return; - } - - /* - * Transition to the PE_SRC_Disabled state when: - * 1) The Port Partners have not been PD Connected - * 2) The NoResponseTimer times out - * 3) And the HardResetCounter > nHardResetCount. - * - * Transition to the Error Recovery state when: - * 1) The Port Partners have previously been PD Connected - * 2) The NoResponseTimer times out - * 3) And the HardResetCounter > nHardResetCount. - */ - if (get_time().val > pe[port].no_response_timer) { - if (pe[port].hard_reset_counter <= N_HARD_RESET_COUNT) - set_state_pe(port, PE_SRC_HARD_RESET); - else if (PE_CHK_FLAG(port, PE_FLAGS_PD_CONNECTION)) - set_state_pe(port, PE_WAIT_FOR_ERROR_RECOVERY); - else - set_state_pe(port, PE_SRC_DISABLED); - return; - } - - /* - * Transition to the PE_SRC_Hard_Reset state when: - * 1) The SenderResponseTimer times out. - */ - if (get_time().val > pe[port].sender_response_timer) { - set_state_pe(port, PE_SRC_HARD_RESET); - return; - } -} - -/** - * PE_SRC_Negotiate_Capability - */ -static void pe_src_negotiate_capability_entry(int port) -{ - uint32_t payload; - - print_current_state(port); - - /* Get message payload */ - payload = *(uint32_t *)(&emsg[port].buf); - - /* - * Evaluate the Request from the Attached Sink - */ - - /* - * Transition to the PE_SRC_Capability_Response state when: - * 1) The Request cannot be met. - * 2) Or the Request can be met later from the Power Reserve - * - * Transition to the PE_SRC_Transition_Supply state when: - * 1) The Request can be met - * - */ - if (pd_check_requested_voltage(payload, port) != EC_SUCCESS) { - set_state_pe(port, PE_SRC_CAPABILITY_RESPONSE); - } else { - PE_SET_FLAG(port, PE_FLAGS_ACCEPT); - pe[port].requested_idx = RDO_POS(payload); - set_state_pe(port, PE_SRC_TRANSITION_SUPPLY); - } -} - -/** - * PE_SRC_Transition_Supply - */ -static void pe_src_transition_supply_entry(int port) -{ - print_current_state(port); - - /* Transition Power Supply */ - pd_transition_voltage(pe[port].requested_idx); - - /* Send a GotoMin Message or otherwise an Accept Message */ - if (PE_CHK_FLAG(port, PE_FLAGS_ACCEPT)) { - PE_CLR_FLAG(port, PE_FLAGS_ACCEPT); - prl_send_ctrl_msg(port, TCPC_TX_SOP, PD_CTRL_ACCEPT); - } else { - prl_send_ctrl_msg(port, TCPC_TX_SOP, PD_CTRL_GOTO_MIN); - } - -} - -static void pe_src_transition_supply_run(int port) -{ - /* - * Transition to the PE_SRC_Ready state when: - * 1) The power supply is ready. - * - * NOTE: This code block is executed twice: - * First Pass) - * When PE_FLAGS_TX_COMPLETE is set due to the - * PD_CTRL_ACCEPT or PD_CTRL_GOTO_MIN messages - * being sent. - * - * Second Pass) - * When PE_FLAGS_TX_COMPLETE is set due to the - * PD_CTRL_PS_RDY message being sent. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - - /* - * NOTE: If a message was received, - * pe_src_ready state will handle it. - */ - - if (PE_CHK_FLAG(port, PE_FLAGS_PS_READY)) { - PE_CLR_FLAG(port, PE_FLAGS_PS_READY); - /* NOTE: Second pass through this code block */ - /* Explicit Contract is now in place */ - PE_SET_FLAG(port, PE_FLAGS_EXPLICIT_CONTRACT); - set_state_pe(port, PE_SRC_READY); - } else { - /* NOTE: First pass through this code block */ - /* Send PS_RDY message */ - prl_send_ctrl_msg(port, TCPC_TX_SOP, PD_CTRL_PS_RDY); - PE_SET_FLAG(port, PE_FLAGS_PS_READY); - } - - return; - } - - /* - * Transition to the PE_SRC_Hard_Reset state when: - * 1) A Protocol Error occurs. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_PROTOCOL_ERROR)) { - PE_CLR_FLAG(port, PE_FLAGS_PROTOCOL_ERROR); - set_state_pe(port, PE_SRC_HARD_RESET); - } -} - -/** - * PE_SRC_Ready - */ -static void pe_src_ready_entry(int port) -{ - print_current_state(port); - - /* - * If the transition into PE_SRC_Ready is the result of Protocol Error - * that has not caused a Soft Reset (see Section 8.3.3.4.1) then the - * notification to the Protocol Layer of the end of the AMS Shall Not - * be sent since there is a Message to be processed. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_PROTOCOL_ERROR)) { - PE_CLR_FLAG(port, PE_FLAGS_PROTOCOL_ERROR); - } else { - PE_CLR_FLAG(port, PE_FLAGS_INTERRUPTIBLE_AMS); - prl_end_ams(port); - } - - /* - * Do port partner discovery - * - * This function modifies state variables that are used in the run - * part of this state. See pe_attempt_port_discovery for details. - */ - pe_attempt_port_discovery(port); -} - -static void pe_src_ready_run(int port) -{ - uint32_t payload; - uint8_t type; - uint8_t cnt; - uint8_t ext; - - /* - * Start Port Discovery when: - * 1) The DiscoverIdentityTimer times out. - */ - if (get_time().val > pe[port].discover_port_identity_timer) { - pe_start_port_discovery(port); - return; - } - - /* - * Handle Device Policy Manager Requests - */ - - /* - * Ignore sink specific request: - * DPM_REQUEST_NEW_POWER_LEVEL - * DPM_REQUEST_SOURCE_CAP - */ - - PE_CLR_DPM_REQUEST(port, DPM_REQUEST_NEW_POWER_LEVEL | - DPM_REQUEST_SOURCE_CAP); - - if (pe[port].dpm_request) { - if (PE_CHK_DPM_REQUEST(port, DPM_REQUEST_DR_SWAP)) { - PE_CLR_DPM_REQUEST(port, DPM_REQUEST_DR_SWAP); - if (PE_CHK_FLAG(port, PE_FLAGS_MODAL_OPERATION)) - set_state_pe(port, PE_SRC_HARD_RESET); - else - set_state_pe(port, PE_DRS_SEND_SWAP); - } else if (PE_CHK_DPM_REQUEST(port, DPM_REQUEST_PR_SWAP)) { - PE_CLR_DPM_REQUEST(port, DPM_REQUEST_PR_SWAP); - set_state_pe(port, PE_PRS_SRC_SNK_SEND_SWAP); - } else if (PE_CHK_DPM_REQUEST(port, DPM_REQUEST_VCONN_SWAP)) { - PE_CLR_DPM_REQUEST(port, DPM_REQUEST_VCONN_SWAP); - set_state_pe(port, PE_VCS_SEND_SWAP); - } else if (PE_CHK_DPM_REQUEST(port, DPM_REQUEST_GOTO_MIN)) { - PE_CLR_DPM_REQUEST(port, DPM_REQUEST_GOTO_MIN); - set_state_pe(port, PE_SRC_TRANSITION_SUPPLY); - } else if (PE_CHK_DPM_REQUEST(port, - DPM_REQUEST_SRC_CAP_CHANGE)) { - PE_CLR_DPM_REQUEST(port, DPM_REQUEST_SRC_CAP_CHANGE); - set_state_pe(port, PE_SRC_SEND_CAPABILITIES); - } else if (PE_CHK_DPM_REQUEST(port, DPM_REQUEST_SEND_PING)) { - PE_CLR_DPM_REQUEST(port, DPM_REQUEST_SEND_PING); - set_state_pe(port, PE_SRC_PING); - } else if (PE_CHK_DPM_REQUEST(port, - DPM_REQUEST_DISCOVER_IDENTITY)) { - PE_CLR_DPM_REQUEST(port, DPM_REQUEST_DISCOVER_IDENTITY); - - pe[port].partner_type = CABLE; - pe[port].vdm_cmd = DISCOVER_IDENTITY; - pe[port].vdm_data[0] = VDO( - USB_SID_PD, - 1, /* structured */ - VDO_SVDM_VERS(1) | DISCOVER_IDENTITY); - pe[port].vdm_cnt = 1; - set_state_pe(port, PE_VDM_REQUEST); - } - return; - } - - /* - * Handle Source Requests - */ - if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - type = PD_HEADER_TYPE(emsg[port].header); - cnt = PD_HEADER_CNT(emsg[port].header); - ext = PD_HEADER_EXT(emsg[port].header); - payload = *(uint32_t *)emsg[port].buf; - - /* Extended Message Requests */ - if (ext > 0) { - switch (type) { -#ifdef CONFIG_BATTERY - case PD_EXT_GET_BATTERY_CAP: - set_state_pe(port, PE_GIVE_BATTERY_CAP); - break; - case PD_EXT_GET_BATTERY_STATUS: - set_state_pe(port, PE_GIVE_BATTERY_STATUS); - break; -#endif - default: - set_state_pe(port, PE_SEND_NOT_SUPPORTED); - } - } - /* Data Message Requests */ - else if (cnt > 0) { - switch (type) { - case PD_DATA_REQUEST: - set_state_pe(port, PE_SRC_NEGOTIATE_CAPABILITY); - break; - case PD_DATA_SINK_CAP: - break; - case PD_DATA_VENDOR_DEF: - if (PD_HEADER_TYPE(emsg[port].header) == - PD_DATA_VENDOR_DEF) { - if (PD_VDO_SVDM(payload)) { - set_state_pe(port, - PE_VDM_RESPONSE); - } else - set_state_pe(port, - PE_HANDLE_CUSTOM_VDM_REQUEST); - } - break; - case PD_DATA_BIST: - set_state_pe(port, PE_BIST); - break; - default: - set_state_pe(port, PE_SEND_NOT_SUPPORTED); - } - } - /* Control Message Requests */ - else { - switch (type) { - case PD_CTRL_GOOD_CRC: - break; - case PD_CTRL_NOT_SUPPORTED: - break; - case PD_CTRL_PING: - break; - case PD_CTRL_GET_SOURCE_CAP: - set_state_pe(port, PE_SRC_SEND_CAPABILITIES); - break; - case PD_CTRL_GET_SINK_CAP: - set_state_pe(port, PE_SNK_GIVE_SINK_CAP); - break; - case PD_CTRL_GOTO_MIN: - break; - case PD_CTRL_PR_SWAP: - set_state_pe(port, - PE_PRS_SRC_SNK_EVALUATE_SWAP); - break; - case PD_CTRL_DR_SWAP: - if (PE_CHK_FLAG(port, - PE_FLAGS_MODAL_OPERATION)) { - set_state_pe(port, PE_SRC_HARD_RESET); - return; - } - - set_state_pe(port, PE_DRS_EVALUATE_SWAP); - break; - case PD_CTRL_VCONN_SWAP: - set_state_pe(port, PE_VCS_EVALUATE_SWAP); - break; - default: - set_state_pe(port, PE_SEND_NOT_SUPPORTED); - } - } - } -} - -static void pe_src_ready_exit(int port) -{ - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - - /* - * If the Source is initiating an AMS then the Policy Engine Shall - * notify the Protocol Layer that the first Message in an AMS will - * follow. - */ - if (!PE_CHK_FLAG(port, PE_FLAGS_INTERRUPTIBLE_AMS)) - prl_start_ams(port); -} - -/** - * PE_SRC_Disabled - */ -static void pe_src_disabled_entry(int port) -{ - print_current_state(port); - - if ((pe[port].vpd_vdo >= 0) && VPD_VDO_CTS(pe[port].vpd_vdo)) { - /* - * Inform the Device Policy Manager that a Charge-Through VCONN - * Powered Device was detected. - */ - tc_ctvpd_detected(port); - } - - /* - * Unresponsive to USB Power Delivery messaging, but not to Hard Reset - * Signaling. See pe_got_hard_reset - */ -} - -/** - * PE_SRC_Capability_Response - */ -static void pe_src_capability_response_entry(int port) -{ - print_current_state(port); - - /* NOTE: Wait messaging should be implemented. */ - - prl_send_ctrl_msg(port, TCPC_TX_SOP, PD_CTRL_REJECT); -} - -static void pe_src_capability_response_run(int port) -{ - /* - * Transition to the PE_SRC_Ready state when: - * 1) There is an Explicit Contract and - * 2) A Reject Message has been sent and the present Contract is still - * Valid or - * 3) A Wait Message has been sent. - * - * Transition to the PE_SRC_Hard_Reset state when: - * 1) There is an Explicit Contract and - * 2) The Reject Message has been sent and the present - * Contract is Invalid - * - * Transition to the PE_SRC_Wait_New_Capabilities state when: - * 1) There is no Explicit Contract and - * 2) A Reject Message has been sent or - * 3) A Wait Message has been sent. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - - if (PE_CHK_FLAG(port, PE_FLAGS_EXPLICIT_CONTRACT)) - /* - * NOTE: The src capabilities listed in - * board/xxx/usb_pd_policy.c will not - * change so the present contract will - * never be invalid. - */ - set_state_pe(port, PE_SRC_READY); - else - /* - * NOTE: The src capabilities listed in - * board/xxx/usb_pd_policy.c will not - * change, so no need to resending them - * again. Transition to disabled state. - */ - set_state_pe(port, PE_SRC_DISABLED); - } -} - -/** - * PE_SRC_Hard_Reset - */ -static void pe_src_hard_reset_entry(int port) -{ - print_current_state(port); - - /* Generate Hard Reset Signal */ - prl_execute_hard_reset(port); - - /* Increment the HardResetCounter */ - pe[port].hard_reset_counter++; - - /* Start NoResponseTimer */ - pe[port].no_response_timer = get_time().val + PD_T_NO_RESPONSE; - - /* Start PSHardResetTimer */ - pe[port].ps_hard_reset_timer = get_time().val + PD_T_PS_HARD_RESET; -} - -static void pe_src_hard_reset_run(int port) -{ - /* - * Transition to the PE_SRC_Transition_to_default state when: - * 1) The PSHardResetTimer times out. - */ - if (get_time().val > pe[port].ps_hard_reset_timer) - set_state_pe(port, PE_SRC_TRANSITION_TO_DEFAULT); -} - -/** - * PE_SRC_Hard_Reset_Received - */ -static void pe_src_hard_reset_received_entry(int port) -{ - print_current_state(port); - - /* Start NoResponseTimer */ - pe[port].no_response_timer = get_time().val + PD_T_NO_RESPONSE; - - /* Start PSHardResetTimer */ - pe[port].ps_hard_reset_timer = get_time().val + PD_T_PS_HARD_RESET; -} - -static void pe_src_hard_reset_received_run(int port) -{ - /* - * Transition to the PE_SRC_Transition_to_default state when: - * 1) The PSHardResetTimer times out. - */ - if (get_time().val > pe[port].ps_hard_reset_timer) - set_state_pe(port, PE_SRC_TRANSITION_TO_DEFAULT); -} - -/** - * PE_SRC_Transition_To_Default - */ -static void pe_src_transition_to_default_entry(int port) -{ - print_current_state(port); - - /* Reset flags */ - pe[port].flags = 0; - - /* Reset DPM Request */ - pe[port].dpm_request = 0; - - /* - * Request Device Policy Manager to request power - * supply Hard Resets to vSafe5V via vSafe0V - * Reset local HW - * Request Device Policy Manager to set Port Data - * Role to DFP and turn off VCONN - */ - tc_hard_reset(port); -} - -static void pe_src_transition_to_default_run(int port) -{ - /* - * Transition to the PE_SRC_Startup state when: - * 1) The power supply has reached the default level. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_PS_RESET_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_PS_RESET_COMPLETE); - /* Inform the Protocol Layer that the Hard Reset is complete */ - prl_hard_reset_complete(port); - set_state_pe(port, PE_SRC_STARTUP); - } -} - -/** - * PE_SNK_Startup State - */ -static void pe_snk_startup_entry(int port) -{ - print_current_state(port); - - /* Reset the protocol layer */ - prl_reset(port); - - /* Set initial data role */ - pe[port].data_role = tc_get_data_role(port); - - /* Set initial power role */ - pe[port].power_role = PD_ROLE_SINK; - - /* Clear explicit contract */ - pe_invalidate_explicit_contract(port); - - /* Clear port discovery flags */ - PE_CLR_FLAG(port, PE_FLAGS_DISCOVER_PORT_IDENTITY_DONE); - pe[port].discover_port_identity_counter = 0; - - /* Reset dr swap attempt counter */ - pe[port].dr_swap_attempt_counter = 0; -} - -static void pe_snk_startup_run(int port) -{ - /* Wait until protocol layer is running */ - if (!prl_is_running(port)) - return; - - /* - * Once the reset process completes, the Policy Engine Shall - * transition to the PE_SNK_Discovery state - */ - set_state_pe(port, PE_SNK_DISCOVERY); -} - -/** - * PE_SNK_Discovery State - */ -static void pe_snk_discovery_entry(int port) -{ - print_current_state(port); -} - -static void pe_snk_discovery_run(int port) -{ - /* - * Transition to the PE_SNK_Wait_for_Capabilities state when: - * 1) VBUS has been detected - */ - if (pd_is_vbus_present(port)) - set_state_pe(port, PE_SNK_WAIT_FOR_CAPABILITIES); -} - -/** - * PE_SNK_Wait_For_Capabilities State - */ -static void pe_snk_wait_for_capabilities_entry(int port) -{ - print_current_state(port); - - /* Initialize and start the SinkWaitCapTimer */ - pe[port].timeout = get_time().val + PD_T_SINK_WAIT_CAP; -} - -static void pe_snk_wait_for_capabilities_run(int port) -{ - uint8_t type; - uint8_t cnt; - uint8_t ext; - - /* - * Transition to the PE_SNK_Evaluate_Capability state when: - * 1) A Source_Capabilities Message is received. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - type = PD_HEADER_TYPE(emsg[port].header); - cnt = PD_HEADER_CNT(emsg[port].header); - ext = PD_HEADER_EXT(emsg[port].header); - - if ((ext == 0) && (cnt > 0) && (type == PD_DATA_SOURCE_CAP)) { - set_state_pe(port, PE_SNK_EVALUATE_CAPABILITY); - return; - } - } - - /* When the SinkWaitCapTimer times out, perform a Hard Reset. */ - if (get_time().val > pe[port].timeout) { - PE_SET_FLAG(port, PE_FLAGS_SNK_WAIT_CAP_TIMEOUT); - set_state_pe(port, PE_SNK_HARD_RESET); - } -} - -/** - * PE_SNK_Evaluate_Capability State - */ -static void pe_snk_evaluate_capability_entry(int port) -{ - uint32_t *pdo = (uint32_t *)emsg[port].buf; - uint32_t header = emsg[port].header; - uint32_t num = emsg[port].len >> 2; - int i; - - print_current_state(port); - - /* Reset Hard Reset counter to zero */ - pe[port].hard_reset_counter = 0; - - /* Set to highest revision supported by both ports. */ - prl_set_rev(port, TCPC_TX_SOP, (PD_HEADER_REV(header) > PD_REV30) ? - PD_REV30 : PD_HEADER_REV(header)); - - pe[port].src_cap_cnt = num; - - for (i = 0; i < num; i++) - pe[port].src_caps[i] = *pdo++; - - /* src cap 0 should be fixed PDO */ - pe_update_pdo_flags(port, pdo[0]); - - /* Evaluate the options based on supplied capabilities */ - pd_process_source_cap(port, pe[port].src_cap_cnt, pe[port].src_caps); - - /* We are PD Connected */ - PE_SET_FLAG(port, PE_FLAGS_PD_CONNECTION); - tc_pd_connection(port, 1); - - /* Device Policy Response Received */ - set_state_pe(port, PE_SNK_SELECT_CAPABILITY); -} - -/** - * PE_SNK_Select_Capability State - */ -static void pe_snk_select_capability_entry(int port) -{ - print_current_state(port); - - pe[port].sender_response_timer = TIMER_DISABLED; - /* Send Request */ - pe_send_request_msg(port); -} - -static void pe_snk_select_capability_run(int port) -{ - uint8_t type; - uint8_t cnt; - - /* Wait until message is sent */ - if (pe[port].sender_response_timer == TIMER_DISABLED) { - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - /* Initialize and run SenderResponseTimer */ - pe[port].sender_response_timer = - get_time().val + PD_T_SENDER_RESPONSE; - } else { - return; - } - } - - if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - type = PD_HEADER_TYPE(emsg[port].header); - cnt = PD_HEADER_CNT(emsg[port].header); - - /* - * Transition to the PE_SNK_Transition_Sink state when: - * 1) An Accept Message is received from the Source. - * - * Transition to the PE_SNK_Wait_for_Capabilities state when: - * 1) There is no Explicit Contract in place and - * 2) A Reject Message is received from the Source or - * 3) A Wait Message is received from the Source. - * - * Transition to the PE_SNK_Ready state when: - * 1) There is an Explicit Contract in place and - * 2) A Reject Message is received from the Source or - * 3) A Wait Message is received from the Source. - * - * Transition to the PE_SNK_Hard_Reset state when: - * 1) A SenderResponseTimer timeout occurs. - */ - - /* Only look at control messages */ - if (cnt == 0) { - /* - * Accept Message Received - */ - if (type == PD_CTRL_ACCEPT) { - /* explicit contract is now in place */ - PE_SET_FLAG(port, PE_FLAGS_EXPLICIT_CONTRACT); - set_state_pe(port, PE_SNK_TRANSITION_SINK); - - /* - * Setup to get Device Policy Manager to - * request Sink Capabilities for possible FRS - */ - pe_dpm_request(port, DPM_REQUEST_GET_SNK_CAPS); - return; - } - /* - * Reject or Wait Message Received - */ - else if (type == PD_CTRL_REJECT || - type == PD_CTRL_WAIT) { - if (type == PD_CTRL_WAIT) - PE_SET_FLAG(port, PE_FLAGS_WAIT); - - /* - * We had a previous explicit contract, so - * transition to PE_SNK_Ready - */ - if (PE_CHK_FLAG(port, - PE_FLAGS_EXPLICIT_CONTRACT)) - set_state_pe(port, PE_SNK_READY); - /* - * No previous explicit contract, so transition - * to PE_SNK_Wait_For_Capabilities - */ - else - set_state_pe(port, - PE_SNK_WAIT_FOR_CAPABILITIES); - return; - } - /* - * Unexpected Control Message Received - */ - else { - /* Send Soft Reset */ - set_state_pe(port, PE_SEND_SOFT_RESET); - return; - } - } - /* - * Unexpected Data Message - */ - else { - /* Send Soft Reset */ - set_state_pe(port, PE_SEND_SOFT_RESET); - return; - } - } - - /* SenderResponsetimer timeout */ - if (get_time().val > pe[port].sender_response_timer) - set_state_pe(port, PE_SNK_HARD_RESET); -} - -/** - * PE_SNK_Transition_Sink State - */ -static void pe_snk_transition_sink_entry(int port) -{ - print_current_state(port); - - /* Initialize and run PSTransitionTimer */ - pe[port].ps_transition_timer = get_time().val + PD_T_PS_TRANSITION; -} - -static void pe_snk_transition_sink_run(int port) -{ - /* - * Transition to the PE_SNK_Ready state when: - * 1) A PS_RDY Message is received from the Source. - * - * Transition to the PE_SNK_Hard_Reset state when: - * 1) A Protocol Error occurs. - */ - - if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - /* - * PS_RDY message received - */ - if ((PD_HEADER_CNT(emsg[port].header) == 0) && - (PD_HEADER_TYPE(emsg[port].header) == - PD_CTRL_PS_RDY)) { - set_state_pe(port, PE_SNK_READY); - return; - } - - /* - * Protocol Error - */ - set_state_pe(port, PE_SNK_HARD_RESET); - } - - /* - * Timeout will lead to a Hard Reset - */ - if (get_time().val > pe[port].ps_transition_timer && - pe[port].hard_reset_counter <= N_HARD_RESET_COUNT) { - PE_SET_FLAG(port, PE_FLAGS_PS_TRANSITION_TIMEOUT); - - set_state_pe(port, PE_SNK_HARD_RESET); - } -} - -static void pe_snk_transition_sink_exit(int port) -{ - /* Transition Sink's power supply to the new power level */ - pd_set_input_current_limit(port, - pe[port].curr_limit, pe[port].supply_voltage); - - if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) - /* Set ceiling based on what's negotiated */ - charge_manager_set_ceil(port, - CEIL_REQUESTOR_PD, pe[port].curr_limit); -} - - -/** - * PE_SNK_Ready State - */ -static void pe_snk_ready_entry(int port) -{ - print_current_state(port); - - PE_CLR_FLAG(port, PE_FLAGS_INTERRUPTIBLE_AMS); - prl_end_ams(port); - - /* - * On entry to the PE_SNK_Ready state as the result of a wait, then do - * the following: - * 1) Initialize and run the SinkRequestTimer - */ - if (PE_CHK_FLAG(port, PE_FLAGS_WAIT)) { - PE_CLR_FLAG(port, PE_FLAGS_WAIT); - pe[port].sink_request_timer = - get_time().val + PD_T_SINK_REQUEST; - } else { - pe[port].sink_request_timer = TIMER_DISABLED; - } - - /* - * Do port partner discovery - * - * This function modifies state variables that are used in the run - * part of this state. See pe_attempt_port_discovery for details. - */ - pe_attempt_port_discovery(port); -} - -static void pe_snk_ready_run(int port) -{ - uint32_t payload; - uint8_t type; - uint8_t cnt; - uint8_t ext; - - if (get_time().val > pe[port].sink_request_timer) { - set_state_pe(port, PE_SNK_SELECT_CAPABILITY); - return; - } - - /* - * Start Port Discovery when: - * 1) The PortDiscoverIdentityTimer times out. - */ - if (get_time().val > pe[port].discover_port_identity_timer) { - pe_start_port_discovery(port); - return; - } - - /* - * Handle Device Policy Manager Requests - */ - /* - * Ignore source specific requests: - * DPM_REQUEST_GOTO_MIN - * DPM_REQUEST_SRC_CAP_CHANGE, - * DPM_REQUEST_SEND_PING - */ - PE_CLR_DPM_REQUEST(port, DPM_REQUEST_GOTO_MIN | - DPM_REQUEST_SRC_CAP_CHANGE | - DPM_REQUEST_SEND_PING); - - if (pe[port].dpm_request) { - if (PE_CHK_DPM_REQUEST(port, DPM_REQUEST_DR_SWAP)) { - PE_CLR_DPM_REQUEST(port, DPM_REQUEST_DR_SWAP); - if (PE_CHK_FLAG(port, PE_FLAGS_MODAL_OPERATION)) - set_state_pe(port, PE_SNK_HARD_RESET); - else - set_state_pe(port, PE_DRS_SEND_SWAP); - } else if (PE_CHK_DPM_REQUEST(port, DPM_REQUEST_PR_SWAP)) { - PE_CLR_DPM_REQUEST(port, DPM_REQUEST_PR_SWAP); - set_state_pe(port, PE_PRS_SNK_SRC_SEND_SWAP); - } else if (PE_CHK_DPM_REQUEST(port, DPM_REQUEST_VCONN_SWAP)) { - PE_CLR_DPM_REQUEST(port, DPM_REQUEST_VCONN_SWAP); - set_state_pe(port, PE_VCS_SEND_SWAP); - } else if (PE_CHK_DPM_REQUEST(port, DPM_REQUEST_SOURCE_CAP)) { - PE_CLR_DPM_REQUEST(port, DPM_REQUEST_SOURCE_CAP); - set_state_pe(port, PE_SNK_GET_SOURCE_CAP); - } else if (PE_CHK_DPM_REQUEST(port, - DPM_REQUEST_NEW_POWER_LEVEL)) { - PE_CLR_DPM_REQUEST(port, DPM_REQUEST_NEW_POWER_LEVEL); - set_state_pe(port, PE_SNK_SELECT_CAPABILITY); - } else if (PE_CHK_DPM_REQUEST(port, - DPM_REQUEST_DISCOVER_IDENTITY)) { - PE_CLR_DPM_REQUEST(port, - DPM_REQUEST_DISCOVER_IDENTITY); - - pe[port].partner_type = CABLE; - pe[port].vdm_cmd = DISCOVER_IDENTITY; - pe[port].vdm_data[0] = VDO( - USB_SID_PD, - 1, /* structured */ - VDO_SVDM_VERS(1) | DISCOVER_IDENTITY); - pe[port].vdm_cnt = 1; - - set_state_pe(port, PE_VDM_REQUEST); - } else if (PE_CHK_DPM_REQUEST(port, - DPM_REQUEST_GET_SNK_CAPS)) { - PE_CLR_DPM_REQUEST(port, DPM_REQUEST_GET_SNK_CAPS); - set_state_pe(port, PE_DR_SNK_GET_SINK_CAP); - } - return; - } - - /* - * Handle Source Requests - */ - if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - type = PD_HEADER_TYPE(emsg[port].header); - cnt = PD_HEADER_CNT(emsg[port].header); - ext = PD_HEADER_EXT(emsg[port].header); - payload = *(uint32_t *)emsg[port].buf; - - /* Extended Message Request */ - if (ext > 0) { - switch (type) { -#ifdef CONFIG_BATTERY - case PD_EXT_GET_BATTERY_CAP: - set_state_pe(port, PE_GIVE_BATTERY_CAP); - break; - case PD_EXT_GET_BATTERY_STATUS: - set_state_pe(port, PE_GIVE_BATTERY_STATUS); - break; -#endif - default: - set_state_pe(port, PE_SEND_NOT_SUPPORTED); - } - } - /* Data Messages */ - else if (cnt > 0) { - switch (type) { - case PD_DATA_SOURCE_CAP: - set_state_pe(port, - PE_SNK_EVALUATE_CAPABILITY); - break; - case PD_DATA_VENDOR_DEF: - if (PD_HEADER_TYPE(emsg[port].header) == - PD_DATA_VENDOR_DEF) { - if (PD_VDO_SVDM(payload)) - set_state_pe(port, - PE_VDM_RESPONSE); - else - set_state_pe(port, - PE_HANDLE_CUSTOM_VDM_REQUEST); - } - break; - case PD_DATA_BIST: - set_state_pe(port, PE_BIST); - break; - default: - set_state_pe(port, PE_SEND_NOT_SUPPORTED); - } - } - /* Control Messages */ - else { - switch (type) { - case PD_CTRL_GOOD_CRC: - /* Do nothing */ - break; - case PD_CTRL_PING: - /* Do noghing */ - break; - case PD_CTRL_GET_SOURCE_CAP: - set_state_pe(port, PE_SNK_GET_SOURCE_CAP); - break; - case PD_CTRL_GET_SINK_CAP: - set_state_pe(port, PE_SNK_GIVE_SINK_CAP); - break; - case PD_CTRL_GOTO_MIN: - set_state_pe(port, PE_SNK_TRANSITION_SINK); - break; - case PD_CTRL_PR_SWAP: - set_state_pe(port, - PE_PRS_SNK_SRC_EVALUATE_SWAP); - break; - case PD_CTRL_DR_SWAP: - if (PE_CHK_FLAG(port, PE_FLAGS_MODAL_OPERATION)) - set_state_pe(port, PE_SNK_HARD_RESET); - else - set_state_pe(port, - PE_DRS_EVALUATE_SWAP); - break; - case PD_CTRL_VCONN_SWAP: - set_state_pe(port, PE_VCS_EVALUATE_SWAP); - break; - case PD_CTRL_NOT_SUPPORTED: - /* Do nothing */ - break; - default: - set_state_pe(port, PE_SEND_NOT_SUPPORTED); - } - } - } -} - -static void pe_snk_ready_exit(int port) -{ - if (!PE_CHK_FLAG(port, PE_FLAGS_INTERRUPTIBLE_AMS)) - prl_start_ams(port); -} - -/** - * PE_SNK_Hard_Reset - */ -static void pe_snk_hard_reset_entry(int port) -{ - print_current_state(port); - - /* - * Note: If the SinkWaitCapTimer times out and the HardResetCounter is - * greater than nHardResetCount the Sink Shall assume that the - * Source is non-responsive. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_SNK_WAIT_CAP_TIMEOUT) && - pe[port].hard_reset_counter > N_HARD_RESET_COUNT) { - set_state_pe(port, PE_SRC_DISABLED); - } - - PE_CLR_FLAG(port, PE_FLAGS_SNK_WAIT_CAP_TIMEOUT); - - /* Request the generation of Hard Reset Signaling by the PHY Layer */ - pe_prl_execute_hard_reset(port); - - /* Increment the HardResetCounter */ - pe[port].hard_reset_counter++; - - /* - * Transition the Sink’s power supply to the new power level if - * PSTransistionTimer timeout occurred. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_PS_TRANSITION_TIMEOUT)) { - PE_CLR_FLAG(port, PE_FLAGS_PS_TRANSITION_TIMEOUT); - - /* Transition Sink's power supply to the new power level */ - pd_set_input_current_limit(port, pe[port].curr_limit, - pe[port].supply_voltage); - if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) - /* Set ceiling based on what's negotiated */ - charge_manager_set_ceil(port, CEIL_REQUESTOR_PD, - pe[port].curr_limit); - } -} - -static void pe_snk_hard_reset_run(int port) -{ - /* - * Transition to the PE_SNK_Transition_to_default state when: - * 1) The Hard Reset is complete. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_HARD_RESET_PENDING)) - return; - - set_state_pe(port, PE_SNK_TRANSITION_TO_DEFAULT); -} - -/** - * PE_SNK_Transition_to_default - */ -static void pe_snk_transition_to_default_entry(int port) -{ - print_current_state(port); - - tc_hard_reset(port); -} - -static void pe_snk_transition_to_default_run(int port) -{ - if (PE_CHK_FLAG(port, PE_FLAGS_PS_RESET_COMPLETE)) { - /* PE_SNK_Startup clears all flags */ - - /* Inform the Protocol Layer that the Hard Reset is complete */ - prl_hard_reset_complete(port); - set_state_pe(port, PE_SNK_STARTUP); - } -} - -/** - * PE_SNK_Get_Source_Cap - */ -static void pe_snk_get_source_cap_entry(int port) -{ - print_current_state(port); - - /* Send a Get_Source_Cap Message */ - emsg[port].len = 0; - prl_send_ctrl_msg(port, TCPC_TX_SOP, PD_CTRL_GET_SOURCE_CAP); -} - -static void pe_snk_get_source_cap_run(int port) -{ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - - set_state_pe(port, PE_SNK_READY); - } -} - -/** - * PE_SNK_Send_Soft_Reset and PE_SRC_Send_Soft_Reset - */ -static void pe_send_soft_reset_entry(int port) -{ - print_current_state(port); - - /* Reset Protocol Layer */ - prl_reset(port); - - pe[port].sender_response_timer = TIMER_DISABLED; -} - -static void pe_send_soft_reset_run(int port) -{ - int type; - int cnt; - int ext; - - /* Wait until protocol layer is running */ - if (!prl_is_running(port)) - return; - - if (pe[port].sender_response_timer == TIMER_DISABLED) { - /* Send Soft Reset message */ - prl_send_ctrl_msg(port, TCPC_TX_SOP, PD_CTRL_SOFT_RESET); - - /* Initialize and run SenderResponseTimer */ - pe[port].sender_response_timer = - get_time().val + PD_T_SENDER_RESPONSE; - } - - /* - * Transition to PE_SNK_Hard_Reset or PE_SRC_Hard_Reset on Sender - * Response Timer Timeout or Protocol Layer or Protocol Error - */ - if (get_time().val > pe[port].sender_response_timer || - PE_CHK_FLAG(port, PE_FLAGS_PROTOCOL_ERROR)) { - PE_CLR_FLAG(port, PE_FLAGS_PROTOCOL_ERROR); - - if (pe[port].power_role == PD_ROLE_SINK) - set_state_pe(port, PE_SRC_HARD_RESET); - else - set_state_pe(port, PE_SRC_HARD_RESET); - return; - } - - /* - * Transition to the PE_SNK_Send_Capabilities or - * PE_SRC_Send_Capabilities state when: - * 1) An Accept Message has been received. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - type = PD_HEADER_TYPE(emsg[port].header); - cnt = PD_HEADER_CNT(emsg[port].header); - ext = PD_HEADER_EXT(emsg[port].header); - - if ((ext == 0) && (cnt == 0) && (type == PD_CTRL_ACCEPT)) { - if (pe[port].power_role == PD_ROLE_SINK) - set_state_pe(port, - PE_SNK_WAIT_FOR_CAPABILITIES); - else - set_state_pe(port, - PE_SRC_SEND_CAPABILITIES); - return; - } - } -} - -static void pe_send_soft_reset_exit(int port) -{ - /* Clear TX Complete Flag */ - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); -} - -/** - * PE_SNK_Soft_Reset and PE_SNK_Soft_Reset - */ -static void pe_soft_reset_entry(int port) -{ - print_current_state(port); - - pe[port].sender_response_timer = TIMER_DISABLED; - - prl_send_ctrl_msg(port, TCPC_TX_SOP, PD_CTRL_ACCEPT); -} - -static void pe_soft_reset_run(int port) -{ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - - if (pe[port].power_role == PD_ROLE_SINK) - set_state_pe(port, PE_SNK_WAIT_FOR_CAPABILITIES); - else - set_state_pe(port, PE_SRC_SEND_CAPABILITIES); - } else if (PE_CHK_FLAG(port, PE_FLAGS_PROTOCOL_ERROR)) { - PE_CLR_FLAG(port, PE_FLAGS_PROTOCOL_ERROR); - - if (pe[port].power_role == PD_ROLE_SINK) - set_state_pe(port, PE_SNK_HARD_RESET); - else - set_state_pe(port, PE_SRC_HARD_RESET); - } -} - -/** - * PE_SRC_Not_Supported and PE_SNK_Not_Supported - */ -static void pe_send_not_supported_entry(int port) -{ - print_current_state(port); - - /* Request the Protocol Layer to send a Not_Supported Message. */ - if (prl_get_rev(port, TCPC_TX_SOP) > PD_REV20) - prl_send_ctrl_msg(port, TCPC_TX_SOP, PD_CTRL_NOT_SUPPORTED); - else - prl_send_ctrl_msg(port, TCPC_TX_SOP, PD_CTRL_REJECT); -} - -static void pe_send_not_supported_run(int port) -{ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - - if (pe[port].power_role == PD_ROLE_SOURCE) - set_state_pe(port, PE_SRC_READY); - else - set_state_pe(port, PE_SNK_READY); - } -} - -/** - * PE_SRC_Ping - */ -static void pe_src_ping_entry(int port) -{ - print_current_state(port); - prl_send_ctrl_msg(port, TCPC_TX_SOP, PD_CTRL_PING); -} - -static void pe_src_ping_run(int port) -{ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - set_state_pe(port, PE_SRC_READY); - } -} - -/** - * PE_Give_Battery_Cap - */ -static void pe_give_battery_cap_entry(int port) -{ - uint32_t payload = *(uint32_t *)(&emsg[port].buf); - uint16_t *msg = (uint16_t *)emsg[port].buf; - - if (!IS_ENABLED(CONFIG_BATTERY)) - return; - print_current_state(port); - - /* msg[0] - extended header is set by Protocol Layer */ - - /* Set VID */ - msg[1] = USB_VID_GOOGLE; - - /* Set PID */ - msg[2] = CONFIG_USB_PID; - - if (battery_is_present()) { - /* - * We only have one fixed battery, - * so make sure batt cap ref is 0. - */ - if (BATT_CAP_REF(payload) != 0) { - /* Invalid battery reference */ - msg[3] = 0; - msg[4] = 0; - msg[5] = 1; - } else { - uint32_t v; - uint32_t c; - - /* - * The Battery Design Capacity field shall return the - * Battery’s design capacity in tenths of Wh. If the - * Battery is Hot Swappable and is not present, the - * Battery Design Capacity field shall be set to 0. If - * the Battery is unable to report its Design Capacity, - * it shall return 0xFFFF - */ - msg[3] = 0xffff; - - /* - * The Battery Last Full Charge Capacity field shall - * return the Battery’s last full charge capacity in - * tenths of Wh. If the Battery is Hot Swappable and - * is not present, the Battery Last Full Charge Capacity - * field shall be set to 0. If the Battery is unable to - * report its Design Capacity, the Battery Last Full - * Charge Capacity field shall be set to 0xFFFF. - */ - msg[4] = 0xffff; - - if (battery_design_voltage(&v) == 0) { - if (battery_design_capacity(&c) == 0) { - /* - * Wh = (c * v) / 1000000 - * 10th of a Wh = Wh * 10 - */ - msg[3] = DIV_ROUND_NEAREST((c * v), - 100000); - } - - if (battery_full_charge_capacity(&c) == 0) { - /* - * Wh = (c * v) / 1000000 - * 10th of a Wh = Wh * 10 - */ - msg[4] = DIV_ROUND_NEAREST((c * v), - 100000); - } - } - } - } - - /* Extended Battery Cap data is 9 bytes */ - emsg[port].len = 9; - - prl_send_ext_data_msg(port, TCPC_TX_SOP, PD_EXT_BATTERY_CAP); -} - -static void pe_give_battery_cap_run(int port) -{ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - if (pe[port].power_role == PD_ROLE_SOURCE) - set_state_pe(port, PE_SRC_READY); - else - set_state_pe(port, PE_SNK_READY); - } -} - -/** - * PE_Give_Battery_Status - */ -static void pe_give_battery_status_entry(int port) -{ - uint32_t payload = *(uint32_t *)(&emsg[port].buf); - uint32_t *msg = (uint32_t *)emsg[port].buf; - - if (!IS_ENABLED(CONFIG_BATTERY)) - return; - print_current_state(port); - - if (battery_is_present()) { - /* - * We only have one fixed battery, - * so make sure batt cap ref is 0. - */ - if (BATT_CAP_REF(payload) != 0) { - /* Invalid battery reference */ - *msg |= BSDO_INVALID; - } else { - uint32_t v; - uint32_t c; - - if (battery_design_voltage(&v) != 0 || - battery_remaining_capacity(&c) != 0) { - *msg |= BSDO_CAP(BSDO_CAP_UNKNOWN); - } else { - /* - * Wh = (c * v) / 1000000 - * 10th of a Wh = Wh * 10 - */ - *msg |= BSDO_CAP(DIV_ROUND_NEAREST((c * v), - 100000)); - } - - /* Battery is present */ - *msg |= BSDO_PRESENT; - - /* - * For drivers that are not smart battery compliant, - * battery_status() returns EC_ERROR_UNIMPLEMENTED and - * the battery is assumed to be idle. - */ - if (battery_status(&c) != 0) { - *msg |= BSDO_IDLE; /* assume idle */ - } else { - if (c & STATUS_FULLY_CHARGED) - /* Fully charged */ - *msg |= BSDO_IDLE; - else if (c & STATUS_DISCHARGING) - /* Discharging */ - *msg |= BSDO_DISCHARGING; - /* else battery is charging.*/ - } - } - } else { - *msg = BSDO_CAP(BSDO_CAP_UNKNOWN); - } - - /* Battery Status data is 4 bytes */ - emsg[port].len = 4; - - prl_send_data_msg(port, TCPC_TX_SOP, PD_DATA_BATTERY_STATUS); -} - -static void pe_give_battery_status_run(int port) -{ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - set_state_pe(port, PE_SRC_READY); - } -} - -/** - * PE_DRS_Evaluate_Swap - */ -static void pe_drs_evaluate_swap_entry(int port) -{ - print_current_state(port); - - /* Get evaluation of Data Role Swap request from DPM */ - if (pd_check_data_swap(port, pe[port].data_role)) { - PE_SET_FLAG(port, PE_FLAGS_ACCEPT); - /* - * PE_DRS_UFP_DFP_Evaluate_Swap and - * PE_DRS_DFP_UFP_Evaluate_Swap states embedded here. - */ - prl_send_ctrl_msg(port, TCPC_TX_SOP, PD_CTRL_ACCEPT); - } else { - /* - * PE_DRS_UFP_DFP_Reject_Swap and PE_DRS_DFP_UFP_Reject_Swap - * states embedded here. - */ - prl_send_ctrl_msg(port, TCPC_TX_SOP, PD_CTRL_REJECT); - } -} - -static void pe_drs_evaluate_swap_run(int port) -{ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - - /* Accept Message sent. Transtion to PE_DRS_Change */ - if (PE_CHK_FLAG(port, PE_FLAGS_ACCEPT)) { - PE_CLR_FLAG(port, PE_FLAGS_ACCEPT); - set_state_pe(port, PE_DRS_CHANGE); - } else { - /* - * Message sent. Transition back to PE_SRC_Ready or - * PE_SNK_Ready. - */ - if (pe[port].power_role == PD_ROLE_SOURCE) - set_state_pe(port, PE_SRC_READY); - else - set_state_pe(port, PE_SNK_READY); - } - } -} - -/** - * PE_DRS_Change - */ -static void pe_drs_change_entry(int port) -{ - print_current_state(port); - - /* - * PE_DRS_UFP_DFP_Change_to_DFP and PE_DRS_DFP_UFP_Change_to_UFP - * states embedded here. - */ - /* Request DPM to change port data role */ - pd_request_data_swap(port); -} - -static void pe_drs_change_run(int port) -{ - /* Wait until the data role is changed */ - if (pe[port].data_role == tc_get_data_role(port)) - return; - - /* Update the data role */ - pe[port].data_role = tc_get_data_role(port); - - if (pe[port].data_role == PD_ROLE_DFP) - PE_CLR_FLAG(port, PE_FLAGS_DR_SWAP_TO_DFP); - - /* - * Port changed. Transition back to PE_SRC_Ready or - * PE_SNK_Ready. - */ - if (pe[port].power_role == PD_ROLE_SINK) - set_state_pe(port, PE_SNK_READY); - else - set_state_pe(port, PE_SRC_READY); -} - -/** - * PE_DRS_Send_Swap - */ -static void pe_drs_send_swap_entry(int port) -{ - print_current_state(port); - - /* - * PE_DRS_UFP_DFP_Send_Swap and PE_DRS_DFP_UFP_Send_Swap - * states embedded here. - */ - /* Request the Protocol Layer to send a DR_Swap Message */ - prl_send_ctrl_msg(port, TCPC_TX_SOP, PD_CTRL_DR_SWAP); - - pe[port].sender_response_timer = TIMER_DISABLED; -} - -static void pe_drs_send_swap_run(int port) -{ - int type; - int cnt; - int ext; - - /* Wait until message is sent */ - if (pe[port].sender_response_timer == TIMER_DISABLED) { - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - /* Initialize and run SenderResponseTimer */ - pe[port].sender_response_timer = - get_time().val + PD_T_SENDER_RESPONSE; - } else { - return; - } - } - - /* - * Transition to PE_SRC_Ready or PE_SNK_Ready state when: - * 1) Or the SenderResponseTimer times out. - */ - if (get_time().val > pe[port].sender_response_timer) { - if (pe[port].power_role == PD_ROLE_SINK) - set_state_pe(port, PE_SNK_READY); - else - set_state_pe(port, PE_SRC_READY); - return; - } - - /* - * Transition to PE_DRS_Change when: - * 1) An Accept Message is received. - * - * Transition to PE_SRC_Ready or PE_SNK_Ready state when: - * 1) A Reject Message is received. - * 2) Or a Wait Message is received. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - type = PD_HEADER_TYPE(emsg[port].header); - cnt = PD_HEADER_CNT(emsg[port].header); - ext = PD_HEADER_EXT(emsg[port].header); - - if ((ext == 0) && (cnt == 0)) { - if (type == PD_CTRL_ACCEPT) { - set_state_pe(port, PE_DRS_CHANGE); - return; - } else if ((type == PD_CTRL_REJECT) || - (type == PD_CTRL_WAIT)) { - if (type == PD_CTRL_WAIT) - PE_SET_FLAG(port, - PE_FLAGS_WAITING_DR_SWAP); - - if (pe[port].power_role == PD_ROLE_SINK) - set_state_pe(port, PE_SNK_READY); - else - set_state_pe(port, PE_SRC_READY); - return; - } - } - } - - /* - * Transition to PE_SRC_Ready or PE_SNK_Ready state when: - * 1) the SenderResponseTimer times out. - */ - if (get_time().val > pe[port].sender_response_timer) { - if (pe[port].power_role == PD_ROLE_SINK) - set_state_pe(port, PE_SNK_READY); - else - set_state_pe(port, PE_SRC_READY); - return; - } -} - -/** - * PE_PRS_SRC_SNK_Evaluate_Swap - */ -static void pe_prs_src_snk_evaluate_swap_entry(int port) -{ - print_current_state(port); - - if (!pd_check_power_swap(port)) { - /* PE_PRS_SRC_SNK_Reject_PR_Swap state embedded here */ - prl_send_ctrl_msg(port, TCPC_TX_SOP, PD_CTRL_REJECT); - } else { - pd_request_power_swap(port); - /* PE_PRS_SRC_SNK_Accept_Swap state embedded here */ - PE_SET_FLAG(port, PE_FLAGS_ACCEPT); - prl_send_ctrl_msg(port, TCPC_TX_SOP, PD_CTRL_ACCEPT); - } -} - -static void pe_prs_src_snk_evaluate_swap_run(int port) -{ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - - if (PE_CHK_FLAG(port, PE_FLAGS_ACCEPT)) { - PE_CLR_FLAG(port, PE_FLAGS_ACCEPT); - - /* - * Power Role Swap OK, transition to - * PE_PRS_SRC_SNK_Transition_to_off - */ - set_state_pe(port, PE_PRS_SRC_SNK_TRANSITION_TO_OFF); - } else { - /* Message sent, return to PE_SRC_Ready */ - set_state_pe(port, PE_SRC_READY); - } - } -} - -/** - * PE_PRS_SRC_SNK_Transition_To_Off - */ -static void pe_prs_src_snk_transition_to_off_entry(int port) -{ - print_current_state(port); - - /* Tell TypeC to swap from Attached.SRC to Attached.SNK */ - tc_prs_src_snk_assert_rd(port); - pe[port].ps_source_timer = - get_time().val + PD_POWER_SUPPLY_TURN_OFF_DELAY; -} - -static void pe_prs_src_snk_transition_to_off_run(int port) -{ - /* Give time for supply to power off */ - if (get_time().val < pe[port].ps_source_timer) - return; - - /* Wait until Rd is asserted */ - if (tc_is_attached_snk(port)) { - /* Contract is invalid */ - pe_invalidate_explicit_contract(port); - set_state_pe(port, PE_PRS_SRC_SNK_WAIT_SOURCE_ON); - } -} - -/** - * PE_PRS_SRC_SNK_Wait_Source_On - */ -static void pe_prs_src_snk_wait_source_on_entry(int port) -{ - print_current_state(port); - prl_send_ctrl_msg(port, TCPC_TX_SOP, PD_CTRL_PS_RDY); - pe[port].ps_source_timer = TIMER_DISABLED; -} - -static void pe_prs_src_snk_wait_source_on_run(int port) -{ - int type; - int cnt; - int ext; - - if (pe[port].ps_source_timer != TIMER_DISABLED && - PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - - /* Update pe power role */ - pe[port].power_role = tc_get_power_role(port); - pe[port].ps_source_timer = get_time().val + PD_T_PS_SOURCE_ON; - } - - /* - * Transition to PE_SNK_Startup when: - * 1) An PS_RDY Message is received. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - type = PD_HEADER_TYPE(emsg[port].header); - cnt = PD_HEADER_CNT(emsg[port].header); - ext = PD_HEADER_EXT(emsg[port].header); - - if ((ext == 0) && (cnt == 0) && (type == PD_CTRL_PS_RDY)) { - tc_pr_swap_complete(port); - pe[port].ps_source_timer = TIMER_DISABLED; - set_state_pe(port, PE_SNK_STARTUP); - return; - } - } - - /* - * Transition to ErrorRecovery state when: - * 1) The PSSourceOnTimer times out. - * 2) PS_RDY not sent after retries. - */ - if (get_time().val > pe[port].ps_source_timer || - PE_CHK_FLAG(port, PE_FLAGS_PROTOCOL_ERROR)) { - PE_CLR_FLAG(port, PE_FLAGS_PROTOCOL_ERROR); - - set_state_pe(port, PE_WAIT_FOR_ERROR_RECOVERY); - return; - } -} - -/** - * PE_PRS_SRC_SNK_Send_Swap - */ -static void pe_prs_src_snk_send_swap_entry(int port) -{ - print_current_state(port); - - /* Request the Protocol Layer to send a PR_Swap Message. */ - prl_send_ctrl_msg(port, TCPC_TX_SOP, PD_CTRL_PR_SWAP); - - /* Start the SenderResponseTimer */ - pe[port].sender_response_timer = - get_time().val + PD_T_SENDER_RESPONSE; -} - -static void pe_prs_src_snk_send_swap_run(int port) -{ - int type; - int cnt; - int ext; - - /* - * Transition to PE_SRC_Ready state when: - * 1) Or the SenderResponseTimer times out. - */ - if (get_time().val > pe[port].sender_response_timer) { - set_state_pe(port, PE_SRC_READY); - return; - } - - /* - * Transition to PE_PRS_SRC_SNK_Transition_To_Off when: - * 1) An Accept Message is received. - * - * Transition to PE_SRC_Ready state when: - * 1) A Reject Message is received. - * 2) Or a Wait Message is received. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - type = PD_HEADER_TYPE(emsg[port].header); - cnt = PD_HEADER_CNT(emsg[port].header); - ext = PD_HEADER_EXT(emsg[port].header); - - if ((ext == 0) && (cnt == 0)) { - if (type == PD_CTRL_ACCEPT) - set_state_pe(port, - PE_PRS_SRC_SNK_TRANSITION_TO_OFF); - else if ((type == PD_CTRL_REJECT) || - (type == PD_CTRL_WAIT)) - set_state_pe(port, PE_SRC_READY); - } - } -} - -static void pe_prs_src_snk_send_swap_exit(int port) -{ - /* Clear TX Complete Flag if set */ - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); -} - -/** - * PE_PRS_SNK_SRC_Evaluate_Swap - */ -static void pe_prs_snk_src_evaluate_swap_entry(int port) -{ - print_current_state(port); - - if (!pd_check_power_swap(port)) { - /* PE_PRS_SNK_SRC_Reject_Swap state embedded here */ - prl_send_ctrl_msg(port, TCPC_TX_SOP, PD_CTRL_REJECT); - } else { - pd_request_power_swap(port); - /* PE_PRS_SNK_SRC_Accept_Swap state embedded here */ - PE_SET_FLAG(port, PE_FLAGS_ACCEPT); - prl_send_ctrl_msg(port, TCPC_TX_SOP, PD_CTRL_ACCEPT); - } -} - -static void pe_prs_snk_src_evaluate_swap_run(int port) -{ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - if (PE_CHK_FLAG(port, PE_FLAGS_ACCEPT)) { - PE_CLR_FLAG(port, PE_FLAGS_ACCEPT); - - /* - * Accept message sent, transition to - * PE_PRS_SNK_SRC_Transition_to_off - */ - set_state_pe(port, PE_PRS_SNK_SRC_TRANSITION_TO_OFF); - } else { - /* Message sent, return to PE_SNK_Ready */ - set_state_pe(port, PE_SNK_READY); - } - } -} - -/** - * PE_PRS_SNK_SRC_Transition_To_Off - * PE_FRS_SNK_SRC_Transition_To_Off - * - * NOTE: Shared action code used for Power Role Swap and Fast Role Swap - */ -static void pe_prs_snk_src_transition_to_off_entry(int port) -{ - print_current_state(port); - - if (!PE_CHK_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_PATH)) - tc_snk_power_off(port); - - pe[port].ps_source_timer = get_time().val + PD_T_PS_SOURCE_OFF; -} - -static void pe_prs_snk_src_transition_to_off_run(int port) -{ - int type; - int cnt; - int ext; - - /* - * Transition to ErrorRecovery state when: - * 1) The PSSourceOffTimer times out. - */ - if (get_time().val > pe[port].ps_source_timer) - set_state_pe(port, PE_WAIT_FOR_ERROR_RECOVERY); - - /* - * Transition to PE_PRS_SNK_SRC_Assert_Rp when: - * 1) An PS_RDY Message is received. - */ - else if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - type = PD_HEADER_TYPE(emsg[port].header); - cnt = PD_HEADER_CNT(emsg[port].header); - ext = PD_HEADER_EXT(emsg[port].header); - - if ((ext == 0) && (cnt == 0) && (type == PD_CTRL_PS_RDY)) { - /* - * FRS: We are always ready to drive vSafe5v, so just - * skip PE_FRS_SNK_SRC_Vbus_Applied and go direct to - * PE_FRS_SNK_SRC_Assert_Rp - */ - set_state_pe(port, PE_PRS_SNK_SRC_ASSERT_RP); - } - } -} - -/** - * PE_PRS_SNK_SRC_Assert_Rp - * PE_FRS_SNK_SRC_Assert_Rp - * - * NOTE: Shared action code used for Power Role Swap and Fast Role Swap - */ -static void pe_prs_snk_src_assert_rp_entry(int port) -{ - print_current_state(port); - - /* - * Tell TypeC to Power/Fast Role Swap (PRS/FRS) from - * Attached.SNK to Attached.SRC - */ - tc_prs_snk_src_assert_rp(port); -} - -static void pe_prs_snk_src_assert_rp_run(int port) -{ - /* Wait until TypeC is in the Attached.SRC state */ - if (tc_is_attached_src(port)) { - if (!PE_CHK_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_PATH)) { - /* Contract is invalid now */ - pe_invalidate_explicit_contract(port); - } - - set_state_pe(port, PE_PRS_SNK_SRC_SOURCE_ON); - } -} - -/** - * PE_PRS_SNK_SRC_Source_On - * PE_FRS_SNK_SRC_Source_On - * - * NOTE: Shared action code used for Power Role Swap and Fast Role Swap - */ -static void pe_prs_snk_src_source_on_entry(int port) -{ - print_current_state(port); - - /* - * VBUS was enabled when the TypeC state machine entered - * Attached.SRC state - */ - pe[port].ps_source_timer = get_time().val + - PD_POWER_SUPPLY_TURN_ON_DELAY; -} - -static void pe_prs_snk_src_source_on_run(int port) -{ - /* Wait until power supply turns on */ - if (pe[port].ps_source_timer != TIMER_DISABLED) { - if (get_time().val < pe[port].ps_source_timer) - return; - - /* update pe power role */ - pe[port].power_role = tc_get_power_role(port); - prl_send_ctrl_msg(port, TCPC_TX_SOP, PD_CTRL_PS_RDY); - /* reset timer so PD_CTRL_PS_RDY isn't sent again */ - pe[port].ps_source_timer = TIMER_DISABLED; - } - - /* - * Transition to ErrorRecovery state when: - * 1) On protocol error - */ - else if (PE_CHK_FLAG(port, PE_FLAGS_PROTOCOL_ERROR)) { - PE_CLR_FLAG(port, PE_FLAGS_PROTOCOL_ERROR); - set_state_pe(port, PE_WAIT_FOR_ERROR_RECOVERY); - } - - else if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - - /* Run swap source timer on entry to pe_src_startup */ - PE_SET_FLAG(port, PE_FLAGS_RUN_SOURCE_START_TIMER); - tc_pr_swap_complete(port); - set_state_pe(port, PE_SRC_STARTUP); - } -} - -/** - * PE_PRS_SNK_SRC_Send_Swap - * PE_FRS_SNK_SRC_Send_Swap - * - * NOTE: Shared action code used for Power Role Swap and Fast Role Swap - */ -static void pe_prs_snk_src_send_swap_entry(int port) -{ - print_current_state(port); - - /* - * PRS_SNK_SRC_SEND_SWAP - * Request the Protocol Layer to send a PR_Swap Message. - * - * FRS_SNK_SRC_SEND_SWAP - * Hardware should have turned off sink power and started - * bringing Vbus to vSafe5. - * Request the Protocol Layer to send a FR_Swap Message. - */ - prl_send_ctrl_msg(port, - TCPC_TX_SOP, - PE_CHK_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_PATH) - ? PD_CTRL_FR_SWAP - : PD_CTRL_PR_SWAP); - - /* Start the SenderResponseTimer */ - pe[port].sender_response_timer = - get_time().val + PD_T_SENDER_RESPONSE; -} - -static void pe_prs_snk_src_send_swap_run(int port) -{ - int type; - int cnt; - int ext; - - /* - * PRS: Transition to PE_SNK_Ready state when: - * FRS: Transition to ErrorRecovery state when: - * 1) The SenderResponseTimer times out. - */ - if (get_time().val > pe[port].sender_response_timer) - set_state_pe(port, - PE_CHK_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_PATH) - ? PE_WAIT_FOR_ERROR_RECOVERY - : PE_SNK_READY); - - /* - * Transition to PE_PRS_SNK_SRC_Transition_to_off when: - * 1) An Accept Message is received. - * - * PRS: Transition to PE_SNK_Ready state when: - * FRS: Transition to ErrorRecovery state when: - * 1) A Reject Message is received. - * 2) Or a Wait Message is received. - */ - else if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - type = PD_HEADER_TYPE(emsg[port].header); - cnt = PD_HEADER_CNT(emsg[port].header); - ext = PD_HEADER_EXT(emsg[port].header); - - if ((ext == 0) && (cnt == 0)) { - if (type == PD_CTRL_ACCEPT) - set_state_pe(port, - PE_PRS_SNK_SRC_TRANSITION_TO_OFF); - else if ((type == PD_CTRL_REJECT) || - (type == PD_CTRL_WAIT)) - set_state_pe(port, - PE_CHK_FLAG(port, - PE_FLAGS_FAST_ROLE_SWAP_PATH) - ? PE_WAIT_FOR_ERROR_RECOVERY - : PE_SNK_READY); - } - } -} - -static void pe_prs_snk_src_send_swap_exit(int port) -{ - /* Clear TX Complete Flag if set */ - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); -} - -/** - * PE_FRS_SNK_SRC_Start_AMS - */ -static void pe_frs_snk_src_start_ams_entry(int port) -{ - print_current_state(port); - - /* Contract is invalid now */ - pe_invalidate_explicit_contract(port); - - /* Inform Protocol Layer this is start of AMS */ - prl_start_ams(port); - - /* Shared PRS/FRS code, indicate FRS path */ - PE_SET_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_PATH); - set_state_pe(port, PE_PRS_SNK_SRC_SEND_SWAP); -} - -/** - * PE_PRS_FRS_SHARED - */ -static void pe_prs_frs_shared_entry(int port) -{ - /* - * Shared PRS/FRS code, assume PRS path - * - * This is the super state entry. It will be called before - * the first entry state to get into the PRS/FRS path. - * For FRS, PE_FRS_SNK_SRC_START_AMS entry will be called - * after this and that will set for the FRS path. - */ - PE_CLR_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_PATH); -} - -static void pe_prs_frs_shared_exit(int port) -{ - /* - * Shared PRS/FRS code, when not in shared path - * indicate PRS path - */ - PE_CLR_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_PATH); -} - -/** - * BIST - */ -static void pe_bist_entry(int port) -{ - uint32_t *payload = (uint32_t *)emsg[port].buf; - uint8_t mode = BIST_MODE(payload[0]); - - print_current_state(port); - - /* - * See section 6.4.3.6 BIST Carrier Mode 2: - * With a BIST Carrier Mode 2 BIST Data Object, the UUT Shall send out - * a continuous string of alternating "1"s and “0”s. - * The UUT Shall exit the Continuous BIST Mode within tBISTContMode of - * this Continuous BIST Mode being enabled. - */ - if (mode == BIST_CARRIER_MODE_2) { - prl_send_ctrl_msg(port, TCPC_TX_BIST_MODE_2, 0); - pe[port].bist_cont_mode_timer = - get_time().val + PD_T_BIST_CONT_MODE; - } - /* - * See section 6.4.3.9 BIST Test Data: - * With a BIST Test Data BIST Data Object, the UUT Shall return a - * GoodCRC Message and Shall enter a test mode in which it sends no - * further Messages except for GoodCRC Messages in response to received - * Messages. - */ - else if (mode == BIST_TEST_DATA) - pe[port].bist_cont_mode_timer = TIMER_DISABLED; -} - -static void pe_bist_run(int port) -{ - if (get_time().val > pe[port].bist_cont_mode_timer) { - - if (pe[port].power_role == PD_ROLE_SOURCE) - set_state_pe(port, PE_SRC_TRANSITION_TO_DEFAULT); - else - set_state_pe(port, PE_SNK_TRANSITION_TO_DEFAULT); - } else { - /* - * We are in test data mode and no further Messages except for - * GoodCRC Messages in response to received Messages will - * be sent. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - } -} - -/** - * Give_Sink_Cap Message - */ -static void pe_snk_give_sink_cap_entry(int port) -{ - print_current_state(port); - - /* Send a Sink_Capabilities Message */ - emsg[port].len = pd_snk_pdo_cnt * 4; - memcpy(emsg[port].buf, (uint8_t *)pd_snk_pdo, emsg[port].len); - prl_send_data_msg(port, TCPC_TX_SOP, PD_DATA_SINK_CAP); -} - -static void pe_snk_give_sink_cap_run(int port) -{ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - if (pe[port].power_role == PD_ROLE_SOURCE) - set_state_pe(port, PE_SRC_READY); - else - set_state_pe(port, PE_SNK_READY); - } -} - -/** - * Wait For Error Recovery - */ -static void pe_wait_for_error_recovery_entry(int port) -{ - print_current_state(port); - tc_start_error_recovery(port); -} - -static void pe_wait_for_error_recovery_run(int port) -{ - /* Stay here until error recovery is complete */ -} - -/** - * PE_Handle_Custom_Vdm_Request - */ -static void pe_handle_custom_vdm_request_entry(int port) -{ - /* Get the message */ - uint32_t *payload = (uint32_t *)emsg[port].buf; - int cnt = PD_HEADER_CNT(emsg[port].header); - int sop = PD_HEADER_GET_SOP(emsg[port].header); - int rlen = 0; - uint32_t *rdata; - - print_current_state(port); - - /* This is an Interruptible AMS */ - PE_SET_FLAG(port, PE_FLAGS_INTERRUPTIBLE_AMS); - - rlen = pd_custom_vdm(port, cnt, payload, &rdata); - if (rlen > 0) { - emsg[port].len = rlen * 4; - memcpy(emsg[port].buf, (uint8_t *)rdata, emsg[port].len); - prl_send_data_msg(port, sop, PD_DATA_VENDOR_DEF); - } -} - -static void pe_handle_custom_vdm_request_run(int port) -{ - /* Wait for ACCEPT, WAIT or Reject message to send. */ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - - /* - * Message sent. Transition back to - * PE_SRC_Ready or PE_SINK_Ready - */ - if (pe[port].power_role == PD_ROLE_SOURCE) - set_state_pe(port, PE_SRC_READY); - else - set_state_pe(port, PE_SNK_READY); - } -} - -/** - * PE_DO_PORT_Discovery - * - * NOTE: Port Discovery Policy - * To discover a port partner, Vendor Defined Messages (VDMs) are - * sent to the port partner. The sequence of commands are - * sent in the following order: - * 1) CMD_DISCOVER_IDENT - * 2) CMD_DISCOVER_SVID - * 3) CMD_DISCOVER_MODES - * 4) CMD_ENTER_MODE - * 5) CMD_DP_STATUS - * 6) CMD_DP_CONFIG - * - * If a the port partner replies with BUSY, the sequence is resent - * N_DISCOVER_IDENTITY_COUNT times before giving up. - */ -static void pe_do_port_discovery_entry(int port) -{ - print_current_state(port); - - pe[port].partner_type = PORT; - pe[port].vdm_cnt = 0; -} - -static void pe_do_port_discovery_run(int port) -{ -#ifdef CONFIG_USB_PD_ALT_MODE_DFP - uint32_t *payload = (uint32_t *)emsg[port].buf; - struct svdm_amode_data *modep = get_modep(port, PD_VDO_VID(payload[0])); - int ret = 0; - - if (!PE_CHK_FLAG(port, - PE_FLAGS_VDM_REQUEST_NAKED | PE_FLAGS_VDM_REQUEST_BUSY)) { - switch (pe[port].vdm_cmd) { - case DO_PORT_DISCOVERY_START: - pe[port].vdm_cmd = CMD_DISCOVER_IDENT; - pe[port].vdm_data[0] = 0; - ret = 1; - break; - case CMD_DISCOVER_IDENT: - pe[port].vdm_cmd = CMD_DISCOVER_SVID; - pe[port].vdm_data[0] = 0; - ret = 1; - break; - case CMD_DISCOVER_SVID: - pe[port].vdm_cmd = CMD_DISCOVER_MODES; - ret = dfp_discover_modes(port, pe[port].vdm_data); - break; - case CMD_DISCOVER_MODES: - pe[port].vdm_cmd = CMD_ENTER_MODE; - pe[port].vdm_data[0] = pd_dfp_enter_mode(port, 0, 0); - if (pe[port].vdm_data[0]) - ret = 1; - break; - case CMD_ENTER_MODE: - pe[port].vdm_cmd = CMD_DP_STATUS; - if (modep->opos) { - ret = modep->fx->status(port, - pe[port].vdm_data); - pe[port].vdm_data[0] |= - PD_VDO_OPOS(modep->opos); - } - break; - case CMD_DP_STATUS: - pe[port].vdm_cmd = CMD_DP_CONFIG; - - /* - * DP status response & UFP's DP attention have same - * payload - */ - dfp_consume_attention(port, pe[port].vdm_data); - if (modep && modep->opos) - ret = modep->fx->config(port, - pe[port].vdm_data); - break; - case CMD_DP_CONFIG: - if (modep && modep->opos && modep->fx->post_config) - modep->fx->post_config(port); - PE_SET_FLAG(port, PE_FLAGS_DISCOVER_PORT_IDENTITY_DONE); - break; - case CMD_EXIT_MODE: - /* Do nothing */ - break; - case CMD_ATTENTION: - /* Do nothing */ - break; - } - } - - if (ret == 0) { - if (PE_CHK_FLAG(port, PE_FLAGS_VDM_REQUEST_NAKED)) - PE_SET_FLAG(port, PE_FLAGS_DISCOVER_PORT_IDENTITY_DONE); - - if (pe[port].power_role == PD_ROLE_SOURCE) - set_state_pe(port, PE_SRC_READY); - else - set_state_pe(port, PE_SNK_READY); - } else { - PE_CLR_FLAG(port, PE_FLAGS_VDM_REQUEST_BUSY); - - /* - * Copy Vendor Defined Message (VDM) Header into - * message buffer - */ - if (pe[port].vdm_data[0] == 0) - pe[port].vdm_data[0] = VDO( - USB_SID_PD, - 1, /* structured */ - VDO_SVDM_VERS(1) | pe[port].vdm_cmd); - - pe[port].vdm_data[0] |= VDO_CMDT(CMDT_INIT); - pe[port].vdm_data[0] |= VDO_SVDM_VERS(pd_get_vdo_ver(port)); - - pe[port].vdm_cnt = ret; - set_state_pe(port, PE_VDM_REQUEST); - } -#endif -} - -/** - * PE_VDM_REQUEST - */ - -static void pe_vdm_request_entry(int port) -{ - print_current_state(port); - - /* This is an Interruptible AMS */ - PE_SET_FLAG(port, PE_FLAGS_INTERRUPTIBLE_AMS); - - /* Copy Vendor Data Objects (VDOs) into message buffer */ - if (pe[port].vdm_cnt > 0) { - /* Copy data after header */ - memcpy(&emsg[port].buf, - (uint8_t *)pe[port].vdm_data, - pe[port].vdm_cnt * 4); - /* Update len with the number of VDO bytes */ - emsg[port].len = pe[port].vdm_cnt * 4; - } - - if (pe[port].partner_type) { - /* Save power and data roles */ - pe[port].saved_power_role = tc_get_power_role(port); - pe[port].saved_data_role = tc_get_data_role(port); - - prl_send_data_msg(port, TCPC_TX_SOP_PRIME, PD_DATA_VENDOR_DEF); - } else { - prl_send_data_msg(port, TCPC_TX_SOP, PD_DATA_VENDOR_DEF); - } - - pe[port].vdm_response_timer = TIMER_DISABLED; -} - -static void pe_vdm_request_run(int port) -{ - if (pe[port].vdm_response_timer == TIMER_DISABLED && - PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - /* Message was sent */ - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - - /* Start no response timer */ - pe[port].vdm_response_timer = - get_time().val + PD_T_VDM_SNDR_RSP; - } - - if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - uint32_t *payload; - int sop; - uint8_t type; - uint8_t cnt; - uint8_t ext; - - /* Message received */ - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - /* Get the message */ - payload = (uint32_t *)emsg[port].buf; - sop = PD_HEADER_GET_SOP(emsg[port].header); - type = PD_HEADER_TYPE(emsg[port].header); - cnt = PD_HEADER_CNT(emsg[port].header); - ext = PD_HEADER_EXT(emsg[port].header); - - if ((sop == TCPC_TX_SOP || sop == TCPC_TX_SOP_PRIME) && - type == PD_DATA_VENDOR_DEF && cnt > 0 && - ext == 0) { - if (PD_VDO_CMDT(payload[0]) == CMDT_RSP_ACK) { - set_state_pe(port, PE_VDM_ACKED); - return; - } else if (PD_VDO_CMDT(payload[0]) == CMDT_RSP_NAK || - PD_VDO_CMDT(payload[0]) == CMDT_RSP_BUSY) { - if (PD_VDO_CMDT(payload[0]) == CMDT_RSP_NAK) - PE_SET_FLAG(port, - PE_FLAGS_VDM_REQUEST_NAKED); - else - PE_SET_FLAG(port, - PE_FLAGS_VDM_REQUEST_BUSY); - } - } else { - /* - * Unexpected Message Received. - * Return to Src.Ready or Snk.Ready to - * handle it. - */ - if (pe[port].power_role == PD_ROLE_SOURCE) - set_state_pe(port, PE_SRC_READY); - else - set_state_pe(port, PE_SNK_READY); - } - } - - if (PE_CHK_FLAG(port, PE_FLAGS_PROTOCOL_ERROR)) { - /* Message not sent and we received a protocol error */ - PE_CLR_FLAG(port, PE_FLAGS_PROTOCOL_ERROR); - - /* Fake busy response so we try to send command again */ - PE_SET_FLAG(port, PE_FLAGS_VDM_REQUEST_BUSY); - } else if (get_time().val > pe[port].vdm_response_timer) { - CPRINTF("VDM %s Response Timeout\n", - pe[port].partner_type ? "Cable" : "Port"); - - PE_SET_FLAG(port, PE_FLAGS_VDM_REQUEST_NAKED); - } - - if (PE_CHK_FLAG(port, PE_FLAGS_VDM_REQUEST_NAKED | - PE_FLAGS_VDM_REQUEST_BUSY)) { - /* Return to previous state */ - if (get_last_state_pe(port) == PE_DO_PORT_DISCOVERY) - set_state_pe(port, PE_DO_PORT_DISCOVERY); - else if (pe[port].power_role == PD_ROLE_SOURCE) - set_state_pe(port, PE_SRC_READY); - else - set_state_pe(port, PE_SNK_READY); - } -} - -static void pe_vdm_request_exit(int port) -{ - PE_CLR_FLAG(port, PE_FLAGS_INTERRUPTIBLE_AMS); -} - -enum idh_ptype get_usb_pd_mux_cable_type(int port) -{ - if (pe[port].passive_cable_vdo != PD_VDO_INVALID) - return IDH_PTYPE_PCABLE; - else if (pe[port].active_cable_vdo1 != PD_VDO_INVALID) - return IDH_PTYPE_ACABLE; - else - return IDH_PTYPE_UNDEF; -} - -/** - * PE_VDM_Acked - */ -static void pe_vdm_acked_entry(int port) -{ - uint32_t *payload; - uint8_t vdo_cmd; - int sop; - - print_current_state(port); - - /* Get the message */ - payload = (uint32_t *)emsg[port].buf; - vdo_cmd = PD_VDO_CMD(payload[0]); - sop = PD_HEADER_GET_SOP(emsg[port].header); - - if (sop == TCPC_TX_SOP) { - /* - * Handle Message From Port Partner - */ - -#ifdef CONFIG_USB_PD_ALT_MODE_DFP - int cnt = PD_HEADER_CNT(emsg[port].header); - struct svdm_amode_data *modep; - - modep = get_modep(port, PD_VDO_VID(payload[0])); -#endif - - switch (vdo_cmd) { -#ifdef CONFIG_USB_PD_ALT_MODE_DFP - case CMD_DISCOVER_IDENT: - dfp_consume_identity(port, cnt, payload); -#ifdef CONFIG_CHARGE_MANAGER - if (pd_charge_from_device(pd_get_identity_vid(port), - pd_get_identity_pid(port))) { - charge_manager_update_dualrole(port, - CAP_DEDICATED); - } -#endif - break; - case CMD_DISCOVER_SVID: - dfp_consume_svids(port, cnt, payload); - break; - case CMD_DISCOVER_MODES: - dfp_consume_modes(port, cnt, payload); - break; - case CMD_ENTER_MODE: - break; - case CMD_DP_STATUS: - /* - * DP status response & UFP's DP attention have same - * payload - */ - dfp_consume_attention(port, payload); - break; - case CMD_DP_CONFIG: - if (modep && modep->opos && modep->fx->post_config) - modep->fx->post_config(port); - break; - case CMD_EXIT_MODE: - /* Do nothing */ - break; -#endif - case CMD_ATTENTION: - /* Do nothing */ - break; - default: - CPRINTF("ERR:CMD:%d\n", vdo_cmd); - } - } - - if (!PE_CHK_FLAG(port, PE_FLAGS_DISCOVER_PORT_IDENTITY_DONE)) { - set_state_pe(port, PE_DO_PORT_DISCOVERY); - } else if (pe[port].power_role == PD_ROLE_SOURCE) { - set_state_pe(port, PE_SRC_READY); - } else { - set_state_pe(port, PE_SNK_READY); - } -} - -/** - * PE_VDM_Response - */ -static void pe_vdm_response_entry(int port) -{ - int ret = 0; - uint32_t *payload; - uint8_t vdo_cmd; - int cmd_type; - svdm_rsp_func func = NULL; - - print_current_state(port); - - /* Get the message */ - payload = (uint32_t *)emsg[port].buf; - vdo_cmd = PD_VDO_CMD(payload[0]); - cmd_type = PD_VDO_CMDT(payload[0]); - payload[0] &= ~VDO_CMDT_MASK; - - if (cmd_type != CMDT_INIT) { - CPRINTF("ERR:CMDT:%d\n", vdo_cmd); - - if (pe[port].power_role == PD_ROLE_SOURCE) - set_state_pe(port, PE_SRC_READY); - else - set_state_pe(port, PE_SNK_READY); - return; - } - - switch (vdo_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; -#ifdef CONFIG_USB_PD_ALT_MODE_DFP - case CMD_ATTENTION: - /* - * attention is only SVDM with no response - * (just goodCRC) return zero here. - */ - dfp_consume_attention(port, payload); - if (pe[port].power_role == PD_ROLE_SOURCE) - set_state_pe(port, PE_SRC_READY); - else - set_state_pe(port, PE_SNK_READY); - return; -#endif - default: - CPRINTF("VDO ERR:CMD:%d\n", vdo_cmd); - } - - if (func) { - ret = func(port, payload); - if (ret) - /* ACK */ - payload[0] = VDO( - USB_VID_GOOGLE, - 1, /* Structured VDM */ - VDO_SVDM_VERS(pd_get_vdo_ver(port)) | - VDO_CMDT(CMDT_RSP_ACK) | - vdo_cmd); - else if (!ret) - /* NAK */ - payload[0] = VDO( - USB_VID_GOOGLE, - 1, /* Structured VDM */ - VDO_SVDM_VERS(pd_get_vdo_ver(port)) | - VDO_CMDT(CMDT_RSP_NAK) | - vdo_cmd); - else - /* BUSY */ - payload[0] = VDO( - USB_VID_GOOGLE, - 1, /* Structured VDM */ - VDO_SVDM_VERS(pd_get_vdo_ver(port)) | - VDO_CMDT(CMDT_RSP_BUSY) | - vdo_cmd); - - if (ret <= 0) - ret = 4; - } else { - /* not supported : NACK it */ - payload[0] = VDO( - USB_VID_GOOGLE, - 1, /* Structured VDM */ - VDO_SVDM_VERS(pd_get_vdo_ver(port)) | - VDO_CMDT(CMDT_RSP_NAK) | - vdo_cmd); - ret = 4; - } - - /* Send ACK, NAK, or BUSY */ - emsg[port].len = ret; - prl_send_data_msg(port, TCPC_TX_SOP, PD_DATA_VENDOR_DEF); -} - -static void pe_vdm_response_run(int port) -{ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE) || - PE_CHK_FLAG(port, PE_FLAGS_PROTOCOL_ERROR)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE | - PE_FLAGS_PROTOCOL_ERROR); - - if (pe[port].power_role == PD_ROLE_SOURCE) - set_state_pe(port, PE_SRC_READY); - else - set_state_pe(port, PE_SNK_READY); - } -} - -/* - * PE_VCS_Evaluate_Swap - */ -static void pe_vcs_evaluate_swap_entry(int port) -{ - print_current_state(port); - - /* - * Request the DPM for an evaluation of the VCONN Swap request. - * Note: Ports that are presently the VCONN Source must always - * accept a VCONN - */ - - /* - * Transition to the PE_VCS_Accept_Swap state when: - * 1) The Device Policy Manager indicates that a VCONN Swap is ok. - * - * Transition to the PE_VCS_Reject_Swap state when: - * 1) Port is not presently the VCONN Source and - * 2) The DPM indicates that a VCONN Swap is not ok or - * 3) The DPM indicates that a VCONN Swap cannot be done at this time. - */ - - /* DPM rejects a VCONN Swap and port is not a VCONN source*/ - if (!tc_check_vconn_swap(port) && tc_is_vconn_src(port) < 1) { - /* NOTE: PE_VCS_Reject_Swap State embedded here */ - prl_send_ctrl_msg(port, TCPC_TX_SOP, PD_CTRL_REJECT); - } - /* Port is not ready to perform a VCONN swap */ - else if (tc_is_vconn_src(port) < 0) { - /* NOTE: PE_VCS_Reject_Swap State embedded here */ - prl_send_ctrl_msg(port, TCPC_TX_SOP, PD_CTRL_WAIT); - } - /* Port is ready to perform a VCONN swap */ - else { - /* NOTE: PE_VCS_Accept_Swap State embedded here */ - PE_SET_FLAG(port, PE_FLAGS_ACCEPT); - prl_send_ctrl_msg(port, TCPC_TX_SOP, PD_CTRL_ACCEPT); - } -} - -static void pe_vcs_evaluate_swap_run(int port) -{ - /* Wait for ACCEPT, WAIT or Reject message to send. */ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - - if (PE_CHK_FLAG(port, PE_FLAGS_ACCEPT)) { - PE_CLR_FLAG(port, PE_FLAGS_ACCEPT); - /* Accept Message sent and Presently VCONN Source */ - if (tc_is_vconn_src(port)) - set_state_pe(port, PE_VCS_WAIT_FOR_VCONN_SWAP); - /* Accept Message sent and Not presently VCONN Source */ - else - set_state_pe(port, PE_VCS_TURN_ON_VCONN_SWAP); - } else { - /* - * Message sent. Transition back to PE_SRC_Ready or - * PE_SINK_Ready - */ - if (pe[port].power_role == PD_ROLE_SOURCE) - set_state_pe(port, PE_SRC_READY); - else - set_state_pe(port, PE_SNK_READY); - - } - } -} - -/* - * PE_VCS_Send_Swap - */ -static void pe_vcs_send_swap_entry(int port) -{ - print_current_state(port); - - /* Send a VCONN_Swap Message */ - prl_send_ctrl_msg(port, TCPC_TX_SOP, PD_CTRL_VCONN_SWAP); - - pe[port].sender_response_timer = TIMER_DISABLED; -} - -static void pe_vcs_send_swap_run(int port) -{ - uint8_t type; - uint8_t cnt; - - /* Wait until message is sent */ - if (pe[port].sender_response_timer == TIMER_DISABLED && - PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - /* Start the SenderResponseTimer */ - pe[port].sender_response_timer = get_time().val + - PD_T_SENDER_RESPONSE; - } - - if (pe[port].sender_response_timer == TIMER_DISABLED) - return; - - if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - type = PD_HEADER_TYPE(emsg[port].header); - cnt = PD_HEADER_CNT(emsg[port].header); - - /* Only look at control messages */ - if (cnt == 0) { - /* - * Transition to the PE_VCS_Wait_For_VCONN state when: - * 1) Accept Message Received and - * 2) The Port is presently the VCONN Source. - * - * Transition to the PE_VCS_Turn_On_VCONN state when: - * 1) Accept Message Received and - * 2) The Port is not presently the VCONN Source. - */ - if (type == PD_CTRL_ACCEPT) { - if (tc_is_vconn_src(port)) - set_state_pe(port, - PE_VCS_WAIT_FOR_VCONN_SWAP); - else - set_state_pe(port, - PE_VCS_TURN_ON_VCONN_SWAP); - return; - } - /* - * Transition back to either the PE_SRC_Ready or - * PE_SNK_Ready state when: - * 1) SenderResponseTimer Timeout or - * 2) Reject message is received or - * 3) Wait message Received. - */ - if (get_time().val > pe[port].sender_response_timer || - type == PD_CTRL_REJECT || - type == PD_CTRL_WAIT) { - if (pe[port].power_role == PD_ROLE_SOURCE) - set_state_pe(port, PE_SRC_READY); - else - set_state_pe(port, PE_SNK_READY); - } - } - /* - * Unexpected Data Message Received - */ - else { - /* Send Soft Reset */ - set_state_pe(port, PE_SEND_SOFT_RESET); - return; - } - } -} - -/* - * PE_VCS_Wait_for_VCONN_Swap - */ -static void pe_vcs_wait_for_vconn_swap_entry(int port) -{ - print_current_state(port); - - /* Start the VCONNOnTimer */ - pe[port].vconn_on_timer = get_time().val + PD_T_VCONN_SOURCE_ON; -} - -static void pe_vcs_wait_for_vconn_swap_run(int port) -{ - /* - * Transition to the PE_VCS_Turn_Off_VCONN state when: - * 1) A PS_RDY Message is received. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - /* - * PS_RDY message received - */ - if ((PD_HEADER_CNT(emsg[port].header) == 0) && - (PD_HEADER_TYPE(emsg[port].header) == - PD_CTRL_PS_RDY)) { - set_state_pe(port, PE_VCS_TURN_OFF_VCONN_SWAP); - return; - } - } - - /* - * Transition to either the PE_SRC_Hard_Reset or - * PE_SNK_Hard_Reset state when: - * 1) The VCONNOnTimer times out. - */ - if (get_time().val > pe[port].vconn_on_timer) { - if (pe[port].power_role == PD_ROLE_SOURCE) - set_state_pe(port, PE_SRC_HARD_RESET); - else - set_state_pe(port, PE_SNK_HARD_RESET); - } -} - -/* - * PE_VCS_Turn_On_VCONN_Swap - */ -static void pe_vcs_turn_on_vconn_swap_entry(int port) -{ - print_current_state(port); - - /* Request DPM to turn on VCONN */ - pd_request_vconn_swap_on(port); - pe[port].timeout = 0; -} - -static void pe_vcs_turn_on_vconn_swap_run(int port) -{ - - /* - * Transition to the PE_VCS_Send_Ps_Rdy state when: - * 1) The Port’s VCONN is on. - */ - if (pe[port].timeout == 0 && - PE_CHK_FLAG(port, PE_FLAGS_VCONN_SWAP_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_VCONN_SWAP_COMPLETE); - pe[port].timeout = get_time().val + PD_VCONN_SWAP_DELAY; - } - - if (pe[port].timeout > 0 && get_time().val > pe[port].timeout) - set_state_pe(port, PE_VCS_SEND_PS_RDY_SWAP); -} - -/* - * PE_VCS_Turn_Off_VCONN_Swap - */ -static void pe_vcs_turn_off_vconn_swap_entry(int port) -{ - print_current_state(port); - - /* Request DPM to turn off VCONN */ - pd_request_vconn_swap_off(port); - pe[port].timeout = 0; -} - -static void pe_vcs_turn_off_vconn_swap_run(int port) -{ - /* Wait for VCONN to turn off */ - if (pe[port].timeout == 0 && - PE_CHK_FLAG(port, PE_FLAGS_VCONN_SWAP_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_VCONN_SWAP_COMPLETE); - pe[port].timeout = get_time().val + PD_VCONN_SWAP_DELAY; - } - - if (pe[port].timeout > 0 && get_time().val > pe[port].timeout) { - /* - * A VCONN Swap Shall reset the DiscoverIdentityCounter - * to zero - */ - pe[port].discover_port_identity_counter = 0; - pe[port].dr_swap_attempt_counter = 0; - - if (pe[port].power_role == PD_ROLE_SOURCE) - set_state_pe(port, PE_SRC_READY); - else - set_state_pe(port, PE_SNK_READY); - } -} - -/* - * PE_VCS_Send_PS_Rdy_Swap - */ -static void pe_vcs_send_ps_rdy_swap_entry(int port) -{ - print_current_state(port); - - /* Send a PS_RDY Message */ - prl_send_ctrl_msg(port, TCPC_TX_SOP, PD_CTRL_PS_RDY); - - pe[port].sub = PE_SUB0; -} - -static void pe_vcs_send_ps_rdy_swap_run(int port) -{ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - - switch (pe[port].sub) { - case PE_SUB0: - /* - * After a VCONN Swap the VCONN Source needs to reset - * the Cable Plug’s Protocol Layer in order to ensure - * MessageID synchronization. - */ - prl_send_ctrl_msg(port, TCPC_TX_SOP_PRIME, - PD_CTRL_SOFT_RESET); - pe[port].sub = PE_SUB1; - pe[port].timeout = get_time().val + 100*MSEC; - break; - case PE_SUB1: - /* Got ACCEPT or REJECT from Cable Plug */ - if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED) || - get_time().val > pe[port].timeout) { - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - /* - * A VCONN Swap Shall reset the - * DiscoverIdentityCounter to zero - */ - pe[port].discover_port_identity_counter = 0; - pe[port].dr_swap_attempt_counter = 0; - - if (pe[port].power_role == PD_ROLE_SOURCE) - set_state_pe(port, PE_SRC_READY); - else - set_state_pe(port, PE_SNK_READY); - } - break; - case PE_SUB2: - /* Do nothing */ - break; - } - } -} - -/* - * PE_DR_SNK_Get_Sink_Cap - */ -static void pe_dr_snk_get_sink_cap_entry(int port) -{ - print_current_state(port); - - /* Send a Get Sink Cap Message */ - prl_send_ctrl_msg(port, TCPC_TX_SOP, PD_CTRL_GET_SINK_CAP); - - /* Don't start the timer until message sent */ - pe[port].sender_response_timer = 0; -} - -static void pe_dr_snk_get_sink_cap_run(int port) -{ - int type; - int cnt; - int ext; - uint32_t payload; - - /* Wait until message is sent */ - if (pe[port].sender_response_timer == 0) { - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - /* start the SenderResponseTimer */ - pe[port].sender_response_timer = - get_time().val + PD_T_SENDER_RESPONSE; - } else { - return; - } - } - - /* - * Determine if FRS is possible based on the returned Sink Caps - * and transition to PE_SNK_Ready when: - * 1) An Accept Message is received. - * - * Transition to PE_SNK_Ready state when: - * 1) A Reject Message is received. - * 2) Or a Wait Message is received. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - type = PD_HEADER_TYPE(emsg[port].header); - cnt = PD_HEADER_CNT(emsg[port].header); - ext = PD_HEADER_EXT(emsg[port].header); - payload = *(uint32_t *)emsg[port].buf; - - if ((ext == 0) && (cnt == 0)) { - if (type == PD_CTRL_ACCEPT) { - /* - * Check message to see if we can handle - * FRS for this connection. - * - * TODO(b/14191267): Make sure we can handle - * the required current before we enable FRS. - */ - if (payload & PDO_FIXED_DUAL_ROLE) { - switch (payload & - PDO_FIXED_FRS_CURR_MASK) { - case PDO_FIXED_FRS_CURR_NOT_SUPPORTED: - break; - case PDO_FIXED_FRS_CURR_DFLT_USB_POWER: - case PDO_FIXED_FRS_CURR_1A5_AT_5V: - case PDO_FIXED_FRS_CURR_3A0_AT_5V: - pe_set_frs_enable(port, 1); - return; - } - } - set_state_pe(port, PE_SNK_READY); - return; - } else if ((type == PD_CTRL_REJECT) || - (type == PD_CTRL_WAIT)) { - set_state_pe(port, PE_SNK_READY); - return; - } - } - } - - /* - * Transition to PE_SNK_Ready state when: - * 1) SenderResponseTimer times out. - */ - if (get_time().val > pe[port].sender_response_timer) - set_state_pe(port, PE_SNK_READY); -} - -/* Policy Engine utility functions */ -int pd_check_requested_voltage(uint32_t rdo, const int port) -{ - int max_ma = rdo & 0x3FF; - int op_ma = (rdo >> 10) & 0x3FF; - int idx = RDO_POS(rdo); - uint32_t pdo; - uint32_t pdo_ma; -#if defined(CONFIG_USB_PD_DYNAMIC_SRC_CAP) || \ - defined(CONFIG_USB_PD_MAX_SINGLE_SOURCE_CURRENT) - const uint32_t *src_pdo; - const int pdo_cnt = charge_manager_get_source_pdo(&src_pdo, port); -#else - const uint32_t *src_pdo = pd_src_pdo; - const int pdo_cnt = pd_src_pdo_cnt; -#endif - - /* Board specific check for this request */ - if (pd_board_check_request(rdo, pdo_cnt)) - return EC_ERROR_INVAL; - - /* check current ... */ - pdo = 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 && !(rdo & RDO_CAP_MISMATCH)) - return EC_ERROR_INVAL; /* too much max current */ - - CPRINTF("Requested %d mV %d mA (for %d/%d mA)\n", - ((pdo >> 10) & 0x3ff) * 50, (pdo & 0x3ff) * 10, - op_ma * 10, max_ma * 10); - - /* Accept the requested voltage */ - return EC_SUCCESS; -} - -void pd_process_source_cap(int port, int cnt, uint32_t *src_caps) -{ -#ifdef CONFIG_CHARGE_MANAGER - uint32_t ma, mv, pdo; -#endif - int i; - - pe[port].src_cap_cnt = cnt; - for (i = 0; i < cnt; i++) - pe[port].src_caps[i] = *src_caps++; - -#ifdef CONFIG_CHARGE_MANAGER - /* Get max power info that we could request */ - pd_find_pdo_index(pe[port].src_cap_cnt, pe[port].src_caps, - PD_MAX_VOLTAGE_MV, &pdo); - pd_extract_pdo_power(pdo, &ma, &mv); - /* Set max. limit, but apply 500mA ceiling */ - charge_manager_set_ceil(port, CEIL_REQUESTOR_PD, PD_MIN_MA); - pd_set_input_current_limit(port, ma, mv); -#endif -} - -void pd_set_max_voltage(unsigned int mv) -{ - max_request_mv = mv; -} - -unsigned int pd_get_max_voltage(void) -{ - return max_request_mv; -} - -int pd_charge_from_device(uint16_t vid, uint16_t pid) -{ - /* TODO: rewrite into table if we get more of these */ - /* - * Allow-list Apple charge-through accessory since it doesn't set - * externally powered bit, but we still need to charge from it when - * we are a sink. - */ - return (vid == USB_VID_APPLE && - (pid == USB_PID1_APPLE || pid == USB_PID2_APPLE)); -} - -void pd_set_vbus_discharge(int port, int enable) -{ - static struct mutex discharge_lock[CONFIG_USB_PD_PORT_MAX_COUNT]; - - if (port >= board_get_usb_pd_port_count()) - return; - - mutex_lock(&discharge_lock[port]); - enable &= !board_vbus_source_enabled(port); - - if (IS_ENABLED(CONFIG_USB_PD_DISCHARGE_GPIO)) { - switch (port) { -#if defined(CONFIG_USB_PD_DISCHARGE_GPIO) && CONFIG_USB_PD_PORT_MAX_COUNT >= 3 - case 2: - gpio_set_level(GPIO_USB_C2_DISCHARGE, enable); - break; -#endif -#if defined(CONFIG_USB_PD_DISCHARGE_GPIO) && CONFIG_USB_PD_PORT_MAX_COUNT >= 2 - case 1: - gpio_set_level(GPIO_USB_C1_DISCHARGE, enable); - break; -#endif -#if defined(CONFIG_USB_PD_DISCHARGE_GPIO) && CONFIG_USB_PD_PORT_MAX_COUNT >= 1 - case 0: - gpio_set_level(GPIO_USB_C0_DISCHARGE, enable); - break; -#endif - default: - CPRINTF("Could not discharge port %d via GPIO", port); - break; - } - } else if (IS_ENABLED(CONFIG_USB_PD_DISCHARGE_TCPC)) { - tcpc_discharge_vbus(port, enable); - } else if (IS_ENABLED(CONFIG_USB_PD_DISCHARGE_PPC)) { - ppc_discharge_vbus(port, enable); - } - - mutex_unlock(&discharge_lock[port]); -} - -/* VDM utility functions */ -#ifdef CONFIG_USB_PD_ALT_MODE_DFP -static void pd_usb_billboard_deferred(void) -{ -#if defined(CONFIG_USB_PD_ALT_MODE) && !defined(CONFIG_USB_PD_ALT_MODE_DFP) \ - && !defined(CONFIG_USB_PD_SIMPLE_DFP) && defined(CONFIG_USB_BOS) - - /* - * TODO(tbroch) - * 1. Will we have multiple type-C port UFPs - * 2. Will there be other modes applicable to DFPs besides DP - */ - if (!pd_alt_mode(0, USB_SID_DISPLAYPORT)) - usb_connect(); - -#endif -} -DECLARE_DEFERRED(pd_usb_billboard_deferred); - -void pd_dfp_pe_init(int port) -{ - memset(&pe[port].am_policy, 0, sizeof(struct pd_policy)); -} - -#ifdef CONFIG_USB_PD_ALT_MODE_DFP -static void dfp_consume_identity(int port, int cnt, uint32_t *payload) -{ - int ptype = PD_IDH_PTYPE(payload[VDO_I(IDH)]); - size_t identity_size = MIN(sizeof(pe[port].am_policy.identity), - (cnt - 1) * sizeof(uint32_t)); - - pd_dfp_pe_init(port); - memcpy(&pe[port].am_policy.identity, payload + 1, identity_size); - - switch (ptype) { - case IDH_PTYPE_AMA: -/* Leave vbus ON if the following macro is false */ -#if defined(CONFIG_USB_PD_DUAL_ROLE) && defined(CONFIG_USBC_VCONN_SWAP) - /* Adapter is requesting vconn, try to supply it */ - if (PD_VDO_AMA_VCONN_REQ(payload[VDO_I(AMA)])) - tc_vconn_on(port); - - /* Only disable vbus if vconn was requested */ - if (PD_VDO_AMA_VCONN_REQ(payload[VDO_I(AMA)]) && - !PD_VDO_AMA_VBUS_REQ(payload[VDO_I(AMA)])) - pd_power_supply_reset(port); -#endif - break; - default: - break; - } -} - -static void dfp_consume_svids(int port, int cnt, uint32_t *payload) -{ - int i; - uint32_t *ptr = payload + 1; - int vdo = 1; - uint16_t svid0, svid1; - - for (i = pe[port].am_policy.svid_cnt; - i < pe[port].am_policy.svid_cnt + 12; i += 2) { - if (i == SVID_DISCOVERY_MAX) { - CPRINTF("ERR:SVIDCNT\n"); - break; - } - /* - * Verify we're still within the valid packet (count will be one - * for the VDM header + xVDOs) - */ - if (vdo >= cnt) - break; - - svid0 = PD_VDO_SVID_SVID0(*ptr); - if (!svid0) - break; - pe[port].am_policy.svids[i].svid = svid0; - pe[port].am_policy.svid_cnt++; - - svid1 = PD_VDO_SVID_SVID1(*ptr); - if (!svid1) - break; - pe[port].am_policy.svids[i + 1].svid = svid1; - pe[port].am_policy.svid_cnt++; - ptr++; - vdo++; - } - - /* TODO(tbroch) need to re-issue discover svids if > 12 */ - if (i && ((i % 12) == 0)) - CPRINTF("ERR:SVID+12\n"); -} - -static int dfp_discover_modes(int port, uint32_t *payload) -{ - uint16_t svid = - pe[port].am_policy.svids[pe[port].am_policy.svid_idx].svid; - - if (pe[port].am_policy.svid_idx >= pe[port].am_policy.svid_cnt) - return 0; - - payload[0] = VDO(svid, 1, CMD_DISCOVER_MODES); - - return 1; -} - -static void dfp_consume_modes(int port, int cnt, uint32_t *payload) -{ - int idx = pe[port].am_policy.svid_idx; - - pe[port].am_policy.svids[idx].mode_cnt = cnt - 1; - - if (pe[port].am_policy.svids[idx].mode_cnt < 0) { - CPRINTF("ERR:NOMODE\n"); - } else { - memcpy( - pe[port].am_policy.svids[pe[port].am_policy.svid_idx].mode_vdo, - &payload[1], - sizeof(uint32_t) * pe[port].am_policy.svids[idx].mode_cnt); - } - - pe[port].am_policy.svid_idx++; -} - -static int get_mode_idx(int port, uint16_t svid) -{ - int i; - - for (i = 0; i < PD_AMODE_COUNT; i++) { - if (pe[port].am_policy.amodes[i].fx->svid == svid) - return i; - } - - return -1; -} - -static struct svdm_amode_data *get_modep(int port, uint16_t svid) -{ - int idx = get_mode_idx(port, svid); - - return (idx == -1) ? NULL : &pe[port].am_policy.amodes[idx]; -} - -int pd_alt_mode(int port, uint16_t svid) -{ - struct svdm_amode_data *modep = get_modep(port, svid); - - return (modep) ? modep->opos : -1; -} - -int allocate_mode(int port, uint16_t svid) -{ - int i, j; - struct svdm_amode_data *modep; - int mode_idx = get_mode_idx(port, svid); - - if (mode_idx != -1) - return mode_idx; - - /* There's no space to enter another mode */ - if (pe[port].am_policy.amode_idx == PD_AMODE_COUNT) { - CPRINTF("ERR:NO AMODE SPACE\n"); - return -1; - } - - /* Allocate ... if SVID == 0 enter default supported policy */ - for (i = 0; i < supported_modes_cnt; i++) { - if (!&supported_modes[i]) - continue; - - for (j = 0; j < pe[port].am_policy.svid_cnt; j++) { - struct svdm_svid_data *svidp = - &pe[port].am_policy.svids[j]; - - if ((svidp->svid != supported_modes[i].svid) || - (svid && (svidp->svid != svid))) - continue; - - modep = - &pe[port].am_policy.amodes[pe[port].am_policy.amode_idx]; - modep->fx = &supported_modes[i]; - modep->data = &pe[port].am_policy.svids[j]; - pe[port].am_policy.amode_idx++; - return pe[port].am_policy.amode_idx - 1; - } - } - return -1; -} - -uint32_t pd_dfp_enter_mode(int port, uint16_t svid, int opos) -{ - int mode_idx = allocate_mode(port, svid); - struct svdm_amode_data *modep; - uint32_t mode_caps; - - if (mode_idx == -1) - return 0; - - modep = &pe[port].am_policy.amodes[mode_idx]; - - if (!opos) { - /* choose the lowest as default */ - modep->opos = 1; - } else if (opos <= modep->data->mode_cnt) { - modep->opos = opos; - } else { - CPRINTF("opos error\n"); - return 0; - } - - mode_caps = modep->data->mode_vdo[modep->opos - 1]; - if (modep->fx->enter(port, mode_caps) == -1) - return 0; - - PE_SET_FLAG(port, PE_FLAGS_MODAL_OPERATION); - - /* SVDM to send to UFP for mode entry */ - return VDO(modep->fx->svid, 1, CMD_ENTER_MODE | VDO_OPOS(modep->opos)); -} - -static int validate_mode_request(struct svdm_amode_data *modep, - uint16_t svid, int opos) -{ - if (!modep->fx) - return 0; - - if (svid != modep->fx->svid) { - CPRINTF("ERR:svid r:0x%04x != c:0x%04x\n", - svid, modep->fx->svid); - return 0; - } - - if (opos != modep->opos) { - CPRINTF("ERR:opos r:%d != c:%d\n", - opos, modep->opos); - return 0; - } - - return 1; -} - -static void dfp_consume_attention(int port, uint32_t *payload) -{ - uint16_t svid = PD_VDO_VID(payload[0]); - int opos = PD_VDO_OPOS(payload[0]); - struct svdm_amode_data *modep = get_modep(port, svid); - - if (!modep || !validate_mode_request(modep, svid, opos)) - return; - - if (modep->fx->attention) - modep->fx->attention(port, payload); -} -#endif -/* - * This algorithm defaults to choosing higher pin config over lower ones in - * order to prefer multi-function if desired. - * - * NAME | SIGNALING | OUTPUT TYPE | MULTI-FUNCTION | PIN CONFIG - * ------------------------------------------------------------- - * A | USB G2 | ? | no | 00_0001 - * B | USB G2 | ? | yes | 00_0010 - * C | DP | CONVERTED | no | 00_0100 - * D | PD | CONVERTED | yes | 00_1000 - * E | DP | DP | no | 01_0000 - * F | PD | DP | yes | 10_0000 - * - * if UFP has NOT asserted multi-function preferred code masks away B/D/F - * leaving only A/C/E. For single-output dongles that should leave only one - * possible pin config depending on whether its a converter DP->(VGA|HDMI) or DP - * output. If UFP is a USB-C receptacle it may assert C/D/E/F. The DFP USB-C - * receptacle must always choose C/D in those cases. - */ -int pd_dfp_dp_get_pin_mode(int port, uint32_t status) -{ - struct svdm_amode_data *modep = get_modep(port, USB_SID_DISPLAYPORT); - uint32_t mode_caps; - uint32_t pin_caps; - - if (!modep) - return 0; - - mode_caps = modep->data->mode_vdo[modep->opos - 1]; - - /* TODO(crosbug.com/p/39656) revisit with DFP that can be a sink */ - pin_caps = PD_DP_PIN_CAPS(mode_caps); - - /* if don't want multi-function then ignore those pin configs */ - if (!PD_VDO_DPSTS_MF_PREF(status)) - pin_caps &= ~MODE_DP_PIN_MF_MASK; - - /* TODO(crosbug.com/p/39656) revisit if DFP drives USB Gen 2 signals */ - pin_caps &= ~MODE_DP_PIN_BR2_MASK; - - /* if C/D present they have precedence over E/F for USB-C->USB-C */ - if (pin_caps & (MODE_DP_PIN_C | MODE_DP_PIN_D)) - pin_caps &= ~(MODE_DP_PIN_E | MODE_DP_PIN_F); - - /* get_next_bit returns undefined for zero */ - if (!pin_caps) - return 0; - - return 1 << get_next_bit(&pin_caps); -} - -int pd_dfp_exit_mode(int port, uint16_t svid, int opos) -{ - struct svdm_amode_data *modep; - int idx; - - - /* - * Empty svid signals we should reset DFP VDM state by exiting all - * entered modes then clearing state. This occurs when we've - * disconnected or for hard reset. - */ - if (!svid) { - for (idx = 0; idx < PD_AMODE_COUNT; idx++) - if (pe[port].am_policy.amodes[idx].fx) - pe[port].am_policy.amodes[idx].fx->exit(port); - - pd_dfp_pe_init(port); - return 0; - } - - /* - * TODO(crosbug.com/p/33946) : below needs revisited to allow multiple - * mode exit. Additionally it should honor OPOS == 7 as DFP's request - * to exit all modes. We currently don't have any UFPs that support - * multiple modes on one SVID. - */ - modep = get_modep(port, svid); - if (!modep || !validate_mode_request(modep, svid, opos)) - return 0; - - /* call DFPs exit function */ - modep->fx->exit(port); - - PE_CLR_FLAG(port, PE_FLAGS_MODAL_OPERATION); - - /* exit the mode */ - modep->opos = 0; - - return 1; -} - -uint16_t pd_get_identity_vid(int port) -{ - return PD_IDH_VID(pe[port].am_policy.identity[0]); -} - -uint16_t pd_get_identity_pid(int port) -{ - return PD_PRODUCT_PID(pe[port].am_policy.identity[2]); -} - - -#ifdef CONFIG_CMD_USB_PD_PE -static void dump_pe(int port) -{ - const char * const idh_ptype_names[] = { - "UNDEF", "Hub", "Periph", "PCable", "ACable", "AMA", - "RSV6", "RSV7"}; - - int i, j, idh_ptype; - struct svdm_amode_data *modep; - uint32_t mode_caps; - - if (pe[port].am_policy.identity[0] == 0) { - ccprintf("No identity discovered yet.\n"); - return; - } - idh_ptype = PD_IDH_PTYPE(pe[port].am_policy.identity[0]); - ccprintf("IDENT:\n"); - ccprintf("\t[ID Header] %08x :: %s, VID:%04x\n", - pe[port].am_policy.identity[0], - idh_ptype_names[idh_ptype], - pd_get_identity_vid(port)); - ccprintf("\t[Cert Stat] %08x\n", pe[port].am_policy.identity[1]); - for (i = 2; i < ARRAY_SIZE(pe[port].am_policy.identity); i++) { - ccprintf("\t"); - if (pe[port].am_policy.identity[i]) - ccprintf("[%d] %08x ", i, - pe[port].am_policy.identity[i]); - } - ccprintf("\n"); - - if (pe[port].am_policy.svid_cnt < 1) { - ccprintf("No SVIDS discovered yet.\n"); - return; - } - - for (i = 0; i < pe[port].am_policy.svid_cnt; i++) { - ccprintf("SVID[%d]: %04x MODES:", i, - pe[port].am_policy.svids[i].svid); - for (j = 0; j < pe[port].am_policy.svids[j].mode_cnt; j++) - ccprintf(" [%d] %08x", j + 1, - pe[port].am_policy.svids[i].mode_vdo[j]); - ccprintf("\n"); - modep = get_modep(port, pe[port].am_policy.svids[i].svid); - if (modep) { - mode_caps = modep->data->mode_vdo[modep->opos - 1]; - ccprintf("MODE[%d]: svid:%04x caps:%08x\n", modep->opos, - modep->fx->svid, 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 >= board_get_usb_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"); -#endif /* CONFIG_CMD_USB_PD_PE */ - -static enum ec_status hc_remote_pd_discovery(struct host_cmd_handler_args *args) -{ - const uint8_t *port = args->params; - struct ec_params_usb_pd_discovery_entry *r = args->response; - - if (*port >= board_get_usb_pd_port_count()) - return EC_RES_INVALID_PARAM; - - r->vid = pd_get_identity_vid(*port); - r->ptype = PD_IDH_PTYPE(pe[*port].am_policy.identity[0]); - - /* pid only included if vid is assigned */ - if (r->vid) - r->pid = PD_PRODUCT_PID(pe[*port].am_policy.identity[2]); - - args->response_size = sizeof(*r); - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_USB_PD_DISCOVERY, - hc_remote_pd_discovery, - EC_VER_MASK(0)); - -static enum ec_status hc_remote_pd_get_amode(struct host_cmd_handler_args *args) -{ - struct svdm_amode_data *modep; - const struct ec_params_usb_pd_get_mode_request *p = args->params; - struct ec_params_usb_pd_get_mode_response *r = args->response; - - if (p->port >= board_get_usb_pd_port_count()) - return EC_RES_INVALID_PARAM; - - /* no more to send */ - if (p->svid_idx >= pe[p->port].am_policy.svid_cnt) { - r->svid = 0; - args->response_size = sizeof(r->svid); - return EC_RES_SUCCESS; - } - - r->svid = pe[p->port].am_policy.svids[p->svid_idx].svid; - r->opos = 0; - memcpy(r->vdo, pe[p->port].am_policy.svids[p->svid_idx].mode_vdo, 24); - modep = get_modep(p->port, r->svid); - - if (modep) - r->opos = pd_alt_mode(p->port, r->svid); - - args->response_size = sizeof(*r); - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_USB_PD_GET_AMODE, - hc_remote_pd_get_amode, - EC_VER_MASK(0)); - -#endif /* CONFIG_USB_PD_ALT_MODE_DFP */ - -static const struct usb_state pe_states[] = { - /* Super States */ - [PE_PRS_FRS_SHARED] = { - .entry = pe_prs_frs_shared_entry, - .exit = pe_prs_frs_shared_exit, - }, - - /* Normal States */ - [PE_SRC_STARTUP] = { - .entry = pe_src_startup_entry, - .run = pe_src_startup_run, - }, - [PE_SRC_DISCOVERY] = { - .entry = pe_src_discovery_entry, - .run = pe_src_discovery_run, - }, - [PE_SRC_SEND_CAPABILITIES] = { - .entry = pe_src_send_capabilities_entry, - .run = pe_src_send_capabilities_run, - }, - [PE_SRC_NEGOTIATE_CAPABILITY] = { - .entry = pe_src_negotiate_capability_entry, - }, - [PE_SRC_TRANSITION_SUPPLY] = { - .entry = pe_src_transition_supply_entry, - .run = pe_src_transition_supply_run, - }, - [PE_SRC_READY] = { - .entry = pe_src_ready_entry, - .run = pe_src_ready_run, - .exit = pe_src_ready_exit, - }, - [PE_SRC_DISABLED] = { - .entry = pe_src_disabled_entry, - }, - [PE_SRC_CAPABILITY_RESPONSE] = { - .entry = pe_src_capability_response_entry, - .run = pe_src_capability_response_run, - }, - [PE_SRC_HARD_RESET] = { - .entry = pe_src_hard_reset_entry, - .run = pe_src_hard_reset_run, - }, - [PE_SRC_HARD_RESET_RECEIVED] = { - .entry = pe_src_hard_reset_received_entry, - .run = pe_src_hard_reset_received_run, - }, - [PE_SRC_TRANSITION_TO_DEFAULT] = { - .entry = pe_src_transition_to_default_entry, - .run = pe_src_transition_to_default_run, - }, - [PE_SNK_STARTUP] = { - .entry = pe_snk_startup_entry, - .run = pe_snk_startup_run, - }, - [PE_SNK_DISCOVERY] = { - .entry = pe_snk_discovery_entry, - .run = pe_snk_discovery_run, - }, - [PE_SNK_WAIT_FOR_CAPABILITIES] = { - .entry = pe_snk_wait_for_capabilities_entry, - .run = pe_snk_wait_for_capabilities_run, - }, - [PE_SNK_EVALUATE_CAPABILITY] = { - .entry = pe_snk_evaluate_capability_entry, - }, - [PE_SNK_SELECT_CAPABILITY] = { - .entry = pe_snk_select_capability_entry, - .run = pe_snk_select_capability_run, - }, - [PE_SNK_READY] = { - .entry = pe_snk_ready_entry, - .run = pe_snk_ready_run, - .exit = pe_snk_ready_exit, - }, - [PE_SNK_HARD_RESET] = { - .entry = pe_snk_hard_reset_entry, - .run = pe_snk_hard_reset_run, - }, - [PE_SNK_TRANSITION_TO_DEFAULT] = { - .entry = pe_snk_transition_to_default_entry, - .run = pe_snk_transition_to_default_run, - }, - [PE_SNK_GIVE_SINK_CAP] = { - .entry = pe_snk_give_sink_cap_entry, - .run = pe_snk_give_sink_cap_run, - }, - [PE_SNK_GET_SOURCE_CAP] = { - .entry = pe_snk_get_source_cap_entry, - .run = pe_snk_get_source_cap_run, - }, - [PE_SNK_TRANSITION_SINK] = { - .entry = pe_snk_transition_sink_entry, - .run = pe_snk_transition_sink_run, - .exit = pe_snk_transition_sink_exit, - }, - [PE_SEND_SOFT_RESET] = { - .entry = pe_send_soft_reset_entry, - .run = pe_send_soft_reset_run, - .exit = pe_send_soft_reset_exit, - }, - [PE_SOFT_RESET] = { - .entry = pe_soft_reset_entry, - .run = pe_soft_reset_run, - }, - [PE_SEND_NOT_SUPPORTED] = { - .entry = pe_send_not_supported_entry, - .run = pe_send_not_supported_run, - }, - [PE_SRC_PING] = { - .entry = pe_src_ping_entry, - .run = pe_src_ping_run, - }, - [PE_GIVE_BATTERY_CAP] = { - .entry = pe_give_battery_cap_entry, - .run = pe_give_battery_cap_run, - }, - [PE_GIVE_BATTERY_STATUS] = { - .entry = pe_give_battery_status_entry, - .run = pe_give_battery_status_run, - }, - [PE_DRS_EVALUATE_SWAP] = { - .entry = pe_drs_evaluate_swap_entry, - .run = pe_drs_evaluate_swap_run, - }, - [PE_DRS_CHANGE] = { - .entry = pe_drs_change_entry, - .run = pe_drs_change_run, - }, - [PE_DRS_SEND_SWAP] = { - .entry = pe_drs_send_swap_entry, - .run = pe_drs_send_swap_run, - }, - [PE_PRS_SRC_SNK_EVALUATE_SWAP] = { - .entry = pe_prs_src_snk_evaluate_swap_entry, - .run = pe_prs_src_snk_evaluate_swap_run, - }, - [PE_PRS_SRC_SNK_TRANSITION_TO_OFF] = { - .entry = pe_prs_src_snk_transition_to_off_entry, - .run = pe_prs_src_snk_transition_to_off_run, - }, - [PE_PRS_SRC_SNK_WAIT_SOURCE_ON] = { - .entry = pe_prs_src_snk_wait_source_on_entry, - .run = pe_prs_src_snk_wait_source_on_run, - }, - [PE_PRS_SRC_SNK_SEND_SWAP] = { - .entry = pe_prs_src_snk_send_swap_entry, - .run = pe_prs_src_snk_send_swap_run, - .exit = pe_prs_src_snk_send_swap_exit, - }, - [PE_PRS_SNK_SRC_EVALUATE_SWAP] = { - .entry = pe_prs_snk_src_evaluate_swap_entry, - .run = pe_prs_snk_src_evaluate_swap_run, - }, - /* - * Some of the Power Role Swap actions are shared with the very - * similar actions of Fast Role Swap. - */ - /* State actions are shared with PE_FRS_SNK_SRC_TRANSITION_TO_OFF */ - [PE_PRS_SNK_SRC_TRANSITION_TO_OFF] = { - .entry = pe_prs_snk_src_transition_to_off_entry, - .run = pe_prs_snk_src_transition_to_off_run, - .parent = &pe_states[PE_PRS_FRS_SHARED], - }, - /* State actions are shared with PE_FRS_SNK_SRC_ASSERT_RP */ - [PE_PRS_SNK_SRC_ASSERT_RP] = { - .entry = pe_prs_snk_src_assert_rp_entry, - .run = pe_prs_snk_src_assert_rp_run, - .parent = &pe_states[PE_PRS_FRS_SHARED], - }, - /* State actions are shared with PE_FRS_SNK_SRC_SOURCE_ON */ - [PE_PRS_SNK_SRC_SOURCE_ON] = { - .entry = pe_prs_snk_src_source_on_entry, - .run = pe_prs_snk_src_source_on_run, - .parent = &pe_states[PE_PRS_FRS_SHARED], - }, - /* State actions are shared with PE_FRS_SNK_SRC_SEND_SWAP */ - [PE_PRS_SNK_SRC_SEND_SWAP] = { - .entry = pe_prs_snk_src_send_swap_entry, - .run = pe_prs_snk_src_send_swap_run, - .exit = pe_prs_snk_src_send_swap_exit, - .parent = &pe_states[PE_PRS_FRS_SHARED], - }, - [PE_FRS_SNK_SRC_START_AMS] = { - .entry = pe_frs_snk_src_start_ams_entry, - .parent = &pe_states[PE_PRS_FRS_SHARED], - }, - [PE_VCS_EVALUATE_SWAP] = { - .entry = pe_vcs_evaluate_swap_entry, - .run = pe_vcs_evaluate_swap_run, - }, - [PE_VCS_SEND_SWAP] = { - .entry = pe_vcs_send_swap_entry, - .run = pe_vcs_send_swap_run, - }, - [PE_VCS_WAIT_FOR_VCONN_SWAP] = { - .entry = pe_vcs_wait_for_vconn_swap_entry, - .run = pe_vcs_wait_for_vconn_swap_run, - }, - [PE_VCS_TURN_ON_VCONN_SWAP] = { - .entry = pe_vcs_turn_on_vconn_swap_entry, - .run = pe_vcs_turn_on_vconn_swap_run, - }, - [PE_VCS_TURN_OFF_VCONN_SWAP] = { - .entry = pe_vcs_turn_off_vconn_swap_entry, - .run = pe_vcs_turn_off_vconn_swap_run, - }, - [PE_VCS_SEND_PS_RDY_SWAP] = { - .entry = pe_vcs_send_ps_rdy_swap_entry, - .run = pe_vcs_send_ps_rdy_swap_run, - }, - [PE_DO_PORT_DISCOVERY] = { - .entry = pe_do_port_discovery_entry, - .run = pe_do_port_discovery_run, - }, - [PE_VDM_REQUEST] = { - .entry = pe_vdm_request_entry, - .run = pe_vdm_request_run, - .exit = pe_vdm_request_exit, - }, - [PE_VDM_ACKED] = { - .entry = pe_vdm_acked_entry, - }, - [PE_VDM_RESPONSE] = { - .entry = pe_vdm_response_entry, - .run = pe_vdm_response_run, - }, - [PE_HANDLE_CUSTOM_VDM_REQUEST] = { - .entry = pe_handle_custom_vdm_request_entry, - .run = pe_handle_custom_vdm_request_run - }, - [PE_WAIT_FOR_ERROR_RECOVERY] = { - .entry = pe_wait_for_error_recovery_entry, - .run = pe_wait_for_error_recovery_run, - }, - [PE_BIST] = { - .entry = pe_bist_entry, - .run = pe_bist_run, - }, - [PE_DR_SNK_GET_SINK_CAP] = { - .entry = pe_dr_snk_get_sink_cap_entry, - .run = pe_dr_snk_get_sink_cap_run, - }, -}; - -#ifdef TEST_BUILD -const struct test_sm_data test_pe_sm_data[] = { - { - .base = pe_states, - .size = ARRAY_SIZE(pe_states), - .names = pe_state_names, - .names_size = ARRAY_SIZE(pe_state_names), - }, -}; -const int test_pe_sm_data_size = ARRAY_SIZE(test_pe_sm_data); - -void pe_set_flag(int port, int flag) -{ - PE_SET_FLAG(port, flag); -} -void pe_clr_flag(int port, int flag) -{ - PE_CLR_FLAG(port, flag); -} -int pe_chk_flag(int port, int flag) -{ - return PE_CHK_FLAG(port, flag); -} -int pe_get_all_flags(int port) -{ - return pe[port].flags; -} -void pe_set_all_flags(int port, int flags) -{ - pe[port].flags = flags; -} -#endif diff --git a/common/usbc/usb_prl_sm.c b/common/usbc/usb_prl_sm.c deleted file mode 100644 index 9a78b59474..0000000000 --- a/common/usbc/usb_prl_sm.c +++ /dev/null @@ -1,1762 +0,0 @@ -/* Copyright 2019 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 "battery.h" -#include "battery_smart.h" -#include "board.h" -#include "charge_manager.h" -#include "charge_state.h" -#include "chipset.h" -#include "common.h" -#include "console.h" -#include "ec_commands.h" -#include "gpio.h" -#include "hooks.h" -#include "host_command.h" -#include "registers.h" -#include "system.h" -#include "task.h" -#include "timer.h" -#include "tcpm.h" -#include "util.h" -#include "usb_charge.h" -#include "usb_mux.h" -#include "usb_pd.h" -#include "usb_pe_sm.h" -#include "usb_prl_sm.h" -#include "usb_tc_sm.h" -#include "usb_emsg.h" -#include "usb_sm.h" -#include "vpd_api.h" -#include "version.h" - -#define RCH_SET_FLAG(port, flag) atomic_or(&rch[port].flags, (flag)) -#define RCH_CLR_FLAG(port, flag) atomic_clear(&rch[port].flags, (flag)) -#define RCH_CHK_FLAG(port, flag) (rch[port].flags & (flag)) - -#define TCH_SET_FLAG(port, flag) atomic_or(&tch[port].flags, (flag)) -#define TCH_CLR_FLAG(port, flag) atomic_clear(&tch[port].flags, (flag)) -#define TCH_CHK_FLAG(port, flag) (tch[port].flags & (flag)) - -#define PRL_TX_SET_FLAG(port, flag) atomic_or(&prl_tx[port].flags, (flag)) -#define PRL_TX_CLR_FLAG(port, flag) atomic_clear(&prl_tx[port].flags, (flag)) -#define PRL_TX_CHK_FLAG(port, flag) (prl_tx[port].flags & (flag)) - -#define PRL_HR_SET_FLAG(port, flag) atomic_or(&prl_hr[port].flags, (flag)) -#define PRL_HR_CLR_FLAG(port, flag) atomic_clear(&prl_hr[port].flags, (flag)) -#define PRL_HR_CHK_FLAG(port, flag) (prl_hr[port].flags & (flag)) - -#define PDMSG_SET_FLAG(port, flag) atomic_or(&pdmsg[port].flags, (flag)) -#define PDMSG_CLR_FLAG(port, flag) atomic_clear(&pdmsg[port].flags, (flag)) -#define PDMSG_CHK_FLAG(port, flag) (pdmsg[port].flags & (flag)) - -/* Protocol Layer Flags */ -/* - * NOTE: - * These flags are used in multiple state machines and could have - * different meanings in each state machine. - */ -/* Flag to note message transmission completed */ -#define PRL_FLAGS_TX_COMPLETE BIT(0) -/* Flag to note an AMS is being started by PE */ -#define PRL_FLAGS_START_AMS BIT(1) -/* Flag to note an AMS is being stopped by PE */ -#define PRL_FLAGS_END_AMS BIT(2) -/* Flag to note transmission error occurred */ -#define PRL_FLAGS_TX_ERROR BIT(3) -/* Flag to note PE triggered a hard reset */ -#define PRL_FLAGS_PE_HARD_RESET BIT(4) -/* Flag to note hard reset has completed */ -#define PRL_FLAGS_HARD_RESET_COMPLETE BIT(5) -/* Flag to note port partner sent a hard reset */ -#define PRL_FLAGS_PORT_PARTNER_HARD_RESET BIT(6) -/* Flag to note a message transmission has been requested */ -#define PRL_FLAGS_MSG_XMIT BIT(7) -/* Flag to note a message was received */ -#define PRL_FLAGS_MSG_RECEIVED BIT(8) -/* Flag to note aborting current TX message, not currently set */ -#define PRL_FLAGS_ABORT BIT(9) -/* Flag to note current TX message uses chunking */ -#define PRL_FLAGS_CHUNKING BIT(10) - -/* PD counter definitions */ -#define PD_MESSAGE_ID_COUNT 7 - -static enum sm_local_state local_state[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/* Protocol Transmit States (Section 6.11.2.2) */ -enum usb_prl_tx_state { - PRL_TX_PHY_LAYER_RESET, - PRL_TX_WAIT_FOR_MESSAGE_REQUEST, - PRL_TX_LAYER_RESET_FOR_TRANSMIT, - PRL_TX_WAIT_FOR_PHY_RESPONSE, - PRL_TX_SRC_SOURCE_TX, - PRL_TX_SNK_START_AMS, - PRL_TX_SRC_PENDING, - PRL_TX_SNK_PENDING, - PRL_TX_DISCARD_MESSAGE, -}; - -/* Protocol Hard Reset States (Section 6.11.2.4) */ -enum usb_prl_hr_state { - PRL_HR_WAIT_FOR_REQUEST, - PRL_HR_RESET_LAYER, - PRL_HR_WAIT_FOR_PHY_HARD_RESET_COMPLETE, - PRL_HR_WAIT_FOR_PE_HARD_RESET_COMPLETE, -}; - -/* Chunked Rx states (Section 6.11.2.1.2) */ -enum usb_rch_state { - RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER, - RCH_PASS_UP_MESSAGE, - RCH_PROCESSING_EXTENDED_MESSAGE, - RCH_REQUESTING_CHUNK, - RCH_WAITING_CHUNK, - RCH_REPORT_ERROR, -}; - -/* Chunked Tx states (Section 6.11.2.1.3) */ -enum usb_tch_state { - TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE, - TCH_WAIT_FOR_TRANSMISSION_COMPLETE, - TCH_CONSTRUCT_CHUNKED_MESSAGE, - TCH_SENDING_CHUNKED_MESSAGE, - TCH_WAIT_CHUNK_REQUEST, - TCH_MESSAGE_RECEIVED, - TCH_MESSAGE_SENT, - TCH_REPORT_ERROR, -}; - -/* Forward declare full list of states. Index by above enums. */ -static const struct usb_state prl_tx_states[]; -static const struct usb_state prl_hr_states[]; -static const struct usb_state rch_states[]; -static const struct usb_state tch_states[]; - - -/* Chunked Rx State Machine Object */ -static struct rx_chunked { - /* state machine context */ - struct sm_ctx ctx; - /* PRL_FLAGS */ - uint32_t flags; - /* protocol timer */ - uint64_t chunk_sender_response_timer; -} rch[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/* Chunked Tx State Machine Object */ -static struct tx_chunked { - /* state machine context */ - struct sm_ctx ctx; - /* state machine flags */ - uint32_t flags; - /* protocol timer */ - uint64_t chunk_sender_request_timer; - /* error to report when moving to tch_report_error state */ - enum pe_error error; -} tch[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/* Message Reception State Machine Object */ -static struct protocol_layer_rx { - /* message ids for all valid port partners */ - int msg_id[NUM_SOP_STAR_TYPES]; -} prl_rx[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/* Message Transmission State Machine Object */ -static struct protocol_layer_tx { - /* state machine context */ - struct sm_ctx ctx; - /* state machine flags */ - uint32_t flags; - /* protocol timer */ - uint64_t sink_tx_timer; - /* tcpc transmit timeout */ - uint64_t tcpc_tx_timeout; - /* last message type we transmitted */ - enum tcpm_transmit_type last_xmit_type; - /* message id counters for all 6 port partners */ - uint32_t msg_id_counter[NUM_SOP_STAR_TYPES]; - /* message retry counter */ - uint32_t retry_counter; - /* transmit status */ - int xmit_status; -} prl_tx[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/* Hard Reset State Machine Object */ -static struct protocol_hard_reset { - /* state machine context */ - struct sm_ctx ctx; - /* state machine flags */ - uint32_t flags; - /* protocol timer */ - uint64_t hard_reset_complete_timer; -} prl_hr[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/* Chunking Message Object */ -static struct pd_message { - /* message status flags */ - uint32_t flags; - /* SOP* */ - enum tcpm_transmit_type xmit_type; - /* type of message */ - uint8_t msg_type; - /* extended message */ - uint8_t ext; - /* PD revision */ - enum pd_rev_type rev[NUM_SOP_STAR_TYPES]; - /* Number of 32-bit objects in chk_buf */ - uint16_t data_objs; - /* temp chunk buffer */ - uint32_t chk_buf[7]; - uint32_t chunk_number_expected; - uint32_t num_bytes_received; - uint32_t chunk_number_to_send; - uint32_t send_offset; -} pdmsg[CONFIG_USB_PD_PORT_MAX_COUNT]; - -struct extended_msg emsg[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/* Common Protocol Layer Message Transmission */ -static void prl_tx_construct_message(int port); -static void prl_rx_wait_for_phy_message(const int port, int evt); - -/* Set the protocol transmit statemachine to a new state. */ -static void set_state_prl_tx(const int port, - const enum usb_prl_tx_state new_state) -{ - set_state(port, &prl_tx[port].ctx, &prl_tx_states[new_state]); -} - -/* Get the protocol transmit statemachine's current state. */ -test_export_static enum usb_prl_tx_state prl_tx_get_state(const int port) -{ - return prl_tx[port].ctx.current - &prl_tx_states[0]; -} - -/* Set the hard reset statemachine to a new state. */ -static void set_state_prl_hr(const int port, - const enum usb_prl_hr_state new_state) -{ - set_state(port, &prl_hr[port].ctx, &prl_hr_states[new_state]); -} - -#ifdef TEST_BUILD -/* Get the hard reset statemachine's current state. */ -enum usb_prl_hr_state prl_hr_get_state(const int port) -{ - return prl_hr[port].ctx.current - &prl_hr_states[0]; -} -#endif - -/* Set the chunked Rx statemachine to a new state. */ -static void set_state_rch(const int port, const enum usb_rch_state new_state) -{ - set_state(port, &rch[port].ctx, &rch_states[new_state]); -} - -/* Get the chunked Rx statemachine's current state. */ -test_export_static enum usb_rch_state rch_get_state(const int port) -{ - return rch[port].ctx.current - &rch_states[0]; -} - -/* Set the chunked Tx statemachine to a new state. */ -static void set_state_tch(const int port, const enum usb_tch_state new_state) -{ - set_state(port, &tch[port].ctx, &tch_states[new_state]); -} - -/* Get the chunked Tx statemachine's current state. */ -test_export_static enum usb_tch_state tch_get_state(const int port) -{ - return tch[port].ctx.current - &tch_states[0]; -} - -void pd_transmit_complete(int port, int status) -{ - prl_tx[port].xmit_status = status; -} - -void pd_execute_hard_reset(int port) -{ - /* Only allow async. function calls when state machine is running */ - if (!prl_is_running(port)) - return; - - PRL_HR_SET_FLAG(port, PRL_FLAGS_PORT_PARTNER_HARD_RESET); - set_state_prl_hr(port, PRL_HR_RESET_LAYER); - task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SM, 0); -} - -void prl_execute_hard_reset(int port) -{ - /* Only allow async. function calls when state machine is running */ - if (!prl_is_running(port)) - return; - - PRL_HR_SET_FLAG(port, PRL_FLAGS_PE_HARD_RESET); - set_state_prl_hr(port, PRL_HR_RESET_LAYER); - task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SM, 0); -} - -int prl_is_running(int port) -{ - return local_state[port] == SM_RUN; -} - -static void prl_init(int port) -{ - int i; - const struct sm_ctx cleared = {}; - - prl_tx[port].flags = 0; - prl_tx[port].last_xmit_type = TCPC_TX_SOP; - prl_tx[port].xmit_status = TCPC_TX_UNSET; - - tch[port].flags = 0; - rch[port].flags = 0; - - /* - * Initialize to highest revision supported. If the port or cable - * partner doesn't support this revision, the Protocol Engine will - * lower this value to the revision supported by the partner. - */ - pdmsg[port].rev[TCPC_TX_SOP] = PD_REV30; - pdmsg[port].rev[TCPC_TX_SOP_PRIME] = PD_REV30; - pdmsg[port].rev[TCPC_TX_SOP_PRIME_PRIME] = PD_REV30; - pdmsg[port].rev[TCPC_TX_SOP_DEBUG_PRIME] = PD_REV30; - pdmsg[port].rev[TCPC_TX_SOP_DEBUG_PRIME_PRIME] = PD_REV30; - pdmsg[port].flags = 0; - - prl_hr[port].flags = 0; - - for (i = 0; i < NUM_SOP_STAR_TYPES; i++) { - prl_rx[port].msg_id[i] = -1; - prl_tx[port].msg_id_counter[i] = 0; - } - - /* Clear state machines and set initial states */ - prl_tx[port].ctx = cleared; - set_state_prl_tx(port, PRL_TX_PHY_LAYER_RESET); - - rch[port].ctx = cleared; - set_state_rch(port, RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER); - - tch[port].ctx = cleared; - set_state_tch(port, TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE); - - prl_hr[port].ctx = cleared; - set_state_prl_hr(port, PRL_HR_WAIT_FOR_REQUEST); -} - -void prl_start_ams(int port) -{ - PRL_TX_SET_FLAG(port, PRL_FLAGS_START_AMS); -} - -void prl_end_ams(int port) -{ - PRL_TX_SET_FLAG(port, PRL_FLAGS_END_AMS); -} - -void prl_hard_reset_complete(int port) -{ - PRL_HR_SET_FLAG(port, PRL_FLAGS_HARD_RESET_COMPLETE); - task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SM, 0); -} - -void prl_send_ctrl_msg(int port, - enum tcpm_transmit_type type, - enum pd_ctrl_msg_type msg) -{ - pdmsg[port].xmit_type = type; - pdmsg[port].msg_type = msg; - pdmsg[port].ext = 0; - emsg[port].len = 0; - - TCH_SET_FLAG(port, PRL_FLAGS_MSG_XMIT); - task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SM, 0); -} - -void prl_send_data_msg(int port, - enum tcpm_transmit_type type, - enum pd_data_msg_type msg) -{ - pdmsg[port].xmit_type = type; - pdmsg[port].msg_type = msg; - pdmsg[port].ext = 0; - - TCH_SET_FLAG(port, PRL_FLAGS_MSG_XMIT); - task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SM, 0); -} - -void prl_send_ext_data_msg(int port, - enum tcpm_transmit_type type, - enum pd_ext_msg_type msg) -{ - pdmsg[port].xmit_type = type; - pdmsg[port].msg_type = msg; - pdmsg[port].ext = 1; - - TCH_SET_FLAG(port, PRL_FLAGS_MSG_XMIT); - task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SM, 0); -} - -void prl_reset(int port) -{ - local_state[port] = SM_INIT; -} - -void prl_run(int port, int evt, int en) -{ - switch (local_state[port]) { - case SM_PAUSED: - if (!en) - break; - /* fall through */ - case SM_INIT: - prl_init(port); - local_state[port] = SM_RUN; - /* fall through */ - case SM_RUN: - /* If disabling, wait until message is sent. */ - if (!en && tch_get_state(port) == - TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE) { - - /* Disable RX */ - if (IS_ENABLED(CONFIG_USB_TYPEC_CTVPD) || - IS_ENABLED(CONFIG_USB_TYPEC_VPD)) - vpd_rx_enable(0); - else - tcpm_set_rx_enable(port, 0); - - local_state[port] = SM_PAUSED; - break; - } - - /* Run Protocol Layer Message Reception */ - prl_rx_wait_for_phy_message(port, evt); - - /* Run RX Chunked state machine */ - run_state(port, &rch[port].ctx); - - /* Run TX Chunked state machine */ - run_state(port, &tch[port].ctx); - - /* Run Protocol Layer Message Transmission state machine */ - run_state(port, &prl_tx[port].ctx); - - /* Run Protocol Layer Hard Reset state machine */ - run_state(port, &prl_hr[port].ctx); - break; - } -} - -void prl_set_rev(int port, enum tcpm_transmit_type type, - enum pd_rev_type rev) -{ - pdmsg[port].rev[type] = rev; -} -enum pd_rev_type prl_get_rev(int port, enum tcpm_transmit_type type) -{ - return pdmsg[port].rev[type]; -} - -/* Common Protocol Layer Message Transmission */ -static void prl_tx_phy_layer_reset_entry(const int port) -{ - if (IS_ENABLED(CONFIG_USB_TYPEC_CTVPD) - || IS_ENABLED(CONFIG_USB_TYPEC_VPD)) { - vpd_rx_enable(1); - } else { - tcpm_init(port); - tcpm_clear_pending_messages(port); - tcpm_set_rx_enable(port, 1); - } -} - -static void prl_tx_phy_layer_reset_run(const int port) -{ - set_state_prl_tx(port, PRL_TX_WAIT_FOR_MESSAGE_REQUEST); -} - -static void prl_tx_wait_for_message_request_entry(const int port) -{ - /* Reset RetryCounter */ - prl_tx[port].retry_counter = 0; -} - -static void prl_tx_wait_for_message_request_run(const int port) -{ - if ((prl_get_rev(port, pdmsg[port].xmit_type) == PD_REV30) && - PRL_TX_CHK_FLAG(port, - (PRL_FLAGS_START_AMS | PRL_FLAGS_END_AMS))) { - if (tc_get_power_role(port) == PD_ROLE_SOURCE) { - /* - * Start of AMS notification received from - * Policy Engine - */ - if (PRL_TX_CHK_FLAG(port, PRL_FLAGS_START_AMS)) { - PRL_TX_CLR_FLAG(port, PRL_FLAGS_START_AMS); - set_state_prl_tx(port, PRL_TX_SRC_SOURCE_TX); - return; - } - /* - * End of AMS notification received from - * Policy Engine - */ - else if (PRL_TX_CHK_FLAG(port, PRL_FLAGS_END_AMS)) { - PRL_TX_CLR_FLAG(port, PRL_FLAGS_END_AMS); - /* Set Rp = SinkTxOk */ - tcpm_select_rp_value(port, SINK_TX_OK); - tcpm_set_cc(port, TYPEC_CC_RP); - prl_tx[port].retry_counter = 0; - prl_tx[port].flags = 0; - } - } else { - if (PRL_TX_CHK_FLAG(port, PRL_FLAGS_START_AMS)) { - PRL_TX_CLR_FLAG(port, PRL_FLAGS_START_AMS); - /* - * First Message in AMS notification - * received from Policy Engine. - */ - set_state_prl_tx(port, PRL_TX_SNK_START_AMS); - return; - } - } - } else if (PRL_TX_CHK_FLAG(port, PRL_FLAGS_MSG_XMIT)) { - PRL_TX_CLR_FLAG(port, PRL_FLAGS_MSG_XMIT); - /* - * Soft Reset Message Message pending - */ - if ((pdmsg[port].msg_type == PD_CTRL_SOFT_RESET) && - (emsg[port].len == 0)) { - set_state_prl_tx(port, PRL_TX_LAYER_RESET_FOR_TRANSMIT); - } - /* - * Message pending (except Soft Reset) - */ - else { - /* NOTE: PRL_TX_Construct_Message State embedded here */ - prl_tx_construct_message(port); - set_state_prl_tx(port, PRL_TX_WAIT_FOR_PHY_RESPONSE); - } - - return; - } -} - -static void increment_msgid_counter(int port) -{ - /* If the last message wasn't an SOP* message, no need to increment */ - if (prl_tx[port].last_xmit_type >= NUM_SOP_STAR_TYPES) - return; - - prl_tx[port].msg_id_counter[prl_tx[port].last_xmit_type] = - (prl_tx[port].msg_id_counter[prl_tx[port].last_xmit_type] + 1) & - PD_MESSAGE_ID_COUNT; -} - -/* - * PrlTxDiscard - */ -static void prl_tx_discard_message_entry(const int port) -{ - /* Increment msgidCounter */ - increment_msgid_counter(port); - set_state_prl_tx(port, PRL_TX_PHY_LAYER_RESET); -} - -/* - * PrlTxSrcSourceTx - */ -static void prl_tx_src_source_tx_entry(const int port) -{ - /* Set Rp = SinkTxNG */ - tcpm_select_rp_value(port, SINK_TX_NG); - tcpm_set_cc(port, TYPEC_CC_RP); -} - -static void prl_tx_src_source_tx_run(const int port) -{ - if (PRL_TX_CHK_FLAG(port, PRL_FLAGS_MSG_XMIT)) { - PRL_TX_CLR_FLAG(port, PRL_FLAGS_MSG_XMIT); - - set_state_prl_tx(port, PRL_TX_SRC_PENDING); - } -} - -/* - * PrlTxSnkStartAms - */ -static void prl_tx_snk_start_ams_run(const int port) -{ - if (PRL_TX_CHK_FLAG(port, PRL_FLAGS_MSG_XMIT)) { - PRL_TX_CLR_FLAG(port, PRL_FLAGS_MSG_XMIT); - - set_state_prl_tx(port, PRL_TX_SNK_PENDING); - } -} - -/* - * PrlTxLayerResetForTransmit - */ -static void prl_tx_layer_reset_for_transmit_entry(const int port) -{ - int i; - - /* Reset MessageIdCounters */ - for (i = 0; i < NUM_SOP_STAR_TYPES; i++) - prl_tx[port].msg_id_counter[i] = 0; -} - -static void prl_tx_layer_reset_for_transmit_run(const int port) -{ - /* NOTE: PRL_Tx_Construct_Message State embedded here */ - prl_tx_construct_message(port); - set_state_prl_tx(port, PRL_TX_WAIT_FOR_PHY_RESPONSE); -} - -static uint32_t get_sop_star_header(const int port) -{ - const int is_sop_packet = pdmsg[port].xmit_type == TCPC_TX_SOP; - - /* SOP vs SOP'/SOP" headers are different. Replace fields as needed */ - return PD_HEADER( - pdmsg[port].msg_type, - is_sop_packet ? - tc_get_power_role(port) : tc_get_cable_plug(port), - is_sop_packet ? - tc_get_data_role(port) : 0, - prl_tx[port].msg_id_counter[pdmsg[port].xmit_type], - pdmsg[port].data_objs, - pdmsg[port].rev[pdmsg[port].xmit_type], - pdmsg[port].ext); -} - -static void prl_tx_construct_message(const int port) -{ - /* The header is unused for hard reset, etc. */ - const uint32_t header = pdmsg[port].xmit_type < NUM_SOP_STAR_TYPES ? - get_sop_star_header(port) : 0; - - /* Save SOP* so the correct msg_id_counter can be incremented */ - prl_tx[port].last_xmit_type = pdmsg[port].xmit_type; - - /* - * These flags could be set if this function is called before the - * Policy Engine is informed of the previous transmission. Clear the - * flags so that this message can be sent. - */ - prl_tx[port].xmit_status = TCPC_TX_UNSET; - PDMSG_CLR_FLAG(port, PRL_FLAGS_TX_COMPLETE); - - /* Pass message to PHY Layer */ - tcpm_transmit(port, pdmsg[port].xmit_type, header, - pdmsg[port].chk_buf); -} - -/* - * PrlTxWaitForPhyResponse - */ -static void prl_tx_wait_for_phy_response_entry(const int port) -{ - prl_tx[port].tcpc_tx_timeout = get_time().val + PD_T_TCPC_TX_TIMEOUT; -} - -static void prl_tx_wait_for_phy_response_run(const int port) -{ - /* Wait until TX is complete */ - - /* - * NOTE: The TCPC will set xmit_status to TCPC_TX_COMPLETE_DISCARDED - * when a GoodCRC containing an incorrect MessageID is received. - * This condition satifies the PRL_Tx_Match_MessageID state - * requirement. - */ - - if (get_time().val > prl_tx[port].tcpc_tx_timeout || - prl_tx[port].xmit_status == TCPC_TX_COMPLETE_FAILED || - prl_tx[port].xmit_status == TCPC_TX_COMPLETE_DISCARDED) { - - /* NOTE: PRL_Tx_Check_RetryCounter State embedded here. */ - - /* Increment check RetryCounter */ - prl_tx[port].retry_counter++; - - /* - * (RetryCounter > nRetryCount) | Large Extended Message - */ - if (prl_tx[port].retry_counter > N_RETRY_COUNT || - (pdmsg[port].ext && - PD_EXT_HEADER_DATA_SIZE(GET_EXT_HEADER( - pdmsg[port].chk_buf[0]) > 26))) { - - /* - * NOTE: PRL_Tx_Transmission_Error State embedded - * here. - */ - - /* - * State tch_wait_for_transmission_complete will - * inform policy engine of error - */ - PDMSG_SET_FLAG(port, PRL_FLAGS_TX_ERROR); - - /* Increment message id counter */ - increment_msgid_counter(port); - set_state_prl_tx(port, PRL_TX_WAIT_FOR_MESSAGE_REQUEST); - } else { - /* - * NOTE: PRL_TX_Construct_Message State embedded - * here. - */ - /* Try to resend the message. */ - prl_tx_construct_message(port); - } - } else if (prl_tx[port].xmit_status == TCPC_TX_COMPLETE_SUCCESS) { - /* NOTE: PRL_TX_Message_Sent State embedded here. */ - - /* Increment messageId counter */ - increment_msgid_counter(port); - /* Inform Policy Engine Message was sent */ - PDMSG_SET_FLAG(port, PRL_FLAGS_TX_COMPLETE); - /* - * This event reduces the time of informing the policy engine of - * the transmission by one state machine cycle - */ - task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SM, 0); - set_state_prl_tx(port, PRL_TX_WAIT_FOR_MESSAGE_REQUEST); - } -} - -static void prl_tx_wait_for_phy_response_exit(const int port) -{ - prl_tx[port].xmit_status = TCPC_TX_UNSET; -} - -/* Source Protocol Layer Message Transmission */ -/* - * PrlTxSrcPending - */ -static void prl_tx_src_pending_entry(const int port) -{ - /* Start SinkTxTimer */ - prl_tx[port].sink_tx_timer = get_time().val + PD_T_SINK_TX; -} - -static void prl_tx_src_pending_run(const int port) -{ - - if (get_time().val > prl_tx[port].sink_tx_timer) { - /* - * Soft Reset Message pending & - * SinkTxTimer timeout - */ - if ((emsg[port].len == 0) && - (pdmsg[port].msg_type == PD_CTRL_SOFT_RESET)) { - set_state_prl_tx(port, PRL_TX_LAYER_RESET_FOR_TRANSMIT); - } - /* Message pending (except Soft Reset) & - * SinkTxTimer timeout - */ - else { - prl_tx_construct_message(port); - set_state_prl_tx(port, PRL_TX_WAIT_FOR_PHY_RESPONSE); - } - - return; - } -} - -/* - * PrlTxSnkPending - */ -static void prl_tx_snk_pending_run(const int port) -{ - enum tcpc_cc_voltage_status cc1, cc2; - - tcpm_get_cc(port, &cc1, &cc2); - if (cc1 == TYPEC_CC_VOLT_RP_3_0 || cc2 == TYPEC_CC_VOLT_RP_3_0) { - /* - * Soft Reset Message Message pending & - * Rp = SinkTxOk - */ - if ((pdmsg[port].msg_type == PD_CTRL_SOFT_RESET) && - (emsg[port].len == 0)) { - set_state_prl_tx(port, PRL_TX_LAYER_RESET_FOR_TRANSMIT); - } - /* - * Message pending (except Soft Reset) & - * Rp = SinkTxOk - */ - else { - prl_tx_construct_message(port); - set_state_prl_tx(port, PRL_TX_WAIT_FOR_PHY_RESPONSE); - } - return; - } -} - -/* Hard Reset Operation */ - -static void prl_hr_wait_for_request_entry(const int port) -{ - prl_hr[port].flags = 0; -} - -static void prl_hr_wait_for_request_run(const int port) -{ - if (PRL_HR_CHK_FLAG(port, PRL_FLAGS_PE_HARD_RESET | - PRL_FLAGS_PORT_PARTNER_HARD_RESET)) - set_state_prl_hr(port, PRL_HR_RESET_LAYER); -} - -/* - * PrlHrResetLayer - */ -static void prl_hr_reset_layer_entry(const int port) -{ - int i; - - /* reset messageIDCounters */ - for (i = 0; i < NUM_SOP_STAR_TYPES; i++) - prl_tx[port].msg_id_counter[i] = 0; - /* - * Protocol Layer message transmission transitions to - * PRL_Tx_Wait_For_Message_Request state. - */ - set_state_prl_tx(port, PRL_TX_WAIT_FOR_MESSAGE_REQUEST); - - tch[port].flags = 0; - rch[port].flags = 0; - pdmsg[port].flags = 0; - - /* Reset message ids */ - for (i = 0; i < NUM_SOP_STAR_TYPES; i++) { - prl_rx[port].msg_id[i] = -1; - prl_tx[port].msg_id_counter[i] = 0; - } - - /* Disable RX */ - if (IS_ENABLED(CONFIG_USB_TYPEC_CTVPD) || - IS_ENABLED(CONFIG_USB_TYPEC_VPD)) - vpd_rx_enable(0); - else - tcpm_set_rx_enable(port, 0); - - return; -} - -static void prl_hr_reset_layer_run(const int port) -{ - /* - * Protocol Layer reset Complete & - * Hard Reset was initiated by Policy Engine - */ - if (PRL_HR_CHK_FLAG(port, PRL_FLAGS_PE_HARD_RESET)) { - /* Request PHY to perform a Hard Reset */ - prl_send_ctrl_msg(port, TCPC_TX_HARD_RESET, 0); - set_state_prl_hr(port, PRL_HR_WAIT_FOR_PHY_HARD_RESET_COMPLETE); - } - /* - * Protocol Layer reset complete & - * Hard Reset was initiated by Port Partner - */ - else { - /* Inform Policy Engine of the Hard Reset */ - pe_got_hard_reset(port); - set_state_prl_hr(port, PRL_HR_WAIT_FOR_PE_HARD_RESET_COMPLETE); - } -} - -/* - * PrlHrWaitForPhyHardResetComplete - */ -static void prl_hr_wait_for_phy_hard_reset_complete_entry(const int port) -{ - /* Start HardResetCompleteTimer */ - prl_hr[port].hard_reset_complete_timer = - get_time().val + PD_T_PS_HARD_RESET; -} - -static void prl_hr_wait_for_phy_hard_reset_complete_run(const int port) -{ - /* - * Wait for hard reset from PHY - * or timeout - */ - if (PDMSG_CHK_FLAG(port, PRL_FLAGS_TX_COMPLETE) || - (get_time().val > prl_hr[port].hard_reset_complete_timer)) { - /* PRL_HR_PHY_Hard_Reset_Requested */ - - /* Inform Policy Engine Hard Reset was sent */ - pe_hard_reset_sent(port); - set_state_prl_hr(port, PRL_HR_WAIT_FOR_PE_HARD_RESET_COMPLETE); - - return; - } -} - -/* - * PrlHrWaitForPeHardResetComplete - */ -static void prl_hr_wait_for_pe_hard_reset_complete_run(const int port) -{ - /* - * Wait for Hard Reset complete indication from Policy Engine - */ - if (PRL_HR_CHK_FLAG(port, PRL_FLAGS_HARD_RESET_COMPLETE)) - set_state_prl_hr(port, PRL_HR_WAIT_FOR_REQUEST); -} - -static void prl_hr_wait_for_pe_hard_reset_complete_exit(const int port) -{ - /* Exit from Hard Reset */ - - set_state_prl_tx(port, PRL_TX_PHY_LAYER_RESET); - set_state_rch(port, RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER); - set_state_tch(port, TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE); -} - -static void copy_chunk_to_ext(int port) -{ - /* Calculate number of bytes */ - pdmsg[port].num_bytes_received = (PD_HEADER_CNT(emsg[port].header) * 4); - - /* Copy chunk into extended message */ - memcpy((uint8_t *)emsg[port].buf, (uint8_t *)pdmsg[port].chk_buf, - pdmsg[port].num_bytes_received); - - /* Set extended message length */ - emsg[port].len = pdmsg[port].num_bytes_received; -} - -/* - * Chunked Rx State Machine - */ -/* - * RchWaitForMessageFromProtocolLayer - */ -static void rch_wait_for_message_from_protocol_layer_entry(const int port) -{ - /* Clear Abort flag */ - PDMSG_CLR_FLAG(port, PRL_FLAGS_ABORT); - - /* All Messages are chunked */ - rch[port].flags = PRL_FLAGS_CHUNKING; -} - -static void rch_wait_for_message_from_protocol_layer_run(const int port) -{ - if (RCH_CHK_FLAG(port, PRL_FLAGS_MSG_RECEIVED)) { - RCH_CLR_FLAG(port, PRL_FLAGS_MSG_RECEIVED); - /* - * Are we communicating with a PD3.0 device and is - * this an extended message? - */ - if (prl_get_rev(port, pdmsg[port].xmit_type) == PD_REV30 && - PD_HEADER_EXT(emsg[port].header)) { - uint16_t exhdr = GET_EXT_HEADER(*pdmsg[port].chk_buf); - uint8_t chunked = PD_EXT_HEADER_CHUNKED(exhdr); - - /* - * Received Extended Message & - * (Chunking = 1 & Chunked = 1) - */ - if ((RCH_CHK_FLAG(port, PRL_FLAGS_CHUNKING)) && - chunked) { - /* - * RCH_Processing_Extended_Message first chunk - * entry processing embedded here - * - * This is the first chunk: - * Set Chunk_number_expected = 0 and - * Num_Bytes_Received = 0 - */ - pdmsg[port].chunk_number_expected = 0; - pdmsg[port].num_bytes_received = 0; - pdmsg[port].msg_type = - PD_HEADER_TYPE(emsg[port].header); - - set_state_rch(port, - RCH_PROCESSING_EXTENDED_MESSAGE); - } - /* - * (Received Extended Message & - * (Chunking = 0 & Chunked = 0)) - */ - else if (!RCH_CHK_FLAG(port, PRL_FLAGS_CHUNKING) && - !chunked) { - /* Copy chunk to extended buffer */ - copy_chunk_to_ext(port); - set_state_rch(port, RCH_PASS_UP_MESSAGE); - } - /* - * Chunked != Chunking - */ - else { - set_state_rch(port, RCH_REPORT_ERROR); - } - } - /* - * Received Non-Extended Message - */ - else if (!PD_HEADER_EXT(emsg[port].header)) { - /* Copy chunk to extended buffer */ - copy_chunk_to_ext(port); - set_state_rch(port, RCH_PASS_UP_MESSAGE); - } - /* - * Received an Extended Message while communicating at a - * revision lower than PD3.0 - */ - else { - set_state_rch(port, RCH_REPORT_ERROR); - } - } -} - -/* - * RchPassUpMessage - */ -static void rch_pass_up_message_entry(const int port) -{ - /* Pass Message to Policy Engine */ - pe_message_received(port); - set_state_rch(port, RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER); -} - -/* - * RchProcessingExtendedMessage - */ -static void rch_processing_extended_message_run(const int port) -{ - uint16_t exhdr = GET_EXT_HEADER(pdmsg[port].chk_buf[0]); - uint8_t chunk_num = PD_EXT_HEADER_CHUNK_NUM(exhdr); - uint32_t data_size = PD_EXT_HEADER_DATA_SIZE(exhdr); - uint32_t byte_num; - - /* - * Abort Flag Set - */ - if (PDMSG_CHK_FLAG(port, PRL_FLAGS_ABORT)) - set_state_rch(port, RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER); - - /* - * If expected Chunk Number: - * Append data to Extended_Message_Buffer - * Increment Chunk_number_Expected - * Adjust Num Bytes Received - */ - else if (chunk_num == pdmsg[port].chunk_number_expected) { - byte_num = data_size - pdmsg[port].num_bytes_received; - - if (byte_num > 25) - byte_num = 26; - - /* Make sure extended message buffer does not overflow */ - if (pdmsg[port].num_bytes_received + - byte_num > EXTENDED_BUFFER_SIZE) { - set_state_rch(port, RCH_REPORT_ERROR); - return; - } - - /* Append data */ - /* Add 2 to chk_buf to skip over extended message header */ - memcpy(((uint8_t *)emsg[port].buf + - pdmsg[port].num_bytes_received), - (uint8_t *)pdmsg[port].chk_buf + 2, byte_num); - /* increment chunk number expected */ - pdmsg[port].chunk_number_expected++; - /* adjust num bytes received */ - pdmsg[port].num_bytes_received += byte_num; - - /* Was that the last chunk? */ - if (pdmsg[port].num_bytes_received >= data_size) { - emsg[port].len = pdmsg[port].num_bytes_received; - /* Pass Message to Policy Engine */ - set_state_rch(port, RCH_PASS_UP_MESSAGE); - } - /* - * Message not Complete - */ - else - set_state_rch(port, RCH_REQUESTING_CHUNK); - } - /* - * Unexpected Chunk Number - */ - else - set_state_rch(port, RCH_REPORT_ERROR); -} - -/* - * RchRequestingChunk - */ -static void rch_requesting_chunk_entry(const int port) -{ - /* - * Send Chunk Request to Protocol Layer - * with chunk number = Chunk_Number_Expected - */ - pdmsg[port].chk_buf[0] = PD_EXT_HEADER( - pdmsg[port].chunk_number_expected, - 1, /* Request Chunk */ - 0 /* Data Size */ - ); - - pdmsg[port].data_objs = 1; - pdmsg[port].ext = 1; - PRL_TX_SET_FLAG(port, PRL_FLAGS_MSG_XMIT); - task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_TX, 0); -} - -static void rch_requesting_chunk_run(const int port) -{ - /* - * Message Transmitted received from Protocol Layer - */ - if (PDMSG_CHK_FLAG(port, PRL_FLAGS_TX_COMPLETE)) { - PDMSG_CLR_FLAG(port, PRL_FLAGS_TX_COMPLETE); - set_state_rch(port, RCH_WAITING_CHUNK); - } - /* - * Transmission Error from Protocol Layer or - * Message Received From Protocol Layer - */ - else if (RCH_CHK_FLAG(port, PRL_FLAGS_MSG_RECEIVED) || - PDMSG_CHK_FLAG(port, PRL_FLAGS_TX_ERROR)) { - /* - * Leave PRL_FLAGS_MSG_RECEIVED flag set. It'll be - * cleared in rch_report_error state - */ - set_state_rch(port, RCH_REPORT_ERROR); - } -} - -/* - * RchWaitingChunk - */ -static void rch_waiting_chunk_entry(const int port) -{ - /* - * Start ChunkSenderResponseTimer - */ - rch[port].chunk_sender_response_timer = - get_time().val + PD_T_CHUNK_SENDER_RESPONSE; -} - -static void rch_waiting_chunk_run(const int port) -{ - if (RCH_CHK_FLAG(port, PRL_FLAGS_MSG_RECEIVED)) { - /* - * Leave PRL_FLAGS_MSG_RECEIVED flag set just in case an error - * is detected. If an error is detected, PRL_FLAGS_MSG_RECEIVED - * will be cleared in rch_report_error state. - */ - - if (PD_HEADER_EXT(emsg[port].header)) { - uint16_t exhdr = GET_EXT_HEADER(pdmsg[port].chk_buf[0]); - /* - * Other Message Received from Protocol Layer - */ - if (PD_EXT_HEADER_REQ_CHUNK(exhdr) || - !PD_EXT_HEADER_CHUNKED(exhdr)) { - set_state_rch(port, RCH_REPORT_ERROR); - } - /* - * Chunk response Received from Protocol Layer - */ - else { - /* - * No error wad detected, so clear - * PRL_FLAGS_MSG_RECEIVED flag. - */ - RCH_CLR_FLAG(port, PRL_FLAGS_MSG_RECEIVED); - set_state_rch(port, - RCH_PROCESSING_EXTENDED_MESSAGE); - } - } - } - /* - * ChunkSenderResponseTimer Timeout - */ - else if (get_time().val > rch[port].chunk_sender_response_timer) { - set_state_rch(port, RCH_REPORT_ERROR); - } -} - -/* - * RchReportError - */ -static void rch_report_error_entry(const int port) -{ - /* - * If the state was entered because a message was received, - * this message is passed to the Policy Engine. - */ - if (RCH_CHK_FLAG(port, PRL_FLAGS_MSG_RECEIVED)) { - RCH_CLR_FLAG(port, PRL_FLAGS_MSG_RECEIVED); - - /* Copy chunk to extended buffer */ - copy_chunk_to_ext(port); - /* Pass Message to Policy Engine */ - pe_message_received(port); - /* Report error */ - pe_report_error(port, ERR_RCH_MSG_REC); - } else { - /* Report error */ - pe_report_error(port, ERR_RCH_CHUNKED); - } -} - -static void rch_report_error_run(const int port) -{ - set_state_rch(port, RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER); -} - -/* - * Chunked Tx State Machine - */ - -/* - * TchWaitForMessageRequestFromPe - */ -static void tch_wait_for_message_request_from_pe_entry(const int port) -{ - /* Clear Abort flag */ - PDMSG_CLR_FLAG(port, PRL_FLAGS_ABORT); - - /* All Messages are chunked */ - tch[port].flags = PRL_FLAGS_CHUNKING; -} - -static void tch_wait_for_message_request_from_pe_run(const int port) -{ - /* - * Any message received and not in state TCH_Wait_Chunk_Request - */ - if (TCH_CHK_FLAG(port, PRL_FLAGS_MSG_RECEIVED)) { - TCH_CLR_FLAG(port, PRL_FLAGS_MSG_RECEIVED); - set_state_tch(port, TCH_MESSAGE_RECEIVED); - } else if (TCH_CHK_FLAG(port, PRL_FLAGS_MSG_XMIT)) { - TCH_CLR_FLAG(port, PRL_FLAGS_MSG_XMIT); - /* - * Rx Chunking State != RCH_Wait_For_Message_From_Protocol_Layer - * & Abort Supported - * - * Discard the Message - */ - if (rch_get_state(port) != - RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER) { - tch[port].error = ERR_TCH_XMIT; - set_state_tch(port, TCH_REPORT_ERROR); - } else { - /* - * Extended Message Request & Chunking - */ - if (prl_get_rev(port, pdmsg[port].xmit_type) == PD_REV30 - && pdmsg[port].ext && - TCH_CHK_FLAG(port, PRL_FLAGS_CHUNKING)) { - /* - * NOTE: TCH_Prepare_To_Send_Chunked_Message - * embedded here. - */ - pdmsg[port].send_offset = 0; - pdmsg[port].chunk_number_to_send = 0; - set_state_tch(port, - TCH_CONSTRUCT_CHUNKED_MESSAGE); - } else - /* - * Non-Extended Message Request - */ - { - /* Make sure buffer doesn't overflow */ - if (emsg[port].len > BUFFER_SIZE) { - tch[port].error = ERR_TCH_XMIT; - set_state_tch(port, TCH_REPORT_ERROR); - return; - } - - /* NOTE: TCH_Pass_Down_Message embedded here */ - /* Copy message to chunked buffer */ - memset((uint8_t *)pdmsg[port].chk_buf, - 0, BUFFER_SIZE); - memcpy((uint8_t *)pdmsg[port].chk_buf, - (uint8_t *)emsg[port].buf, - emsg[port].len); - /* - * Pad length to 4-byte boundery and - * convert to number of 32-bit objects. - * Since the value is shifted right by 2, - * no need to explicitly clear the lower - * 2-bits. - */ - pdmsg[port].data_objs = - (emsg[port].len + 3) >> 2; - /* Pass Message to Protocol Layer */ - PRL_TX_SET_FLAG(port, PRL_FLAGS_MSG_XMIT); - set_state_tch(port, - TCH_WAIT_FOR_TRANSMISSION_COMPLETE); - } - } - } -} - -/* - * TchWaitForTransmissionComplete - */ -static void tch_wait_for_transmission_complete_run(const int port) -{ - /* - * Inform Policy Engine that Message was sent. - */ - if (PDMSG_CHK_FLAG(port, PRL_FLAGS_TX_COMPLETE)) { - PDMSG_CLR_FLAG(port, PRL_FLAGS_TX_COMPLETE); - set_state_tch(port, TCH_MESSAGE_SENT); - } - /* - * Inform Policy Engine of Tx Error - */ - else if (PDMSG_CHK_FLAG(port, PRL_FLAGS_TX_ERROR)) { - PDMSG_CLR_FLAG(port, PRL_FLAGS_TX_ERROR); - tch[port].error = ERR_TCH_XMIT; - set_state_tch(port, TCH_REPORT_ERROR); - } -} - -/* - * TchConstructChunkedMessage - */ -static void tch_construct_chunked_message_entry(const int port) -{ - uint16_t *ext_hdr; - uint8_t *data; - uint16_t num; - - /* - * Any message received and not in state TCH_Wait_Chunk_Request - */ - if (TCH_CHK_FLAG(port, PRL_FLAGS_MSG_RECEIVED)) { - TCH_CLR_FLAG(port, PRL_FLAGS_MSG_RECEIVED); - set_state_tch(port, TCH_MESSAGE_RECEIVED); - return; - } - - /* Prepare to copy chunk into chk_buf */ - - ext_hdr = (uint16_t *)pdmsg[port].chk_buf; - data = ((uint8_t *)pdmsg[port].chk_buf + 2); - num = emsg[port].len - pdmsg[port].send_offset; - - if (num > 26) - num = 26; - - /* Set the chunks extended header */ - *ext_hdr = PD_EXT_HEADER(pdmsg[port].chunk_number_to_send, - 0, /* Chunk Request */ - emsg[port].len); - - /* Copy the message chunk into chk_buf */ - memset(data, 0, 28); - memcpy(data, emsg[port].buf + pdmsg[port].send_offset, num); - pdmsg[port].send_offset += num; - - /* - * Add in 2 bytes for extended header - * pad out to 4-byte boundary - * convert to number of 4-byte words - * Since the value is shifted right by 2, - * no need to explicitly clear the lower - * 2-bits. - */ - pdmsg[port].data_objs = (num + 2 + 3) >> 2; - - /* Pass message chunk to Protocol Layer */ - PRL_TX_SET_FLAG(port, PRL_FLAGS_MSG_XMIT); -} - -static void tch_construct_chunked_message_run(const int port) -{ - if (PDMSG_CHK_FLAG(port, PRL_FLAGS_ABORT)) - set_state_tch(port, TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE); - else - set_state_tch(port, TCH_SENDING_CHUNKED_MESSAGE); -} - -/* - * TchSendingChunkedMessage - */ -static void tch_sending_chunked_message_run(const int port) -{ - /* - * Transmission Error - */ - if (PDMSG_CHK_FLAG(port, PRL_FLAGS_TX_ERROR)) { - tch[port].error = ERR_TCH_XMIT; - set_state_tch(port, TCH_REPORT_ERROR); - } - /* - * Message Transmitted from Protocol Layer & - * Last Chunk - */ - else if (emsg[port].len == pdmsg[port].send_offset) { - set_state_tch(port, TCH_MESSAGE_SENT); - } - /* - * Any message received and not in state TCH_Wait_Chunk_Request - */ - else if (TCH_CHK_FLAG(port, PRL_FLAGS_MSG_RECEIVED)) { - TCH_CLR_FLAG(port, PRL_FLAGS_MSG_RECEIVED); - set_state_tch(port, TCH_MESSAGE_RECEIVED); - } - /* - * Message Transmitted from Protocol Layer & - * Not Last Chunk - */ - else - set_state_tch(port, TCH_WAIT_CHUNK_REQUEST); -} - -/* - * TchWaitChunkRequest - */ -static void tch_wait_chunk_request_entry(const int port) -{ - /* Increment Chunk Number to Send */ - pdmsg[port].chunk_number_to_send++; - /* Start Chunk Sender Request Timer */ - tch[port].chunk_sender_request_timer = - get_time().val + PD_T_CHUNK_SENDER_REQUEST; -} - -static void tch_wait_chunk_request_run(const int port) -{ - if (TCH_CHK_FLAG(port, PRL_FLAGS_MSG_RECEIVED)) { - TCH_CLR_FLAG(port, PRL_FLAGS_MSG_RECEIVED); - - if (PD_HEADER_EXT(emsg[port].header)) { - uint16_t exthdr; - - exthdr = GET_EXT_HEADER(pdmsg[port].chk_buf[0]); - if (PD_EXT_HEADER_REQ_CHUNK(exthdr)) { - /* - * Chunk Request Received & - * Chunk Number = Chunk Number to Send - */ - if (PD_EXT_HEADER_CHUNK_NUM(exthdr) == - pdmsg[port].chunk_number_to_send) { - set_state_tch(port, - TCH_CONSTRUCT_CHUNKED_MESSAGE); - } - /* - * Chunk Request Received & - * Chunk Number != Chunk Number to Send - */ - else { - tch[port].error = ERR_TCH_CHUNKED; - set_state_tch(port, TCH_REPORT_ERROR); - } - return; - } - } - - /* - * Other message received - */ - set_state_tch(port, TCH_MESSAGE_RECEIVED); - } - /* - * ChunkSenderRequestTimer timeout - */ - else if (get_time().val >= - tch[port].chunk_sender_request_timer) { - set_state_tch(port, TCH_MESSAGE_SENT); - } -} - -/* - * TchMessageReceived - */ -static void tch_message_received_entry(const int port) -{ - /* Pass message to chunked Rx */ - RCH_SET_FLAG(port, PRL_FLAGS_MSG_RECEIVED); -} - -static void tch_message_received_run(const int port) -{ - set_state_tch(port, TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE); -} - -/* - * TchMessageSent - */ -static void tch_message_sent_entry(const int port) -{ - /* Tell PE message was sent */ - pe_message_sent(port); - set_state_tch(port, TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE); -} - -/* - * TchReportError - */ -static void tch_report_error_entry(const int port) -{ - /* Report Error To Policy Engine */ - pe_report_error(port, tch[port].error); - set_state_tch(port, TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE); -} - -/* - * Protocol Layer Message Reception State Machine - */ -static void prl_rx_wait_for_phy_message(const int port, int evt) -{ - uint32_t header; - uint8_t type; - uint8_t cnt; - uint8_t sop; - int8_t msid; - - /* If we don't have any message, just stop processing now. */ - if (!tcpm_has_pending_message(port) || - tcpm_dequeue_message(port, pdmsg[port].chk_buf, &header)) - return; - - emsg[port].header = header; - type = PD_HEADER_TYPE(header); - cnt = PD_HEADER_CNT(header); - msid = PD_HEADER_ID(header); - sop = PD_HEADER_GET_SOP(header); - - /* - * Ignore messages sent to the cable from our - * port partner if we aren't Vconn powered device. - */ - if (!IS_ENABLED(CONFIG_USB_TYPEC_CTVPD) && - !IS_ENABLED(CONFIG_USB_TYPEC_VPD) && - PD_HEADER_GET_SOP(header) != PD_MSG_SOP && - PD_HEADER_PROLE(header) == PD_PLUG_FROM_DFP_UFP) - return; - - if (cnt == 0 && type == PD_CTRL_SOFT_RESET) { - int i; - - for (i = 0; i < NUM_SOP_STAR_TYPES; i++) { - /* Clear MessageIdCounter */ - prl_tx[port].msg_id_counter[i] = 0; - /* Clear stored MessageID value */ - prl_rx[port].msg_id[i] = -1; - } - - /* Inform Policy Engine of Soft Reset */ - pe_got_soft_reset(port); - - /* Soft Reset occurred */ - set_state_prl_tx(port, PRL_TX_PHY_LAYER_RESET); - set_state_rch(port, RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER); - set_state_tch(port, TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE); - } - - /* - * Ignore if this is a duplicate message. Stop processing. - */ - if (prl_rx[port].msg_id[sop] == msid) - return; - - /* - * Discard any pending tx message if this is - * not a ping message - */ - if (prl_get_rev(port, pdmsg[port].xmit_type) == PD_REV30 && - (cnt == 0) && type != PD_CTRL_PING) { - if (prl_tx_get_state(port) == PRL_TX_SRC_PENDING || - prl_tx_get_state(port) == PRL_TX_SNK_PENDING) - set_state_prl_tx(port, PRL_TX_DISCARD_MESSAGE); - } - - /* Store Message Id */ - prl_rx[port].msg_id[sop] = msid; - - /* RTR Chunked Message Router States. */ - /* - * Received Ping from Protocol Layer - */ - if (cnt == 0 && type == PD_CTRL_PING) { - /* NOTE: RTR_PING State embedded here. */ - emsg[port].len = 0; - pe_message_received(port); - return; - } - /* - * Message (not Ping) Received from - * Protocol Layer & Doing Tx Chunks - */ - else if (tch_get_state(port) != TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE && - tch_get_state(port) != TCH_WAIT_FOR_TRANSMISSION_COMPLETE) { - /* NOTE: RTR_TX_CHUNKS State embedded here. */ - /* - * Send Message to Tx Chunk - * Chunk State Machine - */ - TCH_SET_FLAG(port, PRL_FLAGS_MSG_RECEIVED); - } - /* - * Message (not Ping) Received from - * Protocol Layer & Not Doing Tx Chunks - */ - else { - /* NOTE: RTR_RX_CHUNKS State embedded here. */ - /* - * Send Message to Rx - * Chunk State Machine - */ - RCH_SET_FLAG(port, PRL_FLAGS_MSG_RECEIVED); - } - - task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SM, 0); -} - -/* All necessary Protocol Transmit States (Section 6.11.2.2) */ -static const struct usb_state prl_tx_states[] = { - [PRL_TX_PHY_LAYER_RESET] = { - .entry = prl_tx_phy_layer_reset_entry, - .run = prl_tx_phy_layer_reset_run, - }, - [PRL_TX_WAIT_FOR_MESSAGE_REQUEST] = { - .entry = prl_tx_wait_for_message_request_entry, - .run = prl_tx_wait_for_message_request_run, - }, - [PRL_TX_LAYER_RESET_FOR_TRANSMIT] = { - .entry = prl_tx_layer_reset_for_transmit_entry, - .run = prl_tx_layer_reset_for_transmit_run, - }, - [PRL_TX_WAIT_FOR_PHY_RESPONSE] = { - .entry = prl_tx_wait_for_phy_response_entry, - .run = prl_tx_wait_for_phy_response_run, - .exit = prl_tx_wait_for_phy_response_exit, - }, - [PRL_TX_SRC_SOURCE_TX] = { - .entry = prl_tx_src_source_tx_entry, - .run = prl_tx_src_source_tx_run, - }, - [PRL_TX_SNK_START_AMS] = { - .run = prl_tx_snk_start_ams_run, - }, - [PRL_TX_SRC_PENDING] = { - .entry = prl_tx_src_pending_entry, - .run = prl_tx_src_pending_run, - }, - [PRL_TX_SNK_PENDING] = { - .run = prl_tx_snk_pending_run, - }, - [PRL_TX_DISCARD_MESSAGE] = { - .entry = prl_tx_discard_message_entry, - }, -}; - -/* All necessary Protocol Hard Reset States (Section 6.11.2.4) */ -static const struct usb_state prl_hr_states[] = { - [PRL_HR_WAIT_FOR_REQUEST] = { - .entry = prl_hr_wait_for_request_entry, - .run = prl_hr_wait_for_request_run, - }, - [PRL_HR_RESET_LAYER] = { - .entry = prl_hr_reset_layer_entry, - .run = prl_hr_reset_layer_run, - }, - [PRL_HR_WAIT_FOR_PHY_HARD_RESET_COMPLETE] = { - .entry = prl_hr_wait_for_phy_hard_reset_complete_entry, - .run = prl_hr_wait_for_phy_hard_reset_complete_run, - }, - [PRL_HR_WAIT_FOR_PE_HARD_RESET_COMPLETE] = { - .run = prl_hr_wait_for_pe_hard_reset_complete_run, - .exit = prl_hr_wait_for_pe_hard_reset_complete_exit, - }, -}; - -/* All necessary Chunked Rx states (Section 6.11.2.1.2) */ -static const struct usb_state rch_states[] = { - [RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER] = { - .entry = rch_wait_for_message_from_protocol_layer_entry, - .run = rch_wait_for_message_from_protocol_layer_run, - }, - [RCH_PASS_UP_MESSAGE] = { - .entry = rch_pass_up_message_entry, - }, - [RCH_PROCESSING_EXTENDED_MESSAGE] = { - .run = rch_processing_extended_message_run, - }, - [RCH_REQUESTING_CHUNK] = { - .entry = rch_requesting_chunk_entry, - .run = rch_requesting_chunk_run, - }, - [RCH_WAITING_CHUNK] = { - .entry = rch_waiting_chunk_entry, - .run = rch_waiting_chunk_run, - }, - [RCH_REPORT_ERROR] = { - .entry = rch_report_error_entry, - .run = rch_report_error_run, - }, -}; - -/* All necessary Chunked Tx states (Section 6.11.2.1.3) */ -static const struct usb_state tch_states[] = { - [TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE] = { - .entry = tch_wait_for_message_request_from_pe_entry, - .run = tch_wait_for_message_request_from_pe_run, - }, - [TCH_WAIT_FOR_TRANSMISSION_COMPLETE] = { - .run = tch_wait_for_transmission_complete_run, - }, - [TCH_CONSTRUCT_CHUNKED_MESSAGE] = { - .entry = tch_construct_chunked_message_entry, - .run = tch_construct_chunked_message_run, - }, - [TCH_SENDING_CHUNKED_MESSAGE] = { - .run = tch_sending_chunked_message_run, - }, - [TCH_WAIT_CHUNK_REQUEST] = { - .entry = tch_wait_chunk_request_entry, - .run = tch_wait_chunk_request_run, - }, - [TCH_MESSAGE_RECEIVED] = { - .entry = tch_message_received_entry, - .run = tch_message_received_run, - }, - [TCH_MESSAGE_SENT] = { - .entry = tch_message_sent_entry, - }, - [TCH_REPORT_ERROR] = { - .entry = tch_report_error_entry, - }, -}; - -#ifdef TEST_BUILD -const struct test_sm_data test_prl_sm_data[] = { - { - .base = prl_tx_states, - .size = ARRAY_SIZE(prl_tx_states), - }, - { - .base = prl_hr_states, - .size = ARRAY_SIZE(prl_hr_states), - }, - { - .base = rch_states, - .size = ARRAY_SIZE(rch_states), - }, - { - .base = tch_states, - .size = ARRAY_SIZE(tch_states), - }, -}; -const int test_prl_sm_data_size = ARRAY_SIZE(test_prl_sm_data); -#endif - diff --git a/common/usbc/usb_sm.c b/common/usbc/usb_sm.c deleted file mode 100644 index ba155281be..0000000000 --- a/common/usbc/usb_sm.c +++ /dev/null @@ -1,193 +0,0 @@ -/* Copyright 2019 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 "common.h" -#include "console.h" -#include "usb_pd.h" -#include "usb_sm.h" -#include "util.h" - -#ifdef CONFIG_COMMON_RUNTIME -#define CPRINTF(format, args...) cprintf(CC_USB, format, ## args) -#define CPRINTS(format, args...) cprints(CC_USB, format, ## args) -#else /* CONFIG_COMMON_RUNTIME */ -#define CPRINTF(format, args...) -#define CPRINTS(format, args...) -#endif - -/* Private structure (to this file) used to track state machine context */ -struct internal_ctx { - usb_state_ptr last_entered; - uint32_t running : 1; - uint32_t enter : 1; - uint32_t exit : 1; -}; -BUILD_ASSERT(sizeof(struct internal_ctx) == - member_size(struct sm_ctx, internal)); - -/* Gets the first shared parent state between a and b (inclusive) */ -static usb_state_ptr shared_parent_state(usb_state_ptr a, usb_state_ptr b) -{ - const usb_state_ptr orig_b = b; - - /* There are no common ancestors */ - if (b == NULL) - return NULL; - - /* This assumes that both A and B are NULL terminated without cycles */ - while (a != NULL) { - /* We found a match return */ - if (a == b) - return a; - - /* - * Otherwise, increment b down the list for comparison until we - * run out, then increment a and start over on b for comparison - */ - if (b->parent == NULL) { - a = a->parent; - b = orig_b; - } else { - b = b->parent; - } - } - - return NULL; -} - -/* - * Call all entry functions of parents before children. If set_state is called - * during one of the entry functions, then do not call any remaining entry - * functions. - */ -static void call_entry_functions(const int port, - struct internal_ctx *const internal, - const usb_state_ptr stop, - const usb_state_ptr current) -{ - if (current == stop) - return; - - call_entry_functions(port, internal, stop, current->parent); - - /* - * If the previous entry function called set_state, then don't enter - * remaining states. - */ - if (!internal->enter) - return; - - /* Track the latest state that was entered, so we can exit properly. */ - internal->last_entered = current; - if (current->entry) - current->entry(port); -} - -/* - * Call all exit functions of children before parents. Note set_state is ignored - * during an exit function. - */ -static void call_exit_functions(const int port, const usb_state_ptr stop, - const usb_state_ptr current) -{ - if (current == stop) - return; - - if (current->exit) - current->exit(port); - - call_exit_functions(port, stop, current->parent); -} - -void set_state(const int port, struct sm_ctx *const ctx, - const usb_state_ptr new_state) -{ - struct internal_ctx * const internal = (void *) ctx->internal; - usb_state_ptr last_state; - usb_state_ptr shared_parent; - - /* - * It does not make sense to call set_state in an exit phase of a state - * since we are already in a transition; we would always ignore the - * intended state to transition into. - */ - if (internal->exit) { - CPRINTF("C%d: Ignoring set state to 0x%pP within 0x%pP", - port, new_state, ctx->current); - return; - } - - /* - * Determine the last state that was entered. Normally it is current, - * but we could have called set_state within an entry phase, so we - * shouldn't exit any states that weren't fully entered. - */ - last_state = internal->enter ? internal->last_entered : ctx->current; - - /* We don't exit and re-enter shared parent states */ - shared_parent = shared_parent_state(last_state, new_state); - - /* - * Exit all of the non-common states from the last state. - */ - internal->exit = true; - call_exit_functions(port, shared_parent, last_state); - internal->exit = false; - - ctx->previous = ctx->current; - ctx->current = new_state; - - /* - * Enter all new non-common states. last_entered will contain the last - * state that successfully entered before another set_state was called. - */ - internal->last_entered = NULL; - internal->enter = true; - call_entry_functions(port, internal, shared_parent, ctx->current); - /* - * Setting enter to false ensures that all pending entry calls will be - * skipped (in the case of a parent state calling set_state, which means - * we should not enter any child states) - */ - internal->enter = false; - - /* - * If we set_state while we are running a child state, then stop running - * any remaining parent states. - */ - internal->running = false; -} - -/* - * Call all run functions of children before parents. If set_state is called - * during one of the entry functions, then do not call any remaining entry - * functions. - */ -static void call_run_functions(const int port, - const struct internal_ctx *const internal, - const usb_state_ptr current) -{ - if (!current) - return; - - /* If set_state is called during run, don't call remain functions. */ - if (!internal->running) - return; - - if (current->run) - current->run(port); - - call_run_functions(port, internal, current->parent); -} - -void run_state(const int port, struct sm_ctx *const ctx) -{ - struct internal_ctx * const internal = (void *) ctx->internal; - - internal->running = true; - call_run_functions(port, internal, ctx->current); - internal->running = false; -} - diff --git a/common/usbc/usb_tc_ctvpd_sm.c b/common/usbc/usb_tc_ctvpd_sm.c deleted file mode 100644 index 1747a8c0ab..0000000000 --- a/common/usbc/usb_tc_ctvpd_sm.c +++ /dev/null @@ -1,1673 +0,0 @@ -/* Copyright 2019 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 "common.h" -#include "console.h" -#include "system.h" -#include "task.h" -#include "tcpm.h" -#include "usb_pd.h" -#include "usb_tc_sm.h" -#include "vpd_api.h" - -/* USB Type-C CTVPD module */ - -#ifdef CONFIG_COMMON_RUNTIME -#define CPRINTF(format, args...) cprintf(CC_HOOK, format, ## args) -#define CPRINTS(format, args...) cprints(CC_HOOK, format, ## args) -#else /* CONFIG_COMMON_RUNTIME */ -#define CPRINTF(format, args...) -#define CPRINTS(format, args...) -#endif - -/* Type-C Layer Flags */ -#define TC_FLAGS_VCONN_ON BIT(0) - -#define SUPPORT_TIMER_RESET_INIT 0 -#define SUPPORT_TIMER_RESET_REQUEST 1 -#define SUPPORT_TIMER_RESET_COMPLETE 2 - -/** - * This is the Type-C Port object that contains information needed to - * implement a Charge Through VCONN Powered Device. - */ -static struct type_c { - /* state machine context */ - struct sm_ctx ctx; - /* Higher-level power deliver state machines are enabled if true. */ - uint8_t pd_enable; - /* port flags, see TC_FLAGS_* */ - uint32_t flags; - /* - * Time a charge-through port shall wait before it can determine it - * is attached - */ - uint64_t cc_debounce; - /* Time a host port shall wait before it can determine it is attached */ - uint64_t host_cc_debounce; - /* Time a Sink port shall wait before it can determine it is detached - * due to the potential for USB PD signaling on CC as described in - * the state definitions. - */ - uint64_t pd_debounce; - /* Maintains state of billboard device */ - int billboard_presented; - /* - * Time a port shall wait before it can determine it is - * re-attached during the try-wait process. - */ - uint64_t try_wait_debounce; - /* charge-through support timer */ - uint64_t support_timer; - /* reset the charge-through support timer */ - uint8_t support_timer_reset; - /* VPD host port cc state */ - enum pd_cc_states host_cc_state; - uint8_t ct_cc; - /* The cc state */ - enum pd_cc_states cc_state; - uint64_t next_role_swap; -} tc[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/* List of all TypeC-level states */ -enum usb_tc_state { - /* Normal States */ - TC_DISABLED, - TC_UNATTACHED_SNK, - TC_ATTACH_WAIT_SNK, - TC_ATTACHED_SNK, - TC_ERROR_RECOVERY, - TC_TRY_SNK, - TC_UNATTACHED_SRC, - TC_ATTACH_WAIT_SRC, - TC_TRY_WAIT_SRC, - TC_ATTACHED_SRC, - TC_CT_TRY_SNK, - TC_CT_ATTACH_WAIT_UNSUPPORTED, - TC_CT_ATTACHED_UNSUPPORTED, - TC_CT_UNATTACHED_UNSUPPORTED, - TC_CT_UNATTACHED_VPD, - TC_CT_DISABLED_VPD, - TC_CT_ATTACHED_VPD, - TC_CT_ATTACH_WAIT_VPD, - /* Super States */ - TC_VBUS_CC_ISO, - TC_HOST_RARD_CT_RD, - TC_HOST_OPEN_CT_OPEN, - TC_HOST_RP3_CT_RD, - TC_HOST_RP3_CT_RPU, - TC_HOST_RPU_CT_RD, -}; - -/* Forward declare the full list of states. This is indexed by usb_tc_state */ -static const struct usb_state tc_states[]; - - -#ifdef CONFIG_COMMON_RUNTIME -/* List of human readable state names for console debugging */ -const char * const tc_state_names[] = { - [TC_DISABLED] = "Disabled", - [TC_UNATTACHED_SNK] = "Unattached.SNK", - [TC_ATTACH_WAIT_SNK] = "AttachWait.SNK", - [TC_ATTACHED_SNK] = "Attached.SNK", - [TC_ERROR_RECOVERY] = "ErrorRecovery", - [TC_TRY_SNK] = "Try.SNK", - [TC_UNATTACHED_SRC] = "Unattached.SRC", - [TC_ATTACH_WAIT_SRC] = "AttachWait.SRC", - [TC_TRY_WAIT_SRC] = "TryWait.SRC", - [TC_ATTACHED_SRC] = "Attached.SRC", - [TC_CT_TRY_SNK] = "CTTry.SNK", - [TC_CT_ATTACH_WAIT_UNSUPPORTED] = "CTAttachWait.Unsupported", - [TC_CT_ATTACHED_UNSUPPORTED] = "CTAttached.Unsupported", - [TC_CT_UNATTACHED_UNSUPPORTED] = "CTUnattached.Unsupported", - [TC_CT_UNATTACHED_VPD] = "CTUnattached.VPD", - [TC_CT_DISABLED_VPD] = "CTDisabled.VPD", - [TC_CT_ATTACHED_VPD] = "CTAttached.VPD", - [TC_CT_ATTACH_WAIT_VPD] = "CTAttachWait.VPD", -}; -#endif - -/* Forward declare private, common functions */ -static void set_state_tc(const int port, enum usb_tc_state new_state); - -/* Public TypeC functions */ - -enum pd_power_role tc_get_power_role(int port) -{ - /* Vconn power device is always the sink */ - return PD_ROLE_SINK; -} - -enum pd_cable_plug tc_get_cable_plug(int port) -{ - /* Vconn power device is always the cable */ - return PD_PLUG_FROM_CABLE; -} - -enum pd_data_role tc_get_data_role(int port) -{ - /* Vconn power device doesn't have a data role, but UFP matches SNK */ - return PD_ROLE_UFP; -} - -/* Note tc_set_power_role and tc_set_data_role are unimplemented */ - -uint8_t tc_get_polarity(int port) -{ - /* Does not track polarity */ - return 0; -} - -uint8_t tc_get_pd_enabled(int port) -{ - return tc[port].pd_enable; -} - -void tc_reset_support_timer(int port) -{ - tc[port].support_timer_reset |= SUPPORT_TIMER_RESET_REQUEST; -} - -void tc_state_init(int port) -{ - int res = 0; - - res = tc_restart_tcpc(port); - - CPRINTS("TCPC p%d init %s", port, res ? "failed" : "ready"); - - /* Disable if restart failed, otherwise start in default state. */ - set_state_tc(port, res ? TC_DISABLED : TC_UNATTACHED_SNK); - - /* Disable pd state machines */ - tc[port].pd_enable = 0; - tc[port].billboard_presented = 0; - tc[port].flags = 0; -} - -void tc_event_check(int port, int evt) -{ - /* Do Nothing */ -} - -void tc_run(const int port) -{ - run_state(port, &tc[port].ctx); -} - -/* Internal Functions */ - -/* Set the TypeC state machine to a new state. */ -static void set_state_tc(const int port, enum usb_tc_state new_state) -{ - set_state(port, &tc[port].ctx, &tc_states[new_state]); -} - -/* Get the current TypeC state. */ -test_export_static enum usb_tc_state get_state_tc(const int port) -{ - return tc[port].ctx.current - &tc_states[0]; -} - -/* Get the previous TypeC state. */ -static enum usb_tc_state get_last_state_tc(const int port) -{ - return tc[port].ctx.previous - &tc_states[0]; -} - -test_mockable_static void print_current_state(const int port) -{ - CPRINTS("C%d: %s", port, tc_state_names[get_state_tc(port)]); -} - -/** - * Disabled - * - * Super State Entry Actions: - * Isolate the Host-side port from the Charge-Through port - * Enable mcu communication - * Remove the terminations from Host - * Remove the terminations from Charge-Through - */ -static void tc_disabled_entry(const int port) -{ - print_current_state(port); -} - -static void tc_disabled_run(const int port) -{ - task_wait_event(-1); -} - -static void tc_disabled_exit(const int port) -{ - if (!IS_ENABLED(CONFIG_USB_PD_TCPC)) { - if (tc_restart_tcpc(port) != 0) { - CPRINTS("TCPC p%d restart failed!", port); - return; - } - } - - CPRINTS("TCPC p%d resumed!", port); -} - -/** - * ErrorRecovery - * - * Super State Entry Actions: - * Isolate the Host-side port from the Charge-Through port - * Enable mcu communication - * Remove the terminations from Host - * Remove the terminations from Charge-Through - */ -static void tc_error_recovery_entry(const int port) -{ - print_current_state(port); - /* Use cc_debounce state variable for error recovery timeout */ - tc[port].cc_debounce = get_time().val + PD_T_ERROR_RECOVERY; -} - -static void tc_error_recovery_run(const int port) -{ - if (get_time().val > tc[port].cc_debounce) - set_state_tc(port, TC_UNATTACHED_SNK); -} - -/** - * Unattached.SNK - * - * Super State Entry Actions: - * Isolate the Host-side port from the Charge-Through port - * Enable mcu communication - * Place Ra on VCONN and Rd on Host CC - * Place Rd on Charge-Through CCs - */ -static void tc_unattached_snk_entry(const int port) -{ - if (get_last_state_tc(port) != TC_UNATTACHED_SRC) - print_current_state(port); - - tc[port].flags &= ~TC_FLAGS_VCONN_ON; - tc[port].cc_state = PD_CC_UNSET; -} - -static void tc_unattached_snk_run(const int port) -{ - int host_cc; - int new_cc_state; - int cc1; - int cc2; - - /* Check Host CC for connection */ - vpd_host_get_cc(&host_cc); - - /* - * Transition to AttachWait.SNK when a Source connection is - * detected, as indicated by the SNK.Rp state on its Host-side - * port’s CC pin. - */ - if (cc_is_rp(host_cc)) { - set_state_tc(port, TC_ATTACH_WAIT_SNK); - return; - } - - /* Check Charge-Through CCs for connection */ - vpd_ct_get_cc(&cc1, &cc2); - - if (cc_is_rp(cc1) != cc_is_rp(cc2)) - new_cc_state = PD_CC_DFP_ATTACHED; - else - new_cc_state = PD_CC_NONE; - - /* Debounce Charge-Through CC state */ - if (tc[port].cc_state != new_cc_state) { - tc[port].cc_state = new_cc_state; - tc[port].cc_debounce = get_time().val + PD_T_CC_DEBOUNCE; - } - - /* If we are here, Host CC must be open */ - - /* Wait for Charge-Through CC debounce */ - if (get_time().val < tc[port].cc_debounce) - return; - - /* - * A Charge-Through VCONN-Powered USB Device shall transition to - * Unattached.SRC when the state of the Host-side port’s CC pin is - * SNK.Open for tDRP − dcSRC.DRP ∙ tDRP and both of the following - * is detected on the Charge-Through port. - * 1) SNK.Rp state is detected on exactly one of the CC1 or CC2 - * pins for at least tCCDebounce - * 2) VBUS is detected - */ - if (vpd_is_ct_vbus_present() && - tc[port].cc_state == PD_CC_DFP_ATTACHED) { - set_state_tc(port, TC_UNATTACHED_SRC); - return; - } -} - -/** - * AttachWait.SNK - * - * Super State Entry Actions: - * Isolate the Host-side port from the Charge-Through port - * Enable mcu communication - * Place Ra on VCONN and Rd on Host CC - * Place Rd on Charge-Through CCs - */ -static void tc_attach_wait_snk_entry(const int port) -{ - print_current_state(port); - tc[port].host_cc_state = PD_CC_UNSET; -} - -static void tc_attach_wait_snk_run(const int port) -{ - int host_new_cc_state; - int host_cc; - - /* Check Host CC for connection */ - vpd_host_get_cc(&host_cc); - - if (cc_is_rp(host_cc)) - host_new_cc_state = PD_CC_DFP_ATTACHED; - else - host_new_cc_state = PD_CC_NONE; - - /* Debounce the Host CC state */ - if (tc[port].host_cc_state != host_new_cc_state) { - tc[port].host_cc_state = host_new_cc_state; - if (host_new_cc_state == PD_CC_DFP_ATTACHED) - tc[port].host_cc_debounce = get_time().val + - PD_T_CC_DEBOUNCE; - else - tc[port].host_cc_debounce = get_time().val + - PD_T_PD_DEBOUNCE; - return; - } - - /* Wait for Host CC debounce */ - if (get_time().val < tc[port].host_cc_debounce) - return; - - /* - * A Charge-Through VCONN-Powered USB Device shall transition to - * Attached.SNK after the state of the Host-side port’s CC pin is - * SNK.Rp for at least tCCDebounce and either host-side VCONN or - * VBUS is detected. - * - * Transition to Unattached.SNK when the state of both the CC1 and - * CC2 pins is SNK.Open for at least tPDDebounce. - */ - if (tc[port].host_cc_state == PD_CC_DFP_ATTACHED && - (vpd_is_vconn_present() || vpd_is_host_vbus_present())) - set_state_tc(port, TC_ATTACHED_SNK); - else if (tc[port].host_cc_state == PD_CC_NONE) - set_state_tc(port, TC_UNATTACHED_SNK); -} - -/** - * Attached.SNK - */ -static void tc_attached_snk_entry(const int port) -{ - print_current_state(port); - - /* Enable PD */ - tc[port].pd_enable = 1; - set_polarity(port, 0); - - /* - * This state can only be entered from states AttachWait.SNK - * and Try.SNK. So the Host port is isolated from the - * Charge-Through port. We only need to High-Z the - * Charge-Through ports CC1 and CC2 pins. - */ - vpd_ct_set_pull(TYPEC_CC_OPEN, 0); - - tc[port].host_cc_state = PD_CC_UNSET; - - /* Start Charge-Through support timer */ - tc[port].support_timer_reset = SUPPORT_TIMER_RESET_INIT; - tc[port].support_timer = get_time().val + PD_T_AME; -} - -static void tc_attached_snk_run(const int port) -{ - int host_new_cc_state; - int host_cc; - - /* Has host vbus and vconn been removed */ - if (!vpd_is_host_vbus_present() && !vpd_is_vconn_present()) { - set_state_tc(port, TC_UNATTACHED_SNK); - return; - } - - /* - * Reset the Charge-Through Support Timer when it first - * receives any USB PD Structured VDM Command it supports, - * which is the Discover Identity command. And this is only - * done one time. - */ - if (tc[port].support_timer_reset == SUPPORT_TIMER_RESET_REQUEST) { - tc[port].support_timer_reset |= SUPPORT_TIMER_RESET_COMPLETE; - tc[port].support_timer = get_time().val + PD_T_AME; - } - - /* Check Host CC for connection */ - vpd_host_get_cc(&host_cc); - - if (cc_is_rp(host_cc)) - host_new_cc_state = PD_CC_DFP_ATTACHED; - else - host_new_cc_state = PD_CC_NONE; - - /* Debounce the Host CC state */ - if (tc[port].host_cc_state != host_new_cc_state) { - tc[port].host_cc_state = host_new_cc_state; - tc[port].host_cc_debounce = get_time().val + PD_T_VPDCTDD; - return; - } - - /* Wait for Host CC debounce */ - if (get_time().val < tc[port].host_cc_debounce) - return; - - if (vpd_is_vconn_present()) { - if (!(tc[port].flags & TC_FLAGS_VCONN_ON)) { - /* VCONN detected. Remove RA */ - vpd_host_set_pull(TYPEC_CC_RD, 0); - tc[port].flags |= TC_FLAGS_VCONN_ON; - } - - /* - * A Charge-Through VCONN-Powered USB Device shall transition - * to CTUnattached.VPD if VCONN is present and the state of - * its Host-side port’s CC pin is SNK.Open for tVPDCTDD. - */ - if (tc[port].host_cc_state == PD_CC_NONE) { - set_state_tc(port, TC_CT_UNATTACHED_VPD); - return; - } - } - - /* Check the Support Timer */ - if (get_time().val > tc[port].support_timer && - !tc[port].billboard_presented) { - /* - * Present USB Billboard Device Class interface - * indicating that Charge-Through is not supported - */ - tc[port].billboard_presented = 1; - vpd_present_billboard(BB_SNK); - } -} - -static void tc_attached_snk_exit(const int port) -{ - tc[port].billboard_presented = 0; - vpd_present_billboard(BB_NONE); -} - -/** - * Super State HOST_RA_CT_RD - */ -static void tc_host_rard_ct_rd_entry(const int port) -{ - /* Place Ra on VCONN and Rd on Host CC */ - vpd_host_set_pull(TYPEC_CC_RA_RD, 0); - - /* Place Rd on Charge-Through CCs */ - vpd_ct_set_pull(TYPEC_CC_RD, 0); -} - -/** - * Super State HOST_OPEN_CT_OPEN - */ -static void tc_host_open_ct_open_entry(const int port) -{ - /* Remove the terminations from Host */ - vpd_host_set_pull(TYPEC_CC_OPEN, 0); - - /* Remove the terminations from Charge-Through */ - vpd_ct_set_pull(TYPEC_CC_OPEN, 0); -} - -/** - * Super State VBUS_CC_ISO - */ -static void tc_vbus_cc_iso_entry(const int port) -{ - /* Isolate the Host-side port from the Charge-Through port */ - vpd_vbus_pass_en(0); - - /* Remove Charge-Through side port CCs */ - vpd_ct_cc_sel(CT_OPEN); - - /* Enable mcu communication and cc */ - vpd_mcu_cc_en(1); -} - -/** - * Unattached.SRC - * - * Super State Entry Actions: - * Isolate the Host-side port from the Charge-Through port - * Enable mcu communication - * Place RpUSB on Host CC - * Place Rd on Charge-Through CCs - */ -static void tc_unattached_src_entry(const int port) -{ - if (get_last_state_tc(port) != TC_UNATTACHED_SNK) - print_current_state(port); - - /* Get power from VBUS */ - vpd_vconn_pwr_sel_odl(PWR_VBUS); - - /* Make sure it's the Charge-Through Port's VBUS */ - if (!vpd_is_ct_vbus_present()) { - set_state_tc(port, TC_ERROR_RECOVERY); - return; - } - - tc[port].next_role_swap = get_time().val + PD_T_DRP_SRC; -} - -static void tc_unattached_src_run(const int port) -{ - int host_cc; - - /* Check Host CC for connection */ - vpd_host_get_cc(&host_cc); - - /* - * Transition to AttachWait.SRC when host-side VBUS is - * vSafe0V and SRC.Rd state is detected on the Host-side - * port’s CC pin. - */ - if (!vpd_is_host_vbus_present() && host_cc == TYPEC_CC_VOLT_RD) { - set_state_tc(port, TC_ATTACH_WAIT_SRC); - return; - } - - /* - * Transition to Unattached.SNK within tDRPTransition or - * if Charge-Through VBUS is removed. - */ - if (!vpd_is_ct_vbus_present() || - get_time().val > tc[port].next_role_swap) { - set_state_tc(port, TC_UNATTACHED_SNK); - return; - } -} - -/** - * AttachWait.SRC - * - * Super State Entry Actions: - * Isolate the Host-side port from the Charge-Through port - * Enable mcu communication - * Place RpUSB on Host CC - * Place Rd on Charge-Through CCs - */ -static void tc_attach_wait_src_entry(const int port) -{ - print_current_state(port); - - tc[port].host_cc_state = PD_CC_UNSET; -} - -static void tc_attach_wait_src_run(const int port) -{ - int host_new_cc_state; - int host_cc; - - /* Check Host CC for connection */ - vpd_host_get_cc(&host_cc); - - if (host_cc == TYPEC_CC_VOLT_RD) - host_new_cc_state = PD_CC_UFP_ATTACHED; - else - host_new_cc_state = PD_CC_NONE; - - /* - * A Charge-Through VCONN-Powered USB Device shall transition - * to Unattached.SNK when the SRC.Open state is detected on the - * Host-side port’s CC or if Charge-Through VBUS falls below - * vSinkDisconnect. The Charge-Through VCONN-Powered USB Device - * shall detect the SRC.Open state within tSRCDisconnect, but - * should detect it as quickly as possible. - */ - if (host_new_cc_state == PD_CC_NONE || !vpd_is_ct_vbus_present()) { - set_state_tc(port, TC_UNATTACHED_SNK); - return; - } - - /* Debounce the Host CC state */ - if (tc[port].host_cc_state != host_new_cc_state) { - tc[port].host_cc_state = host_new_cc_state; - tc[port].cc_debounce = get_time().val + PD_T_CC_DEBOUNCE; - return; - } - - /* Wait for Host CC debounce */ - if (get_time().val < tc[port].cc_debounce) - return; - - /* - * A Charge-Through VCONN-Powered USB Device shall transition to - * Try.SNK when the host-side VBUS is at vSafe0V and the SRC.Rd - * state is on the Host-side port’s CC pin for at least tCCDebounce. - */ - if (tc[port].host_cc_state == PD_CC_UFP_ATTACHED && - !vpd_is_host_vbus_present()) { - set_state_tc(port, TC_TRY_SNK); - return; - } -} - -/** - * Attached.SRC - */ -static void tc_attached_src_entry(const int port) -{ - print_current_state(port); - - /* Enable PD */ - tc[port].pd_enable = 1; - set_polarity(port, 0); - - /* Connect Charge-Through VBUS to Host VBUS */ - vpd_vbus_pass_en(1); - - /* - * Get power from VBUS. No need to test because - * the Host VBUS is connected to the Charge-Through - * VBUS - */ - vpd_vconn_pwr_sel_odl(PWR_VBUS); -} - -static void tc_attached_src_run(const int port) -{ - int host_cc; - - /* Check Host CC for connection */ - vpd_host_get_cc(&host_cc); - - /* - * A Charge-Through VCONN-Powered USB Device shall transition to - * Unattached.SNK when VBUS falls below vSinkDisconnect or the - * Host-side port’s CC pin is SRC.Open. The Charge-Through - * VCONNPowered USB Device shall detect the SRC.Open state within - * tSRCDisconnect, but should detect it as quickly as possible. - */ - if (!vpd_is_ct_vbus_present() || host_cc == TYPEC_CC_VOLT_OPEN) - set_state_tc(port, TC_UNATTACHED_SNK); -} - -/** - * Super State HOST_RPU_CT_RD - */ -static void tc_host_rpu_ct_rd_entry(const int port) -{ - /* Place RpUSB on Host CC */ - vpd_host_set_pull(TYPEC_CC_RP, TYPEC_RP_USB); - - /* Place Rd on Charge-Through CCs */ - vpd_ct_set_pull(TYPEC_CC_RD, 0); -} - -/** - * Try.SNK - * - * Super State Entry Actions: - * Isolate the Host-side port from the Charge-Through port - * Enable mcu communication - * Place Ra on VCONN and Rd on Host CC - * Place Rd on Charge-Through CCs - */ -static void tc_try_snk_entry(const int port) -{ - print_current_state(port); - - /* Get power from VBUS */ - vpd_vconn_pwr_sel_odl(PWR_VBUS); - - /* Make sure it's the Charge-Through Port's VBUS */ - if (!vpd_is_ct_vbus_present()) { - set_state_tc(port, TC_ERROR_RECOVERY); - return; - } - - tc[port].host_cc_state = PD_CC_UNSET; - - /* Using next_role_swap timer as try_src timer */ - tc[port].next_role_swap = get_time().val + PD_T_DRP_TRY; -} - -static void tc_try_snk_run(const int port) -{ - int host_new_cc_state; - int host_cc; - - /* - * Wait for tDRPTry before monitoring the Charge-Through - * port’s CC pins for the SNK.Rp - */ - if (get_time().val < tc[port].next_role_swap) - return; - - /* Check Host CC for connection */ - vpd_host_get_cc(&host_cc); - - if (cc_is_rp(host_cc)) - host_new_cc_state = PD_CC_DFP_ATTACHED; - else - host_new_cc_state = PD_CC_NONE; - - /* Debounce the Host CC state */ - if (tc[port].host_cc_state != host_new_cc_state) { - tc[port].host_cc_state = host_new_cc_state; - tc[port].cc_debounce = get_time().val + PD_T_DEBOUNCE; - return; - } - - /* Wait for Host CC debounce */ - if (get_time().val < tc[port].cc_debounce) - return; - - /* - * The Charge-Through VCONN-Powered USB Device shall then transition to - * Attached.SNK when the SNK.Rp state is detected on the Host-side - * port’s CC pin for at least tTryCCDebounce and VBUS or VCONN is - * detected on Host-side port. - * - * Alternatively, the Charge-Through VCONN-Powered USB Device shall - * transition to TryWait.SRC if Host-side SNK.Rp state is not detected - * for tTryCCDebounce. - */ - if (tc[port].host_cc_state == PD_CC_DFP_ATTACHED && - (vpd_is_host_vbus_present() || vpd_is_vconn_present())) - set_state_tc(port, TC_ATTACHED_SNK); - else if (tc[port].host_cc_state == PD_CC_NONE) - set_state_tc(port, TC_TRY_WAIT_SRC); -} - -/** - * TryWait.SRC - * - * Super State Entry Actions: - * Isolate the Host-side port from the Charge-Through port - * Enable mcu communication - * Place RpUSB on Host CC - * Place Rd on Charge-Through CCs - */ -static void tc_try_wait_src_entry(const int port) -{ - print_current_state(port); - - tc[port].host_cc_state = PD_CC_UNSET; - tc[port].next_role_swap = get_time().val + PD_T_DRP_TRY; -} - -static void tc_try_wait_src_run(const int port) -{ - int host_new_cc_state; - int host_cc; - - /* Check Host CC for connection */ - vpd_host_get_cc(&host_cc); - - if (host_cc == TYPEC_CC_VOLT_RD) - host_new_cc_state = PD_CC_UFP_ATTACHED; - else - host_new_cc_state = PD_CC_NONE; - - /* Debounce the Host CC state */ - if (tc[port].host_cc_state != host_new_cc_state) { - tc[port].host_cc_state = host_new_cc_state; - tc[port].host_cc_debounce = - get_time().val + PD_T_TRY_CC_DEBOUNCE; - return; - } - - if (get_time().val > tc[port].host_cc_debounce) { - /* - * A Charge-Through VCONN-Powered USB Device shall transition - * to Attached.SRC when host-side VBUS is at vSafe0V and the - * SRC.Rd state is detected on the Host-side port’s CC pin for - * at least tTryCCDebounce. - */ - if (tc[port].host_cc_state == PD_CC_UFP_ATTACHED && - !vpd_is_host_vbus_present()) { - set_state_tc(port, TC_ATTACHED_SRC); - return; - } - } - - if (get_time().val > tc[port].next_role_swap) { - /* - * The Charge-Through VCONN-Powered USB Device shall transition - * to Unattached.SNK after tDRPTry if the Host-side port’s CC - * pin is not in the SRC.Rd state. - */ - if (tc[port].host_cc_state == PD_CC_NONE) { - set_state_tc(port, TC_UNATTACHED_SNK); - return; - } - } -} - -/** - * CTTry.SNK - * - * Super State Entry Actions: - * Isolate the Host-side port from the Charge-Through port - * Enable mcu communication - * Place RP3A0 on Host CC - * Connect Charge-Through Rd - * Get power from VCONN - */ -static void tc_ct_try_snk_entry(const int port) -{ - print_current_state(port); - - /* Enable PD */ - tc[port].pd_enable = 1; - set_polarity(port, 0); - - tc[port].cc_state = PD_CC_UNSET; - tc[port].next_role_swap = get_time().val + PD_T_DRP_TRY; -} - -static void tc_ct_try_snk_run(const int port) -{ - int new_cc_state; - int cc1; - int cc2; - - /* - * Wait for tDRPTry before monitoring the Charge-Through - * port’s CC pins for the SNK.Rp - */ - if (get_time().val < tc[port].next_role_swap) - return; - - /* Check CT CC for connection */ - vpd_ct_get_cc(&cc1, &cc2); - - if (cc_is_rp(cc1) || cc_is_rp(cc2)) - new_cc_state = PD_CC_DFP_ATTACHED; - else - new_cc_state = PD_CC_NONE; - - /* - * The Charge-Through VCONN-Powered USB Device shall transition - * to Unattached.SNK if VCONN falls below vVCONNDisconnect. - */ - if (!vpd_is_vconn_present()) { - set_state_tc(port, TC_UNATTACHED_SNK); - return; - } - - /* Debounce the CT CC state */ - if (tc[port].cc_state != new_cc_state) { - tc[port].cc_state = new_cc_state; - tc[port].cc_debounce = get_time().val + PD_T_DEBOUNCE; - tc[port].try_wait_debounce = get_time().val + PD_T_TRY_WAIT; - - return; - } - - if (get_time().val > tc[port].cc_debounce) { - /* - * The Charge-Through VCONN-Powered USB Device shall then - * transition to CTAttached.VPD when the SNK.Rp state is - * detected on the Charge-Through port’s CC pins for at - * least tTryCCDebounce and VBUS is detected on - * Charge-Through port. - */ - if (tc[port].cc_state == PD_CC_DFP_ATTACHED && - vpd_is_ct_vbus_present()) { - set_state_tc(port, TC_CT_ATTACHED_VPD); - return; - } - } - - if (get_time().val > tc[port].try_wait_debounce) { - /* - * A Charge-Through VCONN-Powered USB Device shall transition - * to CTAttached.Unsupported if SNK.Rp state is not detected - * for tDRPTryWait. - */ - if (tc[port].cc_state == PD_CC_NONE) { - set_state_tc(port, - TC_CT_ATTACHED_UNSUPPORTED); - return; - } - } -} - -static void tc_ct_try_snk_exit(const int port) -{ - /* Disable PD */ - tc[port].pd_enable = 0; -} - -/** - * CTAttachWait.Unsupported - * - * Super State Entry Actions: - * Isolate the Host-side port from the Charge-Through port - * Enable mcu communication - * Place RP3A0 on Host CC - * Place RPUSB on Charge-Through CC - * Get power from VCONN - */ -static void tc_ct_attach_wait_unsupported_entry(const int port) -{ - print_current_state(port); - - /* Enable PD */ - tc[port].pd_enable = 1; - set_polarity(port, 0); - - tc[port].cc_state = PD_CC_UNSET; -} - -static void tc_ct_attach_wait_unsupported_run(const int port) -{ - int new_cc_state; - int cc1; - int cc2; - - /* Check CT CC for connection */ - vpd_ct_get_cc(&cc1, &cc2); - - if (cc_is_at_least_one_rd(cc1, cc2)) - new_cc_state = PD_CC_UFP_ATTACHED; - else if (cc_is_audio_acc(cc1, cc2)) - new_cc_state = PD_CC_UFP_AUDIO_ACC; - else /* (cc1 == TYPEC_CC_VOLT_OPEN or cc2 == TYPEC_CC_VOLT_OPEN */ - new_cc_state = PD_CC_NONE; - - /* - * A Charge-Through VCONN-Powered USB Device shall transition to - * Unattached.SNK if VCONN falls below vVCONNDisconnect. - */ - if (!vpd_is_vconn_present()) { - set_state_tc(port, TC_UNATTACHED_SNK); - return; - } - - /* Debounce the cc state */ - if (tc[port].cc_state != new_cc_state) { - tc[port].cc_state = new_cc_state; - tc[port].cc_debounce = get_time().val + PD_T_CC_DEBOUNCE; - return; - } - - /* Wait for CC debounce */ - if (get_time().val < tc[port].cc_debounce) - return; - - /* - * A Charge-Through VCONN-Powered USB Device shall transition to - * CTUnattached.VPD when the state of either the Charge-Through - * Port’s CC1 or CC2 pin is SRC.Open for at least tCCDebounce. - * - * A Charge-Through VCONN-Powered USB Device shall transition to - * CTTry.SNK if the state of at least one of the Charge-Through - * port’s CC pins is SRC.Rd, or if the state of both the CC1 and CC2 - * pins is SRC.Ra. for at least tCCDebounce. - */ - if (new_cc_state == PD_CC_NONE) - set_state_tc(port, TC_CT_UNATTACHED_VPD); - else /* PD_CC_UFP_ATTACHED or PD_CC_UFP_AUDIO_ACC */ - set_state_tc(port, TC_CT_TRY_SNK); -} - -static void tc_ct_attach_wait_unsupported_exit(const int port) -{ - /* Disable PD */ - tc[port].pd_enable = 0; -} - -/** - * CTAttached.Unsupported - * - * Super State Entry Actions: - * Isolate the Host-side port from the Charge-Through port - * Enable mcu communication - * Place RP3A0 on Host CC - * Place RPUSB on Charge-Through CC - * Get power from VCONN - */ -static void tc_ct_attached_unsupported_entry(const int port) -{ - print_current_state(port); - - /* Present Billboard device */ - vpd_present_billboard(BB_SNK); -} - -static void tc_ct_attached_unsupported_run(const int port) -{ - int cc1; - int cc2; - - /* Check CT CC for connection */ - vpd_ct_get_cc(&cc1, &cc2); - - if (!vpd_is_vconn_present()) { - set_state_tc(port, TC_UNATTACHED_SNK); - return; - } - - /* - * The Charge-Through VCONN-Powered USB Device shall transition to - * CTUnattached.VPD when SRC.Open state is detected on both the - * Charge-Through port’s CC pins or the SRC.Open state is detected - * on one CC pin and SRC.Ra is detected on the other CC pin. - */ - if ((cc1 == TYPEC_CC_VOLT_OPEN && cc2 == TYPEC_CC_VOLT_OPEN) || - (cc1 == TYPEC_CC_VOLT_OPEN && cc2 == TYPEC_CC_VOLT_RA) || - (cc1 == TYPEC_CC_VOLT_RA && cc2 == TYPEC_CC_VOLT_OPEN)) { - set_state_tc(port, TC_CT_UNATTACHED_VPD); - return; - } -} - -static void tc_ct_attached_unsupported_exit(const int port) -{ - vpd_present_billboard(BB_NONE); -} - -/** - * CTUnattached.Unsupported - * - * Super State Entry Actions: - * Isolate the Host-side port from the Charge-Through port - * Enable mcu communication - * Place RP3A0 on Host CC - * Place RPUSB on Charge-Through CC - * Get power from VCONN - */ -static void tc_ct_unattached_unsupported_entry(const int port) -{ - if (get_last_state_tc(port) != TC_CT_UNATTACHED_VPD) - print_current_state(port); - - /* Enable PD */ - tc[port].pd_enable = 1; - set_polarity(port, 0); - - tc[port].next_role_swap = get_time().val + PD_T_DRP_SRC; -} - -static void tc_ct_unattached_unsupported_run(const int port) -{ - int cc1; - int cc2; - - /* Check CT CC for connection */ - vpd_ct_get_cc(&cc1, &cc2); - - /* - * A Charge-Through VCONN-Powered USB Device shall transition to - * CTAttachWait.Unsupported when a Sink connection is detected on - * the Charge-Through port, as indicated by the SRC.Rd state on at - * least one of the Charge-Through port’s CC pins or SRC.Ra state - * on both the CC1 and CC2 pins. - */ - if (cc_is_at_least_one_rd(cc1, cc2) || cc_is_audio_acc(cc1, cc2)) { - set_state_tc(port, - TC_CT_ATTACH_WAIT_UNSUPPORTED); - return; - } - - /* - * A Charge-Through VCONN-Powered USB Device shall transition to - * Unattached.SNK if VCONN falls below vVCONNDisconnect. - */ - if (!vpd_is_vconn_present()) { - set_state_tc(port, TC_UNATTACHED_SNK); - return; - } - - /* - * A Charge-Through VCONN-Powered USB Device shall transition to - * CTUnattached.VPD within tDRPTransition after dcSRC.DRP ∙ tDRP. - */ - if (get_time().val > tc[port].next_role_swap) { - set_state_tc(port, TC_CT_UNATTACHED_VPD); - return; - } -} - -static void tc_ct_unattached_unsupported_exit(const int port) -{ - /* Disable PD */ - tc[port].pd_enable = 0; -} - -/** - * CTUnattached.VPD - * - * Super State Entry Actions: - * Isolate the Host-side port from the Charge-Through port - * Enable mcu communication - * Place RP3A0 on Host CC - * Connect Charge-Through Rd - * Get power from VCONN - */ -static void tc_ct_unattached_vpd_entry(const int port) -{ - if (get_last_state_tc(port) != TC_CT_UNATTACHED_UNSUPPORTED) - print_current_state(port); - - /* Enable PD */ - tc[port].pd_enable = 1; - set_polarity(port, 0); - - tc[port].cc_state = PD_CC_UNSET; -} - -static void tc_ct_unattached_vpd_run(const int port) -{ - int new_cc_state; - int cc1; - int cc2; - - /* Check CT CC for connection */ - vpd_ct_get_cc(&cc1, &cc2); - - if (cc_is_rp(cc1) != cc_is_rp(cc2)) - new_cc_state = PD_CC_DFP_ATTACHED; - else if (!cc_is_rp(cc1) && !cc_is_rp(cc2)) - new_cc_state = PD_CC_NONE; - else - new_cc_state = PD_CC_UNSET; - - /* - * A Charge-Through VCONN-Powered USB Device shall transition to - * CTAttachWait.VPD when a Source connection is detected on the - * Charge-Through port, as indicated by the SNK.Rp state on - * exactly one of the Charge-Through port’s CC pins. - */ - if (new_cc_state == PD_CC_DFP_ATTACHED) { - set_state_tc(port, TC_CT_ATTACH_WAIT_VPD); - return; - } - - /* - * A Charge-Through VCONN-Powered USB Device shall transition to - * Unattached.SNK if VCONN falls below vVCONNDisconnect. - */ - if (!vpd_is_vconn_present()) { - set_state_tc(port, TC_UNATTACHED_SNK); - return; - } - - /* Debounce the cc state */ - if (new_cc_state != tc[port].cc_state) { - tc[port].cc_state = new_cc_state; - tc[port].cc_debounce = get_time().val + PD_T_DRP_SRC; - return; - } - - if (get_time().val < tc[port].cc_debounce) - return; - - /* - * A Charge-Through VCONN-Powered USB Device shall transition to - * CTUnattached.Unsupported within tDRPTransition after the state - * of both the Charge-Through port’s CC1 and CC2 pins is SNK.Open - * for tDRP-dcSRC.DRP ∙ tDRP, or if directed. - */ - if (tc[port].cc_state == PD_CC_NONE) { - set_state_tc(port, TC_CT_UNATTACHED_UNSUPPORTED); - return; - } -} - -static void tc_ct_unattached_vpd_exit(const int port) -{ - /* Disable PD */ - tc[port].pd_enable = 0; -} - -/** - * CTDisabled.VPD - * - * Super State Entry Actions: - * Isolate the Host-side port from the Charge-Through port - * Enable mcu communication - * Remove the terminations from Host - * Remove the terminations from Charge-Through - */ -static void tc_ct_disabled_vpd_entry(const int port) -{ - print_current_state(port); - - /* Get power from VBUS */ - vpd_vconn_pwr_sel_odl(PWR_VBUS); - - tc[port].next_role_swap = get_time().val + PD_T_VPDDISABLE; -} - -static void tc_ct_disabled_vpd_run(const int port) -{ - /* - * A Charge-Through VCONN-Powered USB Device shall transition - * to Unattached.SNK after tVPDDisable. - */ - if (get_time().val > tc[port].next_role_swap) - set_state_tc(port, TC_UNATTACHED_SNK); -} - -/** - * CTAttached.VPD - */ -static void tc_ct_attached_vpd_entry(const int port) -{ - int cc1; - int cc2; - print_current_state(port); - - /* Get power from VCONN */ - vpd_vconn_pwr_sel_odl(PWR_VCONN); - - /* - * Detect which of the Charge-Through port’s CC1 or CC2 - * pins is connected through the cable - */ - vpd_ct_get_cc(&cc1, &cc2); - tc[port].ct_cc = cc_is_rp(cc2) ? CT_CC2 : CT_CC1; - - /* - * 1. Remove or reduce any additional capacitance on the - * Host-side CC port - */ - vpd_mcu_cc_en(0); - - /* - * 2. Disable the Rp termination advertising 3.0 A on the - * host port’s CC pin - */ - vpd_host_set_pull(TYPEC_CC_OPEN, 0); - - /* - * 3. Passively multiplex the detected Charge-Through port’s - * CC pin through to the host port’s CC - */ - vpd_ct_cc_sel(tc[port].ct_cc); - - /* - * 4. Disable the Rd on the Charge-Through port’s CC1 and CC2 - * pins - */ - vpd_ct_set_pull(TYPEC_CC_OPEN, 0); - - /* - * 5. Connect the Charge-Through port’s VBUS through to the - * host port’s VBUS - */ - vpd_vbus_pass_en(1); - - tc[port].cc_state = PD_CC_UNSET; -} - -static void tc_ct_attached_vpd_run(const int port) -{ - int new_cc_state; - int cc1; - int cc2; - - /* - * A Charge-Through VCONN-Powered USB Device shall transition to - * CTDisabled.VPD if VCONN falls below vVCONNDisconnect. - */ - if (!vpd_is_vconn_present()) { - set_state_tc(port, TC_CT_DISABLED_VPD); - return; - } - - /* Check CT CC for connection */ - vpd_ct_get_cc(&cc1, &cc2); - if ((tc[port].ct_cc ? cc2 : cc1) == TYPEC_CC_VOLT_OPEN) - new_cc_state = PD_CC_NONE; - else - new_cc_state = PD_CC_DFP_ATTACHED; - - /* Debounce the cc state */ - if (new_cc_state != tc[port].cc_state) { - tc[port].cc_state = new_cc_state; - tc[port].cc_debounce = get_time().val + PD_T_VPDCTDD; - return; - } - - if (get_time().val < tc[port].pd_debounce) - return; - - /* - * A Charge-Through VCONN-Powered USB Device shall transition to - * CTUnattached.VPD when VBUS falls below vSinkDisconnect and the - * state of the passed-through CC pin is SNK.Open for tVPDCTDD. - */ - if (tc[port].cc_state == PD_CC_NONE && !vpd_is_ct_vbus_present()) - set_state_tc(port, TC_CT_UNATTACHED_VPD); -} - -/** - * CTAttachWait.VPD - * - * Super State Entry Actions: - * Isolate the Host-side port from the Charge-Through port - * Enable mcu communication - * Place RP3A0 on Host CC - * Connect Charge-Through Rd - * Get power from VCONN - */ -static void tc_ct_attach_wait_vpd_entry(const int port) -{ - print_current_state(port); - - /* Enable PD */ - tc[port].pd_enable = 1; - set_polarity(port, 0); - - tc[port].cc_state = PD_CC_UNSET; -} - -static void tc_ct_attach_wait_vpd_run(const int port) -{ - int new_cc_state; - int cc1; - int cc2; - - /* Check CT CC for connection */ - vpd_ct_get_cc(&cc1, &cc2); - - if (cc_is_rp(cc1) != cc_is_rp(cc2)) - new_cc_state = PD_CC_DFP_ATTACHED; - else if (!cc_is_rp(cc1) && !cc_is_rp(cc2)) - new_cc_state = PD_CC_NONE; - else - new_cc_state = PD_CC_UNSET; - - /* - * A Charge-Through VCONN-Powered USB Device shall transition to - * CTDisabled.VPD if VCONN falls below vVCONNDisconnect. - */ - if (!vpd_is_vconn_present()) { - set_state_tc(port, TC_CT_DISABLED_VPD); - return; - } - - /* Debounce the cc state */ - if (new_cc_state != tc[port].cc_state) { - tc[port].cc_state = new_cc_state; - tc[port].cc_debounce = get_time().val + - PD_T_CC_DEBOUNCE; - tc[port].pd_debounce = get_time().val + - PD_T_PD_DEBOUNCE; - return; - } - - if (get_time().val > tc[port].pd_debounce) { - /* - * A Charge-Through VCONN-Powered USB Device shall transition - * to CTUnattached.VPD when the state of both the Charge-Through - * port’s CC1 and CC2 pins are SNK.Open for at least - * tPDDebounce. - */ - if (tc[port].cc_state == PD_CC_NONE) { - set_state_tc(port, TC_CT_UNATTACHED_VPD); - return; - } - } - - if (get_time().val > tc[port].cc_debounce) { - /* - * A Charge-Through VCONN-Powered USB Device shall transition to - * CTAttached.VPD after the state of only one of the - * Charge-Through port’s CC1 or CC2 pins is SNK.Rp for at - * least tCCDebounce and VBUS on the Charge-Through port is - * detected. - */ - if (tc[port].cc_state == PD_CC_DFP_ATTACHED && - vpd_is_ct_vbus_present()) { - set_state_tc(port, TC_CT_ATTACHED_VPD); - return; - } - } -} - -static void tc_ct_attach_wait_vpd_exit(const int port) -{ - /* Disable PD */ - tc[port].pd_enable = 0; -} - -/** - * Super State HOST_RP3_CT_RD - */ -static void tc_host_rp3_ct_rd_entry(const int port) -{ - /* Place RP3A0 on Host CC */ - vpd_host_set_pull(TYPEC_CC_RP, TYPEC_RP_3A0); - - /* Connect Charge-Through Rd */ - vpd_ct_set_pull(TYPEC_CC_RD, 0); - - /* - * A Charge-Through VCONN-Powered USB Device shall - * ensure that it is powered by VCONN - */ - - /* Make sure vconn is on */ - if (!vpd_is_vconn_present()) - set_state_tc(port, TC_ERROR_RECOVERY); - - /* Get power from VCONN */ - vpd_vconn_pwr_sel_odl(PWR_VCONN); -} - -/** - * Super State HOST_RP3_CT_RPU - */ -static void tc_host_rp3_ct_rpu_entry(const int port) -{ - /* Place RP3A0 on Host CC */ - vpd_host_set_pull(TYPEC_CC_RP, TYPEC_RP_3A0); - - /* Place RPUSB on Charge-Through CC */ - vpd_ct_set_pull(TYPEC_CC_RP, TYPEC_RP_USB); - - /* - * A Charge-Through VCONN-Powered USB Device shall - * ensure that it is powered by VCONN - */ - - /* Make sure vconn is on */ - if (!vpd_is_vconn_present()) - set_state_tc(port, TC_ERROR_RECOVERY); - - /* Get power from VCONN */ - vpd_vconn_pwr_sel_odl(PWR_VCONN); -} - -/* All necessary Type-C states */ - -/* - * Type-C State Hierarchy (Sub-States are listed inside the boxes) - * - * | TC_VBUS_CC_ISO ------------------------------------------------------| - * | | - * | | TC_HOST_RARD_CT_RD -----------| | TC_HOST_OPEN_CT_OPEN ---------| | - * | | | | | | - * | | TC_UNATTACHED_SNK | | TC_DISABLED | | - * | | TC_ATTACH_WAIT_SNK | | TC_ERROR_RECOVERY | | - * | | TC_TRY_SNK | |-------------------------------| | - * | |-------------------------------| | - * | | - * | | TC_HOST_RP3_CT_RD ------------| | TC_HOST_RPU_CT_RD ------------| | - * | | | | | | - * | | TC_CT_TRY_SNK | | TC_UNATTACHED_SRC | | - * | | TC_CT_UNATTACHED_VPD | | TC_ATTACH_WAIT_SRC | | - * | | TC_CT_ATTACH_WAIT_VPD | | TC_TRY_WAIT_SR | | - * | |-------------------------------| |-------------------------------| | - * | | - * | | TC_HOST_RP3_CT_RPU -----------| | - * | | | | - * | | TC_CT_ATTACH_WAIT_UNSUPPORTED | | - * | | TC_CT_ATTACHED_UNSUPPORTED | | - * | | TC_CT_UNATTACHED_UNSUPPORTED | | - * | |-------------------------------| | - * |----------------------------------------------------------------------| - * - * TC_ATTACHED_SNK - * TC_ATTACHED_SRC - * TC_CT_ATTACHED_VPD - * - */ -static const struct usb_state tc_states[] = { - /* Super States */ - [TC_VBUS_CC_ISO] = { - .entry = tc_vbus_cc_iso_entry, - }, - [TC_HOST_RARD_CT_RD] = { - .entry = tc_host_rard_ct_rd_entry, - .parent = &tc_states[TC_VBUS_CC_ISO], - }, - [TC_HOST_OPEN_CT_OPEN] = { - .entry = tc_host_open_ct_open_entry, - .parent = &tc_states[TC_VBUS_CC_ISO], - }, - [TC_HOST_RP3_CT_RD] = { - .entry = tc_host_rp3_ct_rd_entry, - .parent = &tc_states[TC_VBUS_CC_ISO], - }, - [TC_HOST_RP3_CT_RPU] = { - .entry = tc_host_rp3_ct_rpu_entry, - .parent = &tc_states[TC_VBUS_CC_ISO], - }, - [TC_HOST_RPU_CT_RD] = { - .entry = tc_host_rpu_ct_rd_entry, - .parent = &tc_states[TC_VBUS_CC_ISO], - }, - /* Normal States */ - [TC_DISABLED] = { - .entry = tc_disabled_entry, - .run = tc_disabled_run, - .exit = tc_disabled_exit, - .parent = &tc_states[TC_HOST_OPEN_CT_OPEN], - }, - [TC_UNATTACHED_SNK] = { - .entry = tc_unattached_snk_entry, - .run = tc_unattached_snk_run, - .parent = &tc_states[TC_HOST_RARD_CT_RD], - }, - [TC_ATTACH_WAIT_SNK] = { - .entry = tc_attach_wait_snk_entry, - .run = tc_attach_wait_snk_run, - .parent = &tc_states[TC_HOST_RARD_CT_RD], - }, - [TC_ATTACHED_SNK] = { - .entry = tc_attached_snk_entry, - .run = tc_attached_snk_run, - .exit = tc_attached_snk_exit, - }, - [TC_ERROR_RECOVERY] = { - .entry = tc_error_recovery_entry, - .run = tc_error_recovery_run, - .parent = &tc_states[TC_HOST_OPEN_CT_OPEN], - }, - [TC_TRY_SNK] = { - .entry = tc_try_snk_entry, - .run = tc_try_snk_run, - .parent = &tc_states[TC_HOST_RARD_CT_RD], - }, - [TC_UNATTACHED_SRC] = { - .entry = tc_unattached_src_entry, - .run = tc_unattached_src_run, - .parent = &tc_states[TC_HOST_RPU_CT_RD], - }, - [TC_ATTACH_WAIT_SRC] = { - .entry = tc_attach_wait_src_entry, - .run = tc_attach_wait_src_run, - .parent = &tc_states[TC_HOST_RPU_CT_RD], - }, - [TC_TRY_WAIT_SRC] = { - .entry = tc_try_wait_src_entry, - .run = tc_try_wait_src_run, - .parent = &tc_states[TC_HOST_RPU_CT_RD], - }, - [TC_ATTACHED_SRC] = { - .entry = tc_attached_src_entry, - .run = tc_attached_src_run, - }, - [TC_CT_TRY_SNK] = { - .entry = tc_ct_try_snk_entry, - .run = tc_ct_try_snk_run, - .exit = tc_ct_try_snk_exit, - .parent = &tc_states[TC_HOST_RP3_CT_RD], - }, - [TC_CT_ATTACH_WAIT_UNSUPPORTED] = { - .entry = tc_ct_attach_wait_unsupported_entry, - .run = tc_ct_attach_wait_unsupported_run, - .exit = tc_ct_attach_wait_unsupported_exit, - .parent = &tc_states[TC_HOST_RP3_CT_RPU], - }, - [TC_CT_ATTACHED_UNSUPPORTED] = { - .entry = tc_ct_attached_unsupported_entry, - .run = tc_ct_attached_unsupported_run, - .exit = tc_ct_attached_unsupported_exit, - .parent = &tc_states[TC_HOST_RP3_CT_RPU], - }, - [TC_CT_UNATTACHED_UNSUPPORTED] = { - .entry = tc_ct_unattached_unsupported_entry, - .run = tc_ct_unattached_unsupported_run, - .exit = tc_ct_unattached_unsupported_exit, - .parent = &tc_states[TC_HOST_RP3_CT_RPU], - }, - [TC_CT_UNATTACHED_VPD] = { - .entry = tc_ct_unattached_vpd_entry, - .run = tc_ct_unattached_vpd_run, - .exit = tc_ct_unattached_vpd_exit, - .parent = &tc_states[TC_HOST_RP3_CT_RD], - }, - [TC_CT_DISABLED_VPD] = { - .entry = tc_ct_disabled_vpd_entry, - .run = tc_ct_disabled_vpd_run, - .parent = &tc_states[TC_HOST_OPEN_CT_OPEN], - }, - [TC_CT_ATTACHED_VPD] = { - .entry = tc_ct_attached_vpd_entry, - .run = tc_ct_attached_vpd_run, - }, - [TC_CT_ATTACH_WAIT_VPD] = { - .entry = tc_ct_attach_wait_vpd_entry, - .run = tc_ct_attach_wait_vpd_run, - .exit = tc_ct_attach_wait_vpd_exit, - .parent = &tc_states[TC_HOST_RP3_CT_RD], - }, -}; - -#ifdef TEST_BUILD -const struct test_sm_data test_tc_sm_data[] = { - { - .base = tc_states, - .size = ARRAY_SIZE(tc_states), - .names = tc_state_names, - .names_size = ARRAY_SIZE(tc_state_names), - }, -}; -const int test_tc_sm_data_size = ARRAY_SIZE(test_tc_sm_data); -#endif diff --git a/common/usbc/usb_tc_drp_acc_trysrc_sm.c b/common/usbc/usb_tc_drp_acc_trysrc_sm.c deleted file mode 100644 index 6aa39b520b..0000000000 --- a/common/usbc/usb_tc_drp_acc_trysrc_sm.c +++ /dev/null @@ -1,3547 +0,0 @@ -/* Copyright 2019 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 "charge_manager.h" -#include "charge_state.h" -#include "common.h" -#include "console.h" -#include "hooks.h" -#include "system.h" -#include "task.h" -#include "tcpm.h" -#include "usb_common.h" -#include "usb_mux.h" -#include "usb_pd.h" -#include "usb_pe_sm.h" -#include "usb_prl_sm.h" -#include "usb_sm.h" -#include "usb_tc_sm.h" -#include "usbc_ppc.h" - -/* - * USB Type-C DRP with Accessory and Try.SRC module - * See Figure 4-16 in Release 1.4 of USB Type-C Spec. - */ - -#ifdef CONFIG_COMMON_RUNTIME -#define CPRINTF(format, args...) cprintf(CC_HOOK, format, ## args) -#define CPRINTS(format, args...) cprints(CC_HOOK, format, ## args) -#else /* CONFIG_COMMON_RUNTIME */ -#define CPRINTF(format, args...) -#define CPRINTS(format, args...) -#endif - -/* Type-C Layer Flags */ -/* Flag to note we are sourcing VCONN */ -#define TC_FLAGS_VCONN_ON BIT(0) -/* Flag to note port partner has Rp/Rp or Rd/Rd */ -#define TC_FLAGS_TS_DTS_PARTNER BIT(1) -/* Flag to note VBus input has never been low */ -#define TC_FLAGS_VBUS_NEVER_LOW BIT(2) -/* Flag to note Low Power Mode transition is currently happening */ -#define TC_FLAGS_LPM_TRANSITION BIT(3) -/* Flag to note Low Power Mode is currently on */ -#define TC_FLAGS_LPM_ENGAGED BIT(4) -/* Flag to note Low Power Mode is requested. Not currently used */ -#define TC_FLAGS_LPM_REQUESTED BIT(5) -/* Flag to note CVTPD has been detected */ -#define TC_FLAGS_CTVPD_DETECTED BIT(6) -/* Flag to note request to swap to VCONN on */ -#define TC_FLAGS_REQUEST_VC_SWAP_ON BIT(7) -/* Flag to note request to swap to VCONN off */ -#define TC_FLAGS_REQUEST_VC_SWAP_OFF BIT(8) -/* Flag to note request to swap VCONN is being rejected */ -#define TC_FLAGS_REJECT_VCONN_SWAP BIT(9) -/* Flag to note request to power role swap */ -#define TC_FLAGS_REQUEST_PR_SWAP BIT(10) -/* Flag to note request to data role swap */ -#define TC_FLAGS_REQUEST_DR_SWAP BIT(11) -/* Flag to note request to power off sink */ -#define TC_FLAGS_POWER_OFF_SNK BIT(12) -/* Flag to note port partner has external power */ -#define TC_FLAGS_PARTNER_EXTPOWER BIT(13) -/* Flag to note port partner is Dual Role Data */ -#define TC_FLAGS_PARTNER_DR_DATA BIT(14) -/* Flag to note port partner is Dual Role Power */ -#define TC_FLAGS_PARTNER_DR_POWER BIT(15) -/* Flag to note port partner is Power Delivery capable */ -#define TC_FLAGS_PARTNER_PD_CAPABLE BIT(16) -/* Flag to note hard reset has been triggered */ -#define TC_FLAGS_HARD_RESET BIT(17) -/* Flag to note port partner is USB comms capable */ -#define TC_FLAGS_PARTNER_USB_COMM BIT(18) -/* Flag to note we are currently performing PR Swap */ -#define TC_FLAGS_PR_SWAP_IN_PROGRESS BIT(19) -/* Flag to note we need to perform PR Swap */ -#define TC_FLAGS_DO_PR_SWAP BIT(20) -/* Flag to note we are performing Discover Identity */ -#define TC_FLAGS_DISC_IDENT_IN_PROGRESS BIT(21) -/* Flag to note we should wake from LPM */ -#define TC_FLAGS_WAKE_FROM_LPM BIT(22) -/* Flag to note a chipset power state has changed */ -#define TC_FLAGS_POWER_STATE_CHANGE BIT(23) -/* Flag to note the TCPM supports auto toggle */ -#define TC_FLAGS_AUTO_TOGGLE_SUPPORTED BIT(24) - -/* - * Clear all flags except TC_FLAGS_AUTO_TOGGLE_SUPPORTED, - * TC_FLAGS_LPM_REQUESTED, and TC_FLAGS_LPM_ENGAGED if - * they are set. - */ -#define CLR_ALL_BUT_LPM_FLAGS(port) (tc[port].flags &= \ - (TC_FLAGS_AUTO_TOGGLE_SUPPORTED | \ - TC_FLAGS_LPM_REQUESTED | \ - TC_FLAGS_LPM_ENGAGED)) - -/* 100 ms is enough time for any TCPC transaction to complete. */ -#define PD_LPM_DEBOUNCE_US (100 * MSEC) - -enum ps_reset_sequence { - PS_STATE0, - PS_STATE1, - PS_STATE2, - PS_STATE3 -}; - -/* List of all TypeC-level states */ -enum usb_tc_state { - /* Normal States */ - TC_DISABLED, - TC_ERROR_RECOVERY, - TC_UNATTACHED_SNK, - TC_ATTACH_WAIT_SNK, - TC_ATTACHED_SNK, - TC_UNORIENTED_DBG_ACC_SRC, - TC_DBG_ACC_SNK, - TC_UNATTACHED_SRC, - TC_ATTACH_WAIT_SRC, - TC_ATTACHED_SRC, - TC_TRY_SRC, - TC_TRY_WAIT_SNK, -#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE - TC_DRP_AUTO_TOGGLE, -#endif -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER - TC_LOW_POWER_MODE, -#endif -#ifdef CONFIG_USB_PE_SM - TC_CT_UNATTACHED_SNK, - TC_CT_ATTACHED_SNK, -#endif - /* Super States */ - TC_CC_OPEN, - TC_CC_RD, - TC_CC_RP, - TC_UNATTACHED, -}; -/* Forward declare the full list of states. This is indexed by usb_tc_state */ -static const struct usb_state tc_states[]; - -#ifdef CONFIG_COMMON_RUNTIME -/* List of human readable state names for console debugging */ -static const char * const tc_state_names[] = { - [TC_DISABLED] = "Disabled", - [TC_ERROR_RECOVERY] = "ErrorRecovery", - [TC_UNATTACHED_SNK] = "Unattached.SNK", - [TC_ATTACH_WAIT_SNK] = "AttachWait.SNK", - [TC_ATTACHED_SNK] = "Attached.SNK", - [TC_UNORIENTED_DBG_ACC_SRC] = "UnorientedDebugAccessory.SRC", - [TC_DBG_ACC_SNK] = "DebugAccessory.SNK", - [TC_UNATTACHED_SRC] = "Unattached.SRC", - [TC_ATTACH_WAIT_SRC] = "AttachWait.SRC", - [TC_ATTACHED_SRC] = "Attached.SRC", - [TC_TRY_SRC] = "Try.SRC", - [TC_TRY_WAIT_SNK] = "TryWait.SNK", -#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE - [TC_DRP_AUTO_TOGGLE] = "DRPAutoToggle", -#endif -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER - [TC_LOW_POWER_MODE] = "LowPowerMode", -#endif -#ifdef CONFIG_USB_PE_SM - [TC_CT_UNATTACHED_SNK] = "CTUnattached.SNK", - [TC_CT_ATTACHED_SNK] = "CTAttached.SNK", -#endif - -}; -#endif - -/* Generate a compiler error if invalid states are referenced */ -#ifndef CONFIG_USB_PD_TRY_SRC -#define TC_TRY_SRC TC_TRY_SRC_UNDEFINED -#define TC_TRY_WAIT_SNK TC_TRY_WAIT_SNK_UNDEFINED -#endif - -static struct type_c { - /* state machine context */ - struct sm_ctx ctx; - /* current port power role (SOURCE or SINK) */ - enum pd_power_role power_role; - /* current port data role (DFP or UFP) */ - enum pd_data_role data_role; - /* Higher-level power deliver state machines are enabled if true. */ - uint8_t pd_enable; - /* - * Timer for handling TOGGLE_OFF/FORCE_SINK mode when auto-toggle - * enabled. See drp_auto_toggle_next_state() for details. - */ - uint64_t drp_sink_time; -#ifdef CONFIG_USB_PE_SM - /* Power supply reset sequence during a hard reset */ - enum ps_reset_sequence ps_reset_state; -#endif - /* Port polarity : 0 => CC1 is CC line, 1 => CC2 is CC line */ - uint8_t polarity; - /* port flags, see TC_FLAGS_* */ - uint32_t flags; - /* event timeout */ - uint64_t evt_timeout; - /* Time a port shall wait before it can determine it is attached */ - uint64_t cc_debounce; - /* - * Time a Sink port shall wait before it can determine it is detached - * due to the potential for USB PD signaling on CC as described in - * the state definitions. - */ - uint64_t pd_debounce; -#ifdef CONFIG_USB_PD_TRY_SRC - /* - * Time a port shall wait before it can determine it is - * re-attached during the try-wait process. - */ - uint64_t try_wait_debounce; -#endif - /* The cc state */ - enum pd_cc_states cc_state; - /* Role toggle timer */ - uint64_t next_role_swap; - /* Generic timer */ - uint64_t timeout; - /* Time to enter low power mode */ - uint64_t low_power_time; - /* Tasks to notify after TCPC has been reset */ - int tasks_waiting_on_reset; - /* Tasks preventing TCPC from entering low power mode */ - int tasks_preventing_lpm; - /* Voltage on CC pin */ - enum tcpc_cc_voltage_status cc_voltage; - /* Type-C current */ - typec_current_t typec_curr; - /* Type-C current change */ - typec_current_t typec_curr_change; - /* Attached ChromeOS device id, RW hash, and current RO / RW image */ - uint16_t dev_id; - uint32_t dev_rw_hash[PD_RW_HASH_SIZE/4]; - enum ec_current_image current_image; -} tc[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/* Port dual-role state */ -static volatile __maybe_unused -enum pd_dual_role_states drp_state[CONFIG_USB_PD_PORT_MAX_COUNT] = { - [0 ... (CONFIG_USB_PD_PORT_MAX_COUNT - 1)] = - CONFIG_USB_PD_INITIAL_DRP_STATE}; - -#ifdef CONFIG_USBC_VCONN -static void set_vconn(int port, int enable); -#endif - -#ifdef CONFIG_USB_PE_SM - -#ifdef CONFIG_USB_PD_ALT_MODE_DFP -/* Tracker for which task is waiting on sysjump prep to finish */ -static volatile task_id_t sysjump_task_waiting = TASK_ID_INVALID; -#endif - -/* - * 4 entry rw_hash table of type-C devices that AP has firmware updates for. - */ -#ifdef CONFIG_COMMON_RUNTIME -#define RW_HASH_ENTRIES 4 -static struct ec_params_usb_pd_rw_hash_entry rw_hash_table[RW_HASH_ENTRIES]; -#endif - -/* Forward declare common, private functions */ -static __maybe_unused int reset_device_and_notify(int port); - -#ifdef CONFIG_POWER_COMMON -static void handle_new_power_state(int port); -#endif /* CONFIG_POWER_COMMON */ - -static void pd_update_dual_role_config(int port); -#endif /* CONFIG_USB_PE_SM */ - -/* Forward declare common, private functions */ -static void set_state_tc(const int port, const enum usb_tc_state new_state); -test_export_static enum usb_tc_state get_state_tc(const int port); - -#ifdef CONFIG_USB_PD_TRY_SRC -/* Enable variable for Try.SRC states */ -static uint8_t pd_try_src_enable; -static void pd_update_try_source(void); -#endif - -static void sink_stop_drawing_current(int port); - -/* - * Public Functions - * - * NOTE: Functions prefixed with pd_ are defined in usb_pd.h - * Functions prefixed with tc_ are defined int usb_tc_sm.h - */ - -#ifndef CONFIG_USB_PRL_SM - -/* - * These pd_ functions are implemented in common/usb_prl_sm.c - */ - -void pd_transmit_complete(int port, int status) -{ - /* DO NOTHING */ -} - -void pd_execute_hard_reset(int port) -{ - /* DO NOTHING */ -} - -void pd_set_vbus_discharge(int port, int enable) -{ - /* DO NOTHING */ -} - -uint16_t pd_get_identity_vid(int port) -{ - /* DO NOTHING */ - return 0; -} - -#endif /* !CONFIG_USB_PRL_SM */ - -void pd_update_contract(int port) -{ - if (IS_ENABLED(CONFIG_USB_PE_SM)) { - /* Must be in Attached.SRC when this function is called */ - if (get_state_tc(port) == TC_ATTACHED_SRC) - pe_dpm_request(port, DPM_REQUEST_SRC_CAP_CHANGE); - } -} - -void pd_request_source_voltage(int port, int mv) -{ - if (IS_ENABLED(CONFIG_USB_PE_SM)) { - pd_set_max_voltage(mv); - - /* Must be in Attached.SNK when this function is called */ - if (get_state_tc(port) == TC_ATTACHED_SNK) - pe_dpm_request(port, DPM_REQUEST_NEW_POWER_LEVEL); - else - TC_SET_FLAG(port, TC_FLAGS_REQUEST_PR_SWAP); - - task_wake(PD_PORT_TO_TASK_ID(port)); - } -} - -void pd_set_external_voltage_limit(int port, int mv) -{ - if (IS_ENABLED(CONFIG_USB_PE_SM)) { - pd_set_max_voltage(mv); - - /* Must be in Attached.SNK when this function is called */ - if (get_state_tc(port) == TC_ATTACHED_SNK) - pe_dpm_request(port, DPM_REQUEST_NEW_POWER_LEVEL); - - task_wake(PD_PORT_TO_TASK_ID(port)); - } -} - -void pd_set_new_power_request(int port) -{ - if (IS_ENABLED(CONFIG_USB_PE_SM)) { - /* Must be in Attached.SNK when this function is called */ - if (get_state_tc(port) == TC_ATTACHED_SNK) - pe_dpm_request(port, DPM_REQUEST_NEW_POWER_LEVEL); - } -} - -void pd_request_power_swap(int port) -{ - if (IS_ENABLED(CONFIG_USB_PE_SM)) { - /* - * Must be in Attached.SRC, Attached.SNK, UnorientedDbgAcc.SRC, - * or DbgAcc.SNK, when this function is called. - */ - if (get_state_tc(port) == TC_ATTACHED_SRC || - get_state_tc(port) == TC_ATTACHED_SNK || - get_state_tc(port) == TC_DBG_ACC_SNK || - get_state_tc(port) == - TC_UNORIENTED_DBG_ACC_SRC) { - TC_SET_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS); - } - } -} - -static inline void pd_set_dual_role_no_wakeup(int port, - enum pd_dual_role_states state) -{ - drp_state[port] = state; - - if (IS_ENABLED(CONFIG_USB_PD_TRY_SRC)) - pd_update_try_source(); -} - -void pd_set_dual_role(int port, enum pd_dual_role_states state) -{ - pd_set_dual_role_no_wakeup(port, state); - - /* Wake task up to process change */ - task_set_event(PD_PORT_TO_TASK_ID(port), - PD_EVENT_UPDATE_DUAL_ROLE, 0); -} - -#ifdef CONFIG_USB_PE_SM -int pd_get_partner_data_swap_capable(int port) -{ - /* return data swap capable status of port partner */ - return TC_CHK_FLAG(port, TC_FLAGS_PARTNER_DR_DATA); -} - -int pd_comm_is_enabled(int port) -{ - return tc[port].pd_enable; -} - -void pd_send_vdm(int port, uint32_t vid, int cmd, const uint32_t *data, - int count) -{ - pe_send_vdm(port, vid, cmd, data, count); -} - -void pd_request_data_swap(int port) -{ - /* - * Must be in Attached.SRC or Attached.SNK when this function - * is called - */ - if (get_state_tc(port) == TC_ATTACHED_SRC || - get_state_tc(port) == TC_ATTACHED_SNK) { - TC_SET_FLAG(port, TC_FLAGS_REQUEST_DR_SWAP); - task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SM, 0); - } -} - -/* - * Return true if partner port is a DTS or TS capable of entering debug - * mode (eg. is presenting Rp/Rp or Rd/Rd). - */ -int pd_ts_dts_plugged(int port) -{ - return TC_CHK_FLAG(port, TC_FLAGS_TS_DTS_PARTNER); -} - -/* Return true if partner port is known to be PD capable. */ -int pd_capable(int port) -{ - return TC_CHK_FLAG(port, TC_FLAGS_PARTNER_PD_CAPABLE); -} - -/* - * Return true if partner port is capable of communication over USB data - * lines. - */ -int pd_get_partner_usb_comm_capable(int port) -{ - return TC_CHK_FLAG(port, TC_FLAGS_PARTNER_USB_COMM); -} - -enum pd_dual_role_states pd_get_dual_role(int port) -{ - return drp_state[port]; -} - -int pd_dev_store_rw_hash(int port, uint16_t dev_id, uint32_t *rw_hash, - uint32_t current_image) -{ - int i; - - tc[port].dev_id = dev_id; - memcpy(tc[port].dev_rw_hash, rw_hash, PD_RW_HASH_SIZE); -#ifdef CONFIG_CMD_PD_DEV_DUMP_INFO - if (debug_level >= 2) - pd_dev_dump_info(dev_id, (uint8_t *)rw_hash); -#endif - tc[port].current_image = current_image; - - /* Search table for matching device / hash */ - for (i = 0; i < RW_HASH_ENTRIES; i++) - if (dev_id == rw_hash_table[i].dev_id) - return !memcmp(rw_hash, - rw_hash_table[i].dev_rw_hash, - PD_RW_HASH_SIZE); - return 0; -} - -void pd_got_frs_signal(int port) -{ - pe_got_frs_signal(port); -} - -int tc_is_attached_src(int port) -{ - return get_state_tc(port) == TC_ATTACHED_SRC; -} - -int tc_is_attached_snk(int port) -{ - return get_state_tc(port) == TC_ATTACHED_SNK; -} - -void tc_partner_dr_power(int port, int en) -{ - if (en) - TC_SET_FLAG(port, TC_FLAGS_PARTNER_DR_POWER); - else - TC_CLR_FLAG(port, TC_FLAGS_PARTNER_DR_POWER); -} - -void tc_partner_extpower(int port, int en) -{ - if (en) - TC_SET_FLAG(port, TC_FLAGS_PARTNER_EXTPOWER); - else - TC_CLR_FLAG(port, TC_FLAGS_PARTNER_EXTPOWER); -} - -void tc_partner_usb_comm(int port, int en) -{ - if (en) - TC_SET_FLAG(port, TC_FLAGS_PARTNER_USB_COMM); - else - TC_CLR_FLAG(port, TC_FLAGS_PARTNER_USB_COMM); -} - -void tc_partner_dr_data(int port, int en) -{ - if (en) - TC_SET_FLAG(port, TC_FLAGS_PARTNER_DR_DATA); - else - TC_CLR_FLAG(port, TC_FLAGS_PARTNER_DR_DATA); -} - -void tc_pd_connection(int port, int en) -{ - if (en) - TC_SET_FLAG(port, TC_FLAGS_PARTNER_PD_CAPABLE); - else - TC_CLR_FLAG(port, TC_FLAGS_PARTNER_PD_CAPABLE); -} - -void tc_ctvpd_detected(int port) -{ - TC_SET_FLAG(port, TC_FLAGS_CTVPD_DETECTED); -} - -void tc_vconn_on(int port) -{ - set_vconn(port, 1); -} - -int tc_check_vconn_swap(int port) -{ -#ifdef CONFIG_USBC_VCONN - if (TC_CHK_FLAG(port, TC_FLAGS_REJECT_VCONN_SWAP)) - return 0; - - return pd_check_vconn_swap(port); -#else - return 0; -#endif -} - -void tc_pr_swap_complete(int port) -{ - TC_CLR_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS); -} - -void tc_prs_src_snk_assert_rd(int port) -{ - /* Must be in Attached.SRC when this function is called */ - if (get_state_tc(port) == TC_ATTACHED_SRC) { - /* Transition to Attached.SNK to assert Rd */ - TC_SET_FLAG(port, TC_FLAGS_DO_PR_SWAP); - task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SM, 0); - } -} - -void tc_prs_snk_src_assert_rp(int port) -{ - /* Must be in Attached.SNK when this function is called */ - if (get_state_tc(port) == TC_ATTACHED_SNK) { - /* Transition to Attached.SRC to assert Rp */ - TC_SET_FLAG(port, TC_FLAGS_DO_PR_SWAP); - task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SM, 0); - } -} - -void tc_hard_reset(int port) -{ - TC_SET_FLAG(port, TC_FLAGS_HARD_RESET); - task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SM, 0); -} - -void tc_disc_ident_in_progress(int port) -{ - TC_SET_FLAG(port, TC_FLAGS_DISC_IDENT_IN_PROGRESS); -} - -void tc_disc_ident_complete(int port) -{ - TC_CLR_FLAG(port, TC_FLAGS_DISC_IDENT_IN_PROGRESS); -} -#endif /* CONFIG_USB_PE_SM */ - -void tc_snk_power_off(int port) -{ - if (get_state_tc(port) == TC_ATTACHED_SNK || - get_state_tc(port) == TC_DBG_ACC_SNK) { - TC_SET_FLAG(port, TC_FLAGS_POWER_OFF_SNK); - sink_stop_drawing_current(port); - } -} - -int tc_src_power_on(int port) -{ - if (get_state_tc(port) == TC_ATTACHED_SRC) - return pd_set_power_supply_ready(port); - - return 0; -} - -void tc_src_power_off(int port) -{ - if (get_state_tc(port) == TC_ATTACHED_SRC || - get_state_tc(port) == TC_UNORIENTED_DBG_ACC_SRC) { - /* Remove VBUS */ - pd_power_supply_reset(port); - - if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) - charge_manager_set_ceil(port, CEIL_REQUESTOR_PD, - CHARGE_CEIL_NONE); - } -} - -void pd_set_suspend(int port, int enable) -{ - if (pd_is_port_enabled(port) == !enable) - return; - - set_state_tc(port, - enable ? TC_DISABLED : TC_UNATTACHED_SNK); -} - -int pd_is_port_enabled(int port) -{ - return get_state_tc(port) != TC_DISABLED; -} - -int pd_fetch_acc_log_entry(int port) -{ - if (IS_ENABLED(CONFIG_USB_PE_SM)) - pd_send_vdm(port, USB_VID_GOOGLE, VDO_CMD_GET_LOG, NULL, 0); - - return EC_RES_SUCCESS; -} - -int pd_get_polarity(int port) -{ - return tc[port].polarity; -} - -int pd_get_role(int port) -{ - return tc[port].data_role; -} - -int pd_is_vbus_present(int port) -{ - if (IS_ENABLED(CONFIG_USB_PD_VBUS_DETECT_TCPC)) - return tcpm_get_vbus_level(port); - else - return pd_snk_is_vbus_provided(port); -} - -void pd_vbus_low(int port) -{ - TC_CLR_FLAG(port, TC_FLAGS_VBUS_NEVER_LOW); -} - -int pd_is_connected(int port) -{ - return (get_state_tc(port) == TC_ATTACHED_SNK) || - (get_state_tc(port) == TC_ATTACHED_SRC); -} - -#ifdef CONFIG_USB_PD_ALT_MODE_DFP -/* - * TODO(b/137493121): Move this function to a separate file that's shared - * between the this and the original stack. - */ -void pd_prepare_sysjump(void) -{ - if (IS_ENABLED(CONFIG_USB_PE_SM)) { - int i; - - /* - * Exit modes before sysjump so we can cleanly enter again - * later - */ - for (i = 0; i < board_get_usb_pd_port_count(); i++) { - /* - * We can't be in an alternate mode if PD comm is - * disabled, so no need to send the event - */ - if (!pd_comm_is_enabled(i)) - continue; - - sysjump_task_waiting = task_get_current(); - task_set_event(PD_PORT_TO_TASK_ID(i), - PD_EVENT_SYSJUMP, 0); - task_wait_event_mask(TASK_EVENT_SYSJUMP_READY, -1); - sysjump_task_waiting = TASK_ID_INVALID; - } - } -} -#endif - -#ifdef CONFIG_USB_PE_SM -static void tc_perform_src_hard_reset(int port) -{ - switch (tc[port].ps_reset_state) { - case PS_STATE0: - /* Remove VBUS */ - tc_src_power_off(port); - - /* Turn off VCONN */ - set_vconn(port, 0); - - /* Set role to DFP */ - tc_set_data_role(port, PD_ROLE_DFP); - - tc[port].ps_reset_state = PS_STATE1; - tc[port].timeout = get_time().val + PD_T_SRC_RECOVER; - return; - case PS_STATE1: - /* Enable VBUS */ - pd_set_power_supply_ready(port); - - /* Turn off VCONN */ - set_vconn(port, 1); - - tc[port].ps_reset_state = PS_STATE3; - tc[port].timeout = get_time().val + - PD_POWER_SUPPLY_TURN_ON_DELAY; - return; - case PS_STATE2: - case PS_STATE3: - /* Tell Policy Engine Hard Reset is complete */ - pe_ps_reset_complete(port); - - TC_CLR_FLAG(port, TC_FLAGS_HARD_RESET); - tc[port].ps_reset_state = PS_STATE0; - return; - } -} -#endif - -static void tc_perform_snk_hard_reset(int port) -{ - tc_set_data_role(port, PD_ROLE_UFP); - - /* Clear the input current limit */ - sink_stop_drawing_current(port); - - /* - * When VCONN is supported, the Hard Reset Shall cause - * the Port with the Rd resistor asserted to turn off - * VCONN. - */ -#ifdef CONFIG_USBC_VCONN - if (TC_CHK_FLAG(port, TC_FLAGS_VCONN_ON)) - set_vconn(port, 0); -#endif - - /* - * Inform policy engine that power supply - * reset is complete - */ - pe_ps_reset_complete(port); -} - -void tc_start_error_recovery(int port) -{ - /* - * Async. function call: - * The port should transition to the ErrorRecovery state - * from any other state when directed. - */ - set_state_tc(port, TC_ERROR_RECOVERY); -} - -static void restart_tc_sm(int port, enum usb_tc_state start_state) -{ - int res = 0; - - res = tc_restart_tcpc(port); - - CPRINTS("TCPC p%d init %s", port, res ? "failed" : "ready"); - - /* Disable if restart failed, otherwise start in default state. */ - set_state_tc(port, res ? TC_DISABLED : start_state); - - if (IS_ENABLED(CONFIG_USBC_SS_MUX)) - /* Initialize USB mux to its default state */ - usb_mux_init(port); - - tcpm_select_rp_value(port, CONFIG_USB_PD_PULLUP); - - if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) { - /* Initialize PD and type-C supplier current limits to 0 */ - pd_set_input_current_limit(port, 0, 0); - typec_set_input_current_limit(port, 0, 0); - charge_manager_update_dualrole(port, CAP_UNKNOWN); - } - - tc[port].flags = 0; - -#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE - /* - * Some TCPCs may not support DRP Auto Toggle, so query the - * query the TCPC for DRP Auto toggle support. - */ - if (tcpm_auto_toggle_supported(port)) - TC_SET_FLAG(port, TC_FLAGS_AUTO_TOGGLE_SUPPORTED); -#endif - -#ifdef CONFIG_USB_PE_SM - tc[port].pd_enable = 0; - tc[port].ps_reset_state = PS_STATE0; -#endif -} - -void tc_state_init(int port) -{ - /* Unattached.SNK is the default starting state. */ - restart_tc_sm(port, TC_UNATTACHED_SNK); - - /* - * If the TCPC isn't accessed, it will enter low power mode - * after PD_LPM_DEBOUNCE_US. - */ - tc[port].low_power_time = get_time().val + PD_LPM_DEBOUNCE_US; -} - -enum pd_power_role tc_get_power_role(int port) -{ - return tc[port].power_role; -} - -enum pd_data_role tc_get_data_role(int port) -{ - return tc[port].data_role; -} - -enum pd_cable_plug tc_get_cable_plug(int port) -{ - /* - * Messages sent by this state machine are always from a DFP/UFP, - * i.e. the chromebook. - */ - return PD_PLUG_FROM_DFP_UFP; -} - -uint8_t tc_get_polarity(int port) -{ - return tc[port].polarity; -} - -uint8_t tc_get_pd_enabled(int port) -{ - return tc[port].pd_enable; -} - -void tc_set_power_role(int port, enum pd_power_role role) -{ - tc[port].power_role = role; -} - -/* - * Private Functions - */ - -/* Set the TypeC state machine to a new state. */ -static void set_state_tc(const int port, const enum usb_tc_state new_state) -{ - set_state(port, &tc[port].ctx, &tc_states[new_state]); -} - -/* Get the current TypeC state. */ -test_export_static enum usb_tc_state get_state_tc(const int port) -{ - return tc[port].ctx.current - &tc_states[0]; -} - -/* Get the previous TypeC state. */ -static enum usb_tc_state get_last_state_tc(const int port) -{ - return tc[port].ctx.previous - &tc_states[0]; -} - -static void print_current_state(const int port) -{ - CPRINTS("C%d: %s", port, tc_state_names[get_state_tc(port)]); -} - -#ifdef CONFIG_USB_PE_SM -static void handle_device_access(int port) -{ - tc[port].low_power_time = get_time().val + PD_LPM_DEBOUNCE_US; -} -#endif - -void tc_event_check(int port, int evt) -{ -#ifdef CONFIG_USB_PE_SM - if (IS_ENABLED(CONFIG_USB_PD_TCPC_LOW_POWER)) { - if (evt & PD_EXIT_LOW_POWER_EVENT_MASK) - TC_SET_FLAG(port, TC_FLAGS_WAKE_FROM_LPM); - if (evt & PD_EVENT_DEVICE_ACCESSED) - handle_device_access(port); - } - - /* if TCPC has reset, then need to initialize it again */ - if (evt & PD_EVENT_TCPC_RESET) - reset_device_and_notify(port); - -#ifdef CONFIG_POWER_COMMON - if (IS_ENABLED(CONFIG_POWER_COMMON)) { - if (evt & PD_EVENT_POWER_STATE_CHANGE) { - TC_SET_FLAG(port, TC_FLAGS_POWER_STATE_CHANGE); - handle_new_power_state(port); - } - } -#endif /* CONFIG_POWER_COMMON */ -#ifdef CONFIG_USB_PD_ALT_MODE_DFP - if (IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) { - if (evt & PD_EVENT_SYSJUMP) { - pe_exit_dp_mode(port); - notify_sysjump_ready(&sysjump_task_waiting); - } - } -#endif - - if (evt & PD_EVENT_UPDATE_DUAL_ROLE) - pd_update_dual_role_config(port); -#endif -} - -/* - * CC values for regular sources and Debug sources (aka DTS) - * - * Source type Mode of Operation CC1 CC2 - * --------------------------------------------- - * Regular Default USB Power RpUSB Open - * Regular USB-C @ 1.5 A Rp1A5 Open - * Regular USB-C @ 3 A Rp3A0 Open - * DTS Default USB Power Rp3A0 Rp1A5 - * DTS USB-C @ 1.5 A Rp1A5 RpUSB - * DTS USB-C @ 3 A Rp3A0 RpUSB - */ - -void tc_set_data_role(int port, enum pd_data_role role) -{ - tc[port].data_role = role; - - if (IS_ENABLED(CONFIG_USBC_SS_MUX)) - set_usb_mux_with_current_data_role(port); - - /* - * Run any board-specific code for role swap (e.g. setting OTG signals - * to SoC). - */ - pd_execute_data_swap(port, role); - - /* Notify TCPC of role update */ - tcpm_set_msg_header(port, tc[port].power_role, tc[port].data_role); -} - -static void sink_stop_drawing_current(int port) -{ - pd_set_input_current_limit(port, 0, 0); - - if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) { - typec_set_input_current_limit(port, 0, 0); - charge_manager_set_ceil(port, - CEIL_REQUESTOR_PD, CHARGE_CEIL_NONE); - } -} - -#ifdef CONFIG_USB_PD_TRY_SRC -static void pd_update_try_source(void) -{ - int i; - int try_src = 0; - static struct mutex pd_try_src_enable_lock; - - int batt_soc = usb_get_battery_soc(); - - try_src = 0; - for (i = 0; i < board_get_usb_pd_port_count(); i++) - try_src |= drp_state[i] == PD_DRP_TOGGLE_ON; - - /* - * This function is called from this PD task and the hooks tasks. - * A lock is added here to serialize access to the - * pd_try_source_enable variable. - */ - mutex_lock(&pd_try_src_enable_lock); - - /* - * Enable try source when dual-role toggling AND battery is present - * and at some minimum percentage. - */ - pd_try_src_enable = try_src && - batt_soc >= CONFIG_USB_PD_TRY_SRC_MIN_BATT_SOC; - -#ifdef CONFIG_BATTERY_REVIVE_DISCONNECT - /* - * Don't attempt Try.Src if the battery is in the disconnect state. The - * discharge FET may not be enabled and so attempting Try.Src may cut - * off our only power source at the time. - */ - pd_try_src_enable &= (battery_get_disconnect_state() == - BATTERY_NOT_DISCONNECTED); -#elif defined(CONFIG_BATTERY_PRESENT_CUSTOM) || \ - defined(CONFIG_BATTERY_PRESENT_GPIO) - /* - * When battery is cutoff in ship mode it may not be reliable to - * check if battery is present with its state of charge. - * Also check if battery is initialized and ready to provide power. - */ - pd_try_src_enable &= (battery_is_present() == BP_YES); -#endif /* CONFIG_BATTERY_PRESENT_[CUSTOM|GPIO] */ - - mutex_unlock(&pd_try_src_enable_lock); -} -DECLARE_HOOK(HOOK_BATTERY_SOC_CHANGE, pd_update_try_source, HOOK_PRIO_DEFAULT); -#endif /* CONFIG_USB_PD_TRY_SRC */ - -#ifdef CONFIG_CMD_PD_DEV_DUMP_INFO -static inline void pd_dev_dump_info(uint16_t dev_id, uint8_t *hash) -{ - int j; - - ccprintf("DevId:%d.%d Hash:", HW_DEV_ID_MAJ(dev_id), - HW_DEV_ID_MIN(dev_id)); - for (j = 0; j < PD_RW_HASH_SIZE; j += 4) { - ccprintf(" 0x%02x%02x%02x%02x", hash[j + 3], hash[j + 2], - hash[j + 1], hash[j]); - } - ccprintf("\n"); -} -#endif /* CONFIG_CMD_PD_DEV_DUMP_INFO */ - -static void set_vconn(int port, int enable) -{ - if (enable == TC_CHK_FLAG(port, TC_FLAGS_VCONN_ON)) - return; - - if (enable) - TC_SET_FLAG(port, TC_FLAGS_VCONN_ON); - else - TC_CLR_FLAG(port, TC_FLAGS_VCONN_ON); - - /* - * We always need to tell the TCPC to enable Vconn first, otherwise some - * TCPCs get confused and think the CC line is in over voltage mode and - * immediately disconnects. If there is a PPC, both devices will - * potentially source Vconn, but that should be okay since Vconn has - * "make before break" electrical requirements when swapping anyway. - */ - tcpm_set_vconn(port, enable); - - if (IS_ENABLED(CONFIG_USBC_PPC_VCONN)) - ppc_set_vconn(port, enable); -} - -#ifdef CONFIG_USB_PD_TCPM_TCPCI -static uint32_t pd_ports_to_resume; -static void resume_pd_port(void) -{ - uint32_t port; - uint32_t suspended_ports = atomic_read_clear(&pd_ports_to_resume); - - while (suspended_ports) { - port = __builtin_ctz(suspended_ports); - suspended_ports &= ~(1 << port); - pd_set_suspend(port, 0); - } -} -DECLARE_DEFERRED(resume_pd_port); - -void pd_deferred_resume(int port) -{ - atomic_or(&pd_ports_to_resume, 1 << port); - hook_call_deferred(&resume_pd_port_data, SECOND); -} -#endif /* CONFIG_USB_PD_DEFERRED_RESUME */ - -#ifdef CONFIG_USB_PE_SM -/* This must only be called from the PD task */ -static void pd_update_dual_role_config(int port) -{ - /* - * Change to sink if port is currently a source AND (new DRP - * state is force sink OR new DRP state is either toggle off - * or debug accessory toggle only and we are in the source - * disconnected state). - */ - if (!IS_ENABLED(CONFIG_USB_PE_SM)) - return; - - if (tc[port].power_role == PD_ROLE_SOURCE && - ((drp_state[port] == PD_DRP_FORCE_SINK && - !pd_ts_dts_plugged(port)) || - (drp_state[port] == PD_DRP_TOGGLE_OFF && - get_state_tc(port) == TC_UNATTACHED_SRC))) { - set_state_tc(port, TC_UNATTACHED_SNK); - } else if (tc[port].power_role == PD_ROLE_SINK && - drp_state[port] == PD_DRP_FORCE_SOURCE) { - /* - * Change to source if port is currently a sink and the - * new DRP state is force source. - */ - set_state_tc(port, TC_UNATTACHED_SRC); - } -} - -#ifdef CONFIG_POWER_COMMON -static void handle_new_power_state(int port) -{ - if (IS_ENABLED(CONFIG_POWER_COMMON) && - IS_ENABLED(CONFIG_USB_PE_SM)) { - if (chipset_in_or_transitioning_to_state(CHIPSET_STATE_ANY_OFF)) - /* - * The SoC will negotiated DP mode again when it - * boots up - */ - pe_exit_dp_mode(port); - - /* Ensure mux is set properly after chipset transition */ - set_usb_mux_with_current_data_role(port); - } -} -#endif /* CONFIG_POWER_COMMON */ - -/* - * HOST COMMANDS - */ -#ifdef HAS_TASK_HOSTCMD -static enum ec_status hc_pd_ports(struct host_cmd_handler_args *args) -{ - struct ec_response_usb_pd_ports *r = args->response; - - r->num_ports = board_get_usb_pd_port_count(); - args->response_size = sizeof(*r); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_USB_PD_PORTS, - hc_pd_ports, - EC_VER_MASK(0)); -static const enum pd_dual_role_states dual_role_map[USB_PD_CTRL_ROLE_COUNT] = { - [USB_PD_CTRL_ROLE_TOGGLE_ON] = PD_DRP_TOGGLE_ON, - [USB_PD_CTRL_ROLE_TOGGLE_OFF] = PD_DRP_TOGGLE_OFF, - [USB_PD_CTRL_ROLE_FORCE_SINK] = PD_DRP_FORCE_SINK, - [USB_PD_CTRL_ROLE_FORCE_SOURCE] = PD_DRP_FORCE_SOURCE, - [USB_PD_CTRL_ROLE_FREEZE] = PD_DRP_FREEZE, -}; - -#ifdef CONFIG_USBC_SS_MUX -static const enum typec_mux typec_mux_map[USB_PD_CTRL_MUX_COUNT] = { - [USB_PD_CTRL_MUX_NONE] = TYPEC_MUX_NONE, - [USB_PD_CTRL_MUX_USB] = TYPEC_MUX_USB, - [USB_PD_CTRL_MUX_AUTO] = TYPEC_MUX_DP, - [USB_PD_CTRL_MUX_DP] = TYPEC_MUX_DP, - [USB_PD_CTRL_MUX_DOCK] = TYPEC_MUX_DOCK, -}; -#endif - -__overridable uint8_t board_get_dp_pin_mode(int port) -{ - return 0; -} - -/* - * TODO(b/142911453): Move this function to a common/usb_common.c to avoid - * duplicate code - */ -static enum ec_status hc_usb_pd_control(struct host_cmd_handler_args *args) -{ - const struct ec_params_usb_pd_control *p = args->params; - struct ec_response_usb_pd_control_v2 *r_v2 = args->response; - struct ec_response_usb_pd_control *r = args->response; - - if (p->port >= board_get_usb_pd_port_count()) - return EC_RES_INVALID_PARAM; - - if (p->role >= USB_PD_CTRL_ROLE_COUNT || - p->mux >= USB_PD_CTRL_MUX_COUNT) - return EC_RES_INVALID_PARAM; - - if (p->role != USB_PD_CTRL_ROLE_NO_CHANGE) - pd_set_dual_role(p->port, dual_role_map[p->role]); - -#ifdef CONFIG_USBC_SS_MUX - if (p->mux != USB_PD_CTRL_MUX_NO_CHANGE) - usb_mux_set(p->port, typec_mux_map[p->mux], - typec_mux_map[p->mux] == TYPEC_MUX_NONE ? - USB_SWITCH_DISCONNECT : - USB_SWITCH_CONNECT, - pd_get_polarity(p->port)); -#endif /* CONFIG_USBC_SS_MUX */ - - if (p->swap == USB_PD_CTRL_SWAP_DATA) - pd_request_data_swap(p->port); - else if (p->swap == USB_PD_CTRL_SWAP_POWER) - pd_request_power_swap(p->port); -#ifdef CONFIG_USBC_VCONN_SWAP - else if (p->swap == USB_PD_CTRL_SWAP_VCONN) - pe_dpm_request(p->port, DPM_REQUEST_VCONN_SWAP); -#endif - - switch (args->version) { - case 0: - r->enabled = pd_comm_is_enabled(p->port); - r->role = tc[p->port].power_role; - r->polarity = tc[p->port].polarity; - r->state = get_state_tc(p->port); - args->response_size = sizeof(*r); - break; - case 1: - case 2: - if (sizeof(*r_v2) > args->response_max) - return EC_RES_INVALID_PARAM; - - r_v2->enabled = - (pd_comm_is_enabled(p->port) ? - PD_CTRL_RESP_ENABLED_COMMS : 0) | - (pd_is_connected(p->port) ? - PD_CTRL_RESP_ENABLED_CONNECTED : 0) | - (TC_CHK_FLAG(p->port, TC_FLAGS_PARTNER_PD_CAPABLE) ? - PD_CTRL_RESP_ENABLED_PD_CAPABLE : 0); - r_v2->role = - (tc[p->port].power_role ? PD_CTRL_RESP_ROLE_POWER : 0) | - (tc[p->port].data_role ? PD_CTRL_RESP_ROLE_DATA : 0) | - (TC_CHK_FLAG(p->port, TC_FLAGS_VCONN_ON) ? - PD_CTRL_RESP_ROLE_VCONN : 0) | - (TC_CHK_FLAG(p->port, TC_FLAGS_PARTNER_DR_POWER) ? - PD_CTRL_RESP_ROLE_DR_POWER : 0) | - (TC_CHK_FLAG(p->port, TC_FLAGS_PARTNER_DR_DATA) ? - PD_CTRL_RESP_ROLE_DR_DATA : 0) | - (TC_CHK_FLAG(p->port, TC_FLAGS_PARTNER_USB_COMM) ? - PD_CTRL_RESP_ROLE_USB_COMM : 0) | - (TC_CHK_FLAG(p->port, TC_FLAGS_PARTNER_EXTPOWER) ? - PD_CTRL_RESP_ROLE_EXT_POWERED : 0); - r_v2->polarity = tc[p->port].polarity; - r_v2->cc_state = tc[p->port].cc_state; - r_v2->dp_mode = board_get_dp_pin_mode(p->port); - r_v2->cable_type = get_usb_pd_mux_cable_type(p->port); - - strzcpy(r_v2->state, tc_state_names[get_state_tc(p->port)], - sizeof(r_v2->state)); - if (args->version == 1) { - /* - * ec_response_usb_pd_control_v2 (r_v2) is a - * strict superset of ec_response_usb_pd_control_v1 - */ - args->response_size = - sizeof(struct ec_response_usb_pd_control_v1); - } else - args->response_size = sizeof(*r_v2); - break; - default: - return EC_RES_INVALID_PARAM; - } - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_USB_PD_CONTROL, - hc_usb_pd_control, - EC_VER_MASK(0) | EC_VER_MASK(1) | EC_VER_MASK(2)); - -static enum ec_status hc_remote_flash(struct host_cmd_handler_args *args) -{ - const struct ec_params_usb_pd_fw_update *p = args->params; - int port = p->port; - int rv = EC_RES_SUCCESS; - const uint32_t *data = &(p->size) + 1; - int i, size; - - if (port >= board_get_usb_pd_port_count()) - return EC_RES_INVALID_PARAM; - - if (p->size + sizeof(*p) > args->params_size) - return EC_RES_INVALID_PARAM; - -#if defined(CONFIG_BATTERY_PRESENT_CUSTOM) || \ -defined(CONFIG_BATTERY_PRESENT_GPIO) - /* - * Do not allow PD firmware update if no battery and this port - * is sinking power, because we will lose power. - */ - if (battery_is_present() != BP_YES && - charge_manager_get_active_charge_port() == port) - return EC_RES_UNAVAILABLE; -#endif - - switch (p->cmd) { - case USB_PD_FW_REBOOT: - pe_send_vdm(port, USB_VID_GOOGLE, VDO_CMD_REBOOT, NULL, 0); - /* - * Return immediately to free pending i2c bus. Host needs to - * manage this delay. - */ - return EC_RES_SUCCESS; - - case USB_PD_FW_FLASH_ERASE: - pe_send_vdm(port, USB_VID_GOOGLE, VDO_CMD_FLASH_ERASE, NULL, 0); - /* - * Return immediately. Host needs to manage delays here which - * can be as long as 1.2 seconds on 64KB RW flash. - */ - return EC_RES_SUCCESS; - - case USB_PD_FW_ERASE_SIG: - pe_send_vdm(port, USB_VID_GOOGLE, VDO_CMD_ERASE_SIG, NULL, 0); - break; - - case USB_PD_FW_FLASH_WRITE: - /* Data size must be a multiple of 4 */ - if (!p->size || p->size % 4) - return EC_RES_INVALID_PARAM; - - size = p->size / 4; - for (i = 0; i < size; i += VDO_MAX_SIZE - 1) { - pe_send_vdm(port, USB_VID_GOOGLE, VDO_CMD_FLASH_WRITE, - data + i, MIN(size - i, VDO_MAX_SIZE - 1)); - } - return EC_RES_SUCCESS; - - default: - return EC_RES_INVALID_PARAM; - } - - return rv; -} -DECLARE_HOST_COMMAND(EC_CMD_USB_PD_FW_UPDATE, - hc_remote_flash, - EC_VER_MASK(0)); - -static enum ec_status -hc_remote_rw_hash_entry(struct host_cmd_handler_args *args) -{ - int i, idx = 0, found = 0; - const struct ec_params_usb_pd_rw_hash_entry *p = args->params; - static int rw_hash_next_idx; - - if (!p->dev_id) - return EC_RES_INVALID_PARAM; - - for (i = 0; i < RW_HASH_ENTRIES; i++) { - if (p->dev_id == rw_hash_table[i].dev_id) { - idx = i; - found = 1; - break; - } - } - - if (!found) { - idx = rw_hash_next_idx; - rw_hash_next_idx = rw_hash_next_idx + 1; - if (rw_hash_next_idx == RW_HASH_ENTRIES) - rw_hash_next_idx = 0; - } - memcpy(&rw_hash_table[idx], p, sizeof(*p)); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_USB_PD_RW_HASH_ENTRY, - hc_remote_rw_hash_entry, - EC_VER_MASK(0)); - -static enum ec_status hc_remote_pd_dev_info(struct host_cmd_handler_args *args) -{ - const uint8_t *port = args->params; - struct ec_params_usb_pd_rw_hash_entry *r = args->response; - - if (*port >= board_get_usb_pd_port_count()) - return EC_RES_INVALID_PARAM; - - r->dev_id = tc[*port].dev_id; - - if (r->dev_id) - memcpy(r->dev_rw_hash, tc[*port].dev_rw_hash, PD_RW_HASH_SIZE); - - r->current_image = tc[*port].current_image; - - args->response_size = sizeof(*r); - return EC_RES_SUCCESS; -} - -DECLARE_HOST_COMMAND(EC_CMD_USB_PD_DEV_INFO, - hc_remote_pd_dev_info, - EC_VER_MASK(0)); - -#ifndef CONFIG_USB_PD_TCPC -#ifdef CONFIG_EC_CMD_PD_CHIP_INFO -static enum ec_status hc_remote_pd_chip_info(struct host_cmd_handler_args *args) -{ - const struct ec_params_pd_chip_info *p = args->params; - struct ec_response_pd_chip_info_v1 *info; - - if (p->port >= board_get_usb_pd_port_count()) - return EC_RES_INVALID_PARAM; - - if (tcpm_get_chip_info(p->port, p->live, &info)) - return EC_RES_ERROR; - - /* - * Take advantage of the fact that v0 and v1 structs have the - * same layout for v0 data. (v1 just appends data) - */ - args->response_size = - args->version ? sizeof(struct ec_response_pd_chip_info_v1) - : sizeof(struct ec_response_pd_chip_info); - - memcpy(args->response, info, args->response_size); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_PD_CHIP_INFO, - hc_remote_pd_chip_info, - EC_VER_MASK(0) | EC_VER_MASK(1)); -#endif /* CONFIG_EC_CMD_PD_CHIP_INFO */ -#endif /* !CONFIG_USB_PD_TCPC */ - -#ifdef CONFIG_HOSTCMD_EVENTS -void pd_notify_dp_alt_mode_entry(void) -{ - /* - * Note: EC_HOST_EVENT_PD_MCU may be a more appropriate host event to - * send, but we do not send that here because there are other cases - * where we send EC_HOST_EVENT_PD_MCU such as charger insertion or - * removal. Currently, those do not wake the system up, but - * EC_HOST_EVENT_MODE_CHANGE does. If we made the system wake up on - * EC_HOST_EVENT_PD_MCU, we would be turning the internal display on on - * every charger insertion/removal, which is not desired. - */ - CPRINTS("Notifying AP of DP Alt Mode Entry..."); - host_set_single_event(EC_HOST_EVENT_MODE_CHANGE); -} -#endif /* CONFIG_HOSTCMD_EVENTS */ - -#ifdef CONFIG_USB_PD_ALT_MODE_DFP -static enum ec_status hc_remote_pd_set_amode(struct host_cmd_handler_args *args) -{ - const struct ec_params_usb_pd_set_mode_request *p = args->params; - - if ((p->port >= board_get_usb_pd_port_count()) || - (!p->svid) || (!p->opos)) - return EC_RES_INVALID_PARAM; - - switch (p->cmd) { - case PD_EXIT_MODE: - if (pd_dfp_exit_mode(p->port, p->svid, p->opos)) - pd_send_vdm(p->port, p->svid, - CMD_EXIT_MODE | VDO_OPOS(p->opos), NULL, 0); - else { - CPRINTF("Failed exit mode\n"); - return EC_RES_ERROR; - } - break; - case PD_ENTER_MODE: - if (pd_dfp_enter_mode(p->port, p->svid, p->opos)) - pd_send_vdm(p->port, p->svid, CMD_ENTER_MODE | - VDO_OPOS(p->opos), NULL, 0); - break; - default: - return EC_RES_INVALID_PARAM; - } - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_USB_PD_SET_AMODE, - hc_remote_pd_set_amode, - EC_VER_MASK(0)); -#endif /* CONFIG_USB_PD_ALT_MODE_DFP */ -#endif /* HAS_TASK_HOSTCMD */ - -#if defined(CONFIG_USB_PD_ALT_MODE) && !defined(CONFIG_USB_PD_ALT_MODE_DFP) -void pd_send_hpd(int port, enum hpd_event hpd) -{ - uint32_t data[1]; - int opos = pd_alt_mode(port, USB_SID_DISPLAYPORT); - - if (!opos) - return; - - data[0] = - VDO_DP_STATUS((hpd == hpd_irq), /* IRQ_HPD */ - (hpd != hpd_low), /* HPD_HI|LOW */ - 0, /* request exit DP */ - 0, /* request exit USB */ - 0, /* MF pref */ - 1, /* enabled */ - 0, /* power low */ - 0x2); - pd_send_vdm(port, USB_SID_DISPLAYPORT, - VDO_OPOS(opos) | CMD_ATTENTION, data, 1); -} -#endif -#endif /* CONFIG_USB_PE_SM */ - -#ifdef CONFIG_USBC_VCONN_SWAP -void pd_request_vconn_swap_off(int port) -{ - if (get_state_tc(port) == TC_ATTACHED_SRC || - get_state_tc(port) == TC_ATTACHED_SNK) { - TC_SET_FLAG(port, TC_FLAGS_REQUEST_VC_SWAP_OFF); - task_wake(PD_PORT_TO_TASK_ID(port)); - } -} - -void pd_request_vconn_swap_on(int port) -{ - if (get_state_tc(port) == TC_ATTACHED_SRC || - get_state_tc(port) == TC_ATTACHED_SNK) { - TC_SET_FLAG(port, TC_FLAGS_REQUEST_VC_SWAP_ON); - task_wake(PD_PORT_TO_TASK_ID(port)); - } -} -#endif - -#ifdef CONFIG_USBC_VCONN -int tc_is_vconn_src(int port) -{ - if (get_state_tc(port) == TC_ATTACHED_SRC || - get_state_tc(port) == TC_ATTACHED_SNK) - return TC_CHK_FLAG(port, TC_FLAGS_VCONN_ON); - else - return -1; -} -#endif - -#ifdef CONFIG_USBC_PPC -static void pd_send_hard_reset(int port) -{ - task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SEND_HARD_RESET, 0); -} - -static uint32_t port_oc_reset_req; - -static void re_enable_ports(void) -{ - uint32_t ports = atomic_read_clear(&port_oc_reset_req); - - while (ports) { - int port = __fls(ports); - - ports &= ~BIT(port); - - /* - * Let the board know that the overcurrent is - * over since we're going to attempt re-enabling - * the port. - */ - board_overcurrent_event(port, 0); - - pd_send_hard_reset(port); - /* - * TODO(b/117854867): PD3.0 to send an alert message - * indicating OCP after explicit contract. - */ - } -} -DECLARE_DEFERRED(re_enable_ports); - -void pd_handle_overcurrent(int port) -{ - /* Keep track of the overcurrent events. */ - CPRINTS("C%d: overcurrent!", port); - - if (IS_ENABLED(CONFIG_USB_PD_LOGGING)) - pd_log_event(PD_EVENT_PS_FAULT, PD_LOG_PORT_SIZE(port, 0), - PS_FAULT_OCP, NULL); - - ppc_add_oc_event(port); - /* Let the board specific code know about the OC event. */ - board_overcurrent_event(port, 1); - - /* Wait 1s before trying to re-enable the port. */ - atomic_or(&port_oc_reset_req, BIT(port)); - hook_call_deferred(&re_enable_ports_data, SECOND); -} -#endif /* defined(CONFIG_USBC_PPC) */ - -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER -static int reset_device_and_notify(int port) -{ - int rv; - int task, waiting_tasks; - - /* This should only be called from the PD task */ - assert(port == TASK_ID_TO_PD_PORT(task_get_current())); - - rv = tc_restart_tcpc(port); - - if (rv == EC_SUCCESS) - CPRINTS("TCPC p%d init ready", port); - else - CPRINTS("TCPC p%d init failed!", port); - - /* - * Before getting the other tasks that are waiting, clear the reset - * event from this PD task to prevent multiple reset/init events - * occurring. - * - * The double reset event happens when the higher priority PD interrupt - * task gets an interrupt during the above tcpm_init function. When that - * occurs, the higher priority task waits correctly for us to finish - * waking the TCPC, but it has also set PD_EVENT_TCPC_RESET again, which - * would result in a second, unnecessary init. - */ - atomic_clear(task_get_event_bitmap(task_get_current()), - PD_EVENT_TCPC_RESET); - - waiting_tasks = atomic_read_clear(&tc[port].tasks_waiting_on_reset); - - /* Wake up all waiting tasks. */ - while (waiting_tasks) { - task = __fls(waiting_tasks); - waiting_tasks &= ~BIT(task); - task_set_event(task, TASK_EVENT_PD_AWAKE, 0); - } - - return rv; -} - -void pd_wait_exit_low_power(int port) -{ - if (TC_CHK_FLAG(port, TC_FLAGS_LPM_ENGAGED)) { - TC_SET_FLAG(port, TC_FLAGS_WAKE_FROM_LPM); - - if (port != TASK_ID_TO_PD_PORT(task_get_current())) { - /* - * Otherwise, we need to wait for the TCPC reset to - * complete - */ - atomic_or(&tc[port].tasks_waiting_on_reset, - 1 << task_get_current()); - /* - * NOTE: We could be sending the PD task the reset - * event while it is already processing the reset event. - * If that occurs, then we will reset the TCPC multiple - * times, which is undesirable but most likely benign. - * Empirically, this doesn't happen much, but it if - * starts occurring, we can add a guard to - * prevent/reduce it. - */ - task_set_event(PD_PORT_TO_TASK_ID(port), - PD_EVENT_TCPC_RESET, 0); - task_wait_event_mask(TASK_EVENT_PD_AWAKE, -1); - } - } -} - -/* - * This can be called from any task. If we are in the PD task, we can handle - * immediately. Otherwise, we need to notify the PD task via event. - */ -void pd_device_accessed(int port) -{ - if (port == TASK_ID_TO_PD_PORT(task_get_current())) - handle_device_access(port); - else - task_set_event(PD_PORT_TO_TASK_ID(port), - PD_EVENT_DEVICE_ACCESSED, 0); -} - -/* - * TODO(b/137493121): Move this function to a separate file that's shared - * between the this and the original stack. - */ -void pd_prevent_low_power_mode(int port, int prevent) -{ - const int current_task_mask = (1 << task_get_current()); - - if (!IS_ENABLED(CONFIG_USB_PD_TCPC_LOW_POWER)) - return; - - if (prevent) - atomic_or(&tc[port].tasks_preventing_lpm, current_task_mask); - else - atomic_clear(&tc[port].tasks_preventing_lpm, current_task_mask); -} -#endif /* CONFIG_USB_PD_TCPC_LOW_POWER */ - -static void sink_power_sub_states(int port) -{ - enum tcpc_cc_voltage_status cc1, cc2, cc; - enum tcpc_cc_voltage_status new_cc_voltage; - - tcpm_get_cc(port, &cc1, &cc2); - - cc = tc[port].polarity ? cc2 : cc1; - - if (cc == TYPEC_CC_VOLT_RP_DEF) - new_cc_voltage = TYPEC_CC_VOLT_RP_DEF; - else if (cc == TYPEC_CC_VOLT_RP_1_5) - new_cc_voltage = TYPEC_CC_VOLT_RP_1_5; - else if (cc == TYPEC_CC_VOLT_RP_3_0) - new_cc_voltage = TYPEC_CC_VOLT_RP_3_0; - else - new_cc_voltage = TYPEC_CC_VOLT_OPEN; - - /* Debounce the cc state */ - if (new_cc_voltage != tc[port].cc_voltage) { - tc[port].cc_voltage = new_cc_voltage; - tc[port].cc_debounce = - get_time().val + PD_T_RP_VALUE_CHANGE; - return; - } - - if (tc[port].cc_debounce == 0 || - get_time().val < tc[port].cc_debounce) - return; - - tc[port].cc_debounce = 0; - - if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) { - tc[port].typec_curr = usb_get_typec_current_limit( - tc[port].polarity, cc1, cc2); - - typec_set_input_current_limit(port, - tc[port].typec_curr, TYPE_C_VOLTAGE); - charge_manager_update_dualrole(port, CAP_DEDICATED); - } -} - - -/* - * TYPE-C State Implementations - */ - -/** - * Disabled - * - * Super State Entry Actions: - * Remove the terminations from CC - * Set's VBUS and VCONN off - */ -static void tc_disabled_entry(const int port) -{ - print_current_state(port); -} - -static void tc_disabled_run(const int port) -{ - task_wait_event(-1); -} - -static void tc_disabled_exit(const int port) -{ - if (!IS_ENABLED(CONFIG_USB_PD_TCPC)) { - if (tc_restart_tcpc(port) != 0) { - CPRINTS("TCPC p%d restart failed!", port); - return; - } - } - - CPRINTS("TCPC p%d resumed!", port); -} - -/** - * ErrorRecovery - * - * Super State Entry Actions: - * Remove the terminations from CC - * Set's VBUS and VCONN off - */ -static void tc_error_recovery_entry(const int port) -{ - print_current_state(port); - - tc[port].timeout = get_time().val + PD_T_ERROR_RECOVERY; -} - -static void tc_error_recovery_run(const int port) -{ - if (tc[port].timeout > 0 && get_time().val > tc[port].timeout) { - tc[port].timeout = 0; - restart_tc_sm(port, TC_UNATTACHED_SRC); - } -} - -/** - * Unattached.SNK - * - * Super State is Unattached state - */ -static void tc_unattached_snk_entry(const int port) -{ - if (get_last_state_tc(port) != TC_UNATTACHED_SRC) - print_current_state(port); - - if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) - charge_manager_update_dualrole(port, CAP_UNKNOWN); - - /* - * Indicate that the port is disconnected so the board - * can restore state from any previous data swap. - */ - pd_execute_data_swap(port, PD_ROLE_DISCONNECTED); - tc[port].next_role_swap = get_time().val + PD_T_DRP_SNK; - - if (IS_ENABLED(CONFIG_USBC_SS_MUX)) - usb_mux_set(port, TYPEC_MUX_NONE, - USB_SWITCH_DISCONNECT, tc[port].polarity); - - if (IS_ENABLED(CONFIG_USB_PE_SM)) { - CLR_ALL_BUT_LPM_FLAGS(port); - tc[port].pd_enable = 0; - } -} - -static void tc_unattached_snk_run(const int port) -{ - enum tcpc_cc_voltage_status cc1, cc2; - - /* - * TODO(b/137498392): Add wait before sampling the CC - * status after role changes - */ - - if (IS_ENABLED(CONFIG_USB_PE_SM)) { - if (TC_CHK_FLAG(port, TC_FLAGS_HARD_RESET)) { - TC_CLR_FLAG(port, TC_FLAGS_HARD_RESET); - tc_set_data_role(port, PD_ROLE_UFP); - /* Inform Policy Engine that hard reset is complete */ - pe_ps_reset_complete(port); - } - } - - /* Check for connection */ - tcpm_get_cc(port, &cc1, &cc2); - -#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE - /* - * Attempt TCPC auto DRP toggle if it is - * not already auto toggling. - */ - if (drp_state[port] == PD_DRP_TOGGLE_ON && - TC_CHK_FLAG(port, TC_FLAGS_AUTO_TOGGLE_SUPPORTED) && - cc_is_open(cc1, cc2)) { - set_state_tc(port, TC_DRP_AUTO_TOGGLE); - return; - } -#endif - - /* - * The port shall transition to AttachWait.SNK when a Source - * connection is detected, as indicated by the SNK.Rp state - * on at least one of its CC pins. - * - * A DRP shall transition to Unattached.SRC within tDRPTransition - * after the state of both CC pins is SNK.Open for - * tDRP − dcSRC.DRP ∙ tDRP. - */ - if (cc_is_rp(cc1) || cc_is_rp(cc2)) { - /* Connection Detected */ - set_state_tc(port, TC_ATTACH_WAIT_SNK); - } else if (get_time().val > tc[port].next_role_swap && - drp_state[port] == PD_DRP_TOGGLE_ON) { - /* DRP Toggle */ - set_state_tc(port, TC_UNATTACHED_SRC); - } - -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER - else if (drp_state[port] == PD_DRP_FORCE_SINK || - drp_state[port] == PD_DRP_TOGGLE_OFF) { - set_state_tc(port, TC_LOW_POWER_MODE); - } -#endif -} - -/** - * AttachWait.SNK - * - * Super State Entry Actions: - * Vconn Off - * Place Rd on CC - * Set power role to SINK - */ -static void tc_attach_wait_snk_entry(const int port) -{ - print_current_state(port); - - tc[port].cc_state = PD_CC_UNSET; -} - -static void tc_attach_wait_snk_run(const int port) -{ - enum tcpc_cc_voltage_status cc1, cc2; - enum pd_cc_states new_cc_state; - - /* Check for connection */ - tcpm_get_cc(port, &cc1, &cc2); - - if (cc_is_rp(cc1) && cc_is_rp(cc2)) - new_cc_state = PD_CC_DFP_DEBUG_ACC; - else if (cc_is_rp(cc1) || cc_is_rp(cc2)) - new_cc_state = PD_CC_DFP_ATTACHED; - else - new_cc_state = PD_CC_NONE; - - /* Debounce the cc state */ - if (new_cc_state != tc[port].cc_state) { - tc[port].cc_debounce = get_time().val + PD_T_CC_DEBOUNCE; - tc[port].pd_debounce = get_time().val + PD_T_PD_DEBOUNCE; - tc[port].cc_state = new_cc_state; - return; - } - - /* - * A DRP shall transition to Unattached.SNK when the state of both - * the CC1 and CC2 pins is SNK.Open for at least tPDDebounce. - */ - if (new_cc_state == PD_CC_NONE && - get_time().val > tc[port].pd_debounce) { - if (IS_ENABLED(CONFIG_USB_PE_SM) && - IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) { - pd_dfp_exit_mode(port, 0, 0); - } - - /* We are detached */ - set_state_tc(port, TC_UNATTACHED_SRC); - return; - } - - /* Wait for CC debounce */ - if (get_time().val < tc[port].cc_debounce) - return; - - /* - * The port shall transition to Attached.SNK after the state of only - * one of the CC1 or CC2 pins is SNK.Rp for at least tCCDebounce and - * VBUS is detected. - * - * A DRP that strongly prefers the Source role may optionally - * transition to Try.SRC instead of Attached.SNK when the state of only - * one CC pin has been SNK.Rp for at least tCCDebounce and VBUS is - * detected. - * - * If the port supports Debug Accessory Mode, the port shall transition - * to DebugAccessory.SNK if the state of both the CC1 and CC2 pins is - * SNK.Rp for at least tCCDebounce and VBUS is detected. - */ - if (pd_is_vbus_present(port)) { - if (new_cc_state == PD_CC_DFP_ATTACHED) { -#ifdef CONFIG_USB_PD_TRY_SRC - if (pd_try_src_enable) - set_state_tc(port, TC_TRY_SRC); - else -#endif - set_state_tc(port, TC_ATTACHED_SNK); - } else { - /* new_cc_state is PD_CC_DFP_DEBUG_ACC */ - TC_SET_FLAG(port, TC_FLAGS_TS_DTS_PARTNER); - set_state_tc(port, TC_DBG_ACC_SNK); - } - - if (IS_ENABLED(CONFIG_USB_PE_SM) && - IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) { - hook_call_deferred(&pd_usb_billboard_deferred_data, - PD_T_AME); - } - } -} - -/** - * Attached.SNK - */ -static void tc_attached_snk_entry(const int port) -{ - enum tcpc_cc_voltage_status cc1, cc2; - - print_current_state(port); - - /* Clear Low Power Mode Request */ - TC_CLR_FLAG(port, TC_FLAGS_LPM_REQUESTED); - -#ifdef CONFIG_USB_PE_SM - if (TC_CHK_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS)) { - /* - * Both CC1 and CC2 pins shall be independently terminated to - * ground through Rd. - */ - tcpm_set_cc(port, TYPEC_CC_RD); - - /* Change role to sink */ - tc_set_power_role(port, PD_ROLE_SINK); - tcpm_set_msg_header(port, tc[port].power_role, - tc[port].data_role); - - /* - * Maintain VCONN supply state, whether ON or OFF, and its - * data role / usb mux connections. - */ - } else -#endif - { - /* Get connector orientation */ - tcpm_get_cc(port, &cc1, &cc2); - tc[port].polarity = get_snk_polarity(cc1, cc2); - set_polarity(port, tc[port].polarity); - - /* - * Initial data role for sink is UFP - * This also sets the usb mux - */ - tc_set_data_role(port, PD_ROLE_UFP); - - if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) { - tc[port].typec_curr = - usb_get_typec_current_limit(tc[port].polarity, - cc1, cc2); - typec_set_input_current_limit(port, - tc[port].typec_curr, TYPE_C_VOLTAGE); - charge_manager_update_dualrole(port, - pd_is_port_partner_dualrole(port) ? - CAP_DUALROLE : CAP_DEDICATED); - } - } - - /* Apply Rd */ - tcpm_set_cc(port, TYPEC_CC_RD); - - tc[port].cc_debounce = 0; - - /* Enable PD */ - if (IS_ENABLED(CONFIG_USB_PE_SM)) - tc[port].pd_enable = 1; -} - -static void tc_attached_snk_run(const int port) -{ -#ifdef CONFIG_USB_PE_SM - /* - * Perform Hard Reset - */ - if (TC_CHK_FLAG(port, TC_FLAGS_HARD_RESET)) { - TC_CLR_FLAG(port, TC_FLAGS_HARD_RESET); - tc_perform_snk_hard_reset(port); - } - - /* - * The sink will be powered off during a power role swap but we don't - * want to trigger a disconnect - */ - if (!TC_CHK_FLAG(port, TC_FLAGS_POWER_OFF_SNK) && - !TC_CHK_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS)) { - /* Detach detection */ - if (!pd_is_vbus_present(port)) { - if (IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) - pd_dfp_exit_mode(port, 0, 0); - - set_state_tc(port, TC_UNATTACHED_SNK); - return; - } - - if (!pe_is_explicit_contract(port)) - sink_power_sub_states(port); - } - - /* - * PD swap commands - */ - if (tc[port].pd_enable && prl_is_running(port)) { - /* - * Power Role Swap - */ - if (TC_CHK_FLAG(port, TC_FLAGS_DO_PR_SWAP)) { - /* Clear PR_SWAP flag in exit */ - set_state_tc(port, TC_ATTACHED_SRC); - return; - } - - /* - * Data Role Swap - */ - if (TC_CHK_FLAG(port, TC_FLAGS_REQUEST_DR_SWAP)) { - TC_CLR_FLAG(port, TC_FLAGS_REQUEST_DR_SWAP); - - /* Perform Data Role Swap */ - tc_set_data_role(port, - tc[port].data_role == PD_ROLE_UFP ? - PD_ROLE_DFP : PD_ROLE_UFP); - } - -#ifdef CONFIG_USBC_VCONN - /* - * VCONN Swap - */ - if (TC_CHK_FLAG(port, TC_FLAGS_REQUEST_VC_SWAP_ON)) { - TC_CLR_FLAG(port, TC_FLAGS_REQUEST_VC_SWAP_ON); - - set_vconn(port, 1); - /* Inform policy engine that vconn swap is complete */ - pe_vconn_swap_complete(port); - } else if (TC_CHK_FLAG(port, TC_FLAGS_REQUEST_VC_SWAP_OFF)) { - TC_CLR_FLAG(port, TC_FLAGS_REQUEST_VC_SWAP_OFF); - - set_vconn(port, 0); - /* Inform policy engine that vconn swap is complete */ - pe_vconn_swap_complete(port); - } -#endif - /* - * If the port supports Charge-Through VCONN-Powered USB - * devices, and an explicit PD contract has failed to be - * negotiated, the port shall query the identity of the - * cable via USB PD on SOP’ - */ - if (!pe_is_explicit_contract(port) && - TC_CHK_FLAG(port, TC_FLAGS_CTVPD_DETECTED)) { - /* - * A port that via SOP’ has detected an attached - * Charge-Through VCONN-Powered USB device shall - * transition to Unattached.SRC if an explicit PD - * contract has failed to be negotiated. - */ - /* CTVPD detected */ - set_state_tc(port, TC_UNATTACHED_SRC); - } - } - -#else /* CONFIG_USB_PE_SM */ - - /* Detach detection */ - if (!pd_is_vbus_present(port)) { - set_state_tc(port, TC_UNATTACHED_SNK); - return; - } - - /* Run Sink Power Sub-State */ - sink_power_sub_states(port); -#endif /* CONFIG_USB_PE_SM */ -} - -static void tc_attached_snk_exit(const int port) -{ - /* - * If supplying VCONN, the port shall cease to supply - * it within tVCONNOFF of exiting Attached.SNK if not PR swapping. - */ - if (TC_CHK_FLAG(port, TC_FLAGS_VCONN_ON) && - !TC_CHK_FLAG(port, TC_FLAGS_DO_PR_SWAP)) - set_vconn(port, 0); - - /* Clear flags after checking Vconn status */ - TC_CLR_FLAG(port, TC_FLAGS_DO_PR_SWAP | TC_FLAGS_POWER_OFF_SNK); - - /* Stop drawing power */ - sink_stop_drawing_current(port); -} - -/** - * UnorientedDebugAccessory.SRC - * - * Super State Entry Actions: - * Place Rp on CC - * Set power role to SOURCE - */ -static void tc_unoriented_dbg_acc_src_entry(const int port) -{ - enum tcpc_cc_voltage_status cc1, cc2; - - print_current_state(port); - - /* Run function relies on timeout being 0 or meaningful */ - tc[port].timeout = 0; - - /* Clear Low Power Mode Request */ - TC_CLR_FLAG(port, TC_FLAGS_LPM_REQUESTED); - - if (TC_CHK_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS)) { - /* Enable VBUS */ - pd_set_power_supply_ready(port); - - /* - * Maintain VCONN supply state, whether ON or OFF, and its - * data role / usb mux connections. - */ - } else { - /* Get connector orientation */ - tcpm_get_cc(port, &cc1, &cc2); - tc[port].polarity = (cc1 != TYPEC_CC_VOLT_RD); - set_polarity(port, tc[port].polarity); - - /* - * Initial data role for sink is DFP - * This also sets the usb mux - */ - tc_set_data_role(port, PD_ROLE_DFP); - - /* Enable VBUS */ - if (pd_set_power_supply_ready(port)) { - if (IS_ENABLED(CONFIG_USBC_SS_MUX)) - usb_mux_set(port, TYPEC_MUX_NONE, - USB_SWITCH_DISCONNECT, tc[port].polarity); - } - -#ifdef CONFIG_USB_PE_SM - tc[port].pd_enable = 0; - tc[port].timeout = get_time().val + - PD_POWER_SUPPLY_TURN_ON_DELAY; -#endif - } - - /* Inform PPC that a sink is connected. */ - if (IS_ENABLED(CONFIG_USBC_PPC)) - ppc_sink_is_connected(port, 1); -} - -static void tc_unoriented_dbg_acc_src_run(const int port) -{ - enum tcpc_cc_voltage_status cc1, cc2; - enum pd_cc_states new_cc_state; - -#ifdef CONFIG_USB_PE_SM - /* Enable PD communications after power supply has fully turned on */ - if (tc[port].pd_enable == 0 && - get_time().val > tc[port].timeout) { - - tc[port].pd_enable = 1; - tc[port].timeout = 0; - } - - if (tc[port].pd_enable == 0) - return; - - /* - * Handle Hard Reset from Policy Engine - */ - if (TC_CHK_FLAG(port, TC_FLAGS_HARD_RESET)) { - if (get_time().val < tc[port].timeout) - return; - - /* - * This function clears TC_FLAGS_HARD_RESET - * when the hard reset is complete. - */ - tc_perform_src_hard_reset(port); - } -#endif - - /* Check for connection */ - tcpm_get_cc(port, &cc1, &cc2); - - if (tc[port].polarity) - cc1 = cc2; - - if (cc1 == TYPEC_CC_VOLT_OPEN) - new_cc_state = PD_CC_NONE; - else - new_cc_state = PD_CC_UFP_ATTACHED; - - /* Debounce the cc state */ - if (new_cc_state != tc[port].cc_state) { - tc[port].cc_state = new_cc_state; - tc[port].cc_debounce = get_time().val + PD_T_SRC_DISCONNECT; - } - - if (get_time().val < tc[port].cc_debounce) - return; - - if (tc[port].cc_state == PD_CC_NONE && - !TC_CHK_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS) && - !TC_CHK_FLAG(port, TC_FLAGS_DISC_IDENT_IN_PROGRESS)) { - - tc[port].pd_enable = 0; - set_state_tc(port, TC_UNATTACHED_SNK); - } - -#ifdef CONFIG_USB_PE_SM - /* - * PD swap commands - */ - if (tc[port].pd_enable) { - /* - * Power Role Swap Request - */ - if (TC_CHK_FLAG(port, TC_FLAGS_DO_PR_SWAP)) { - /* Clear TC_FLAGS_DO_PR_SWAP on exit */ - return set_state_tc(port, TC_DBG_ACC_SNK); - } - - /* - * Data Role Swap Request - */ - if (TC_CHK_FLAG(port, TC_FLAGS_REQUEST_DR_SWAP)) { - TC_CLR_FLAG(port, TC_FLAGS_REQUEST_DR_SWAP); - - /* Perform Data Role Swap */ - tc_set_data_role(port, - tc[port].data_role == PD_ROLE_DFP ? - PD_ROLE_UFP : PD_ROLE_DFP); - } - } -#endif -} - -static void tc_unoriented_dbg_acc_src_exit(const int port) -{ - /* - * A port shall cease to supply VBUS within tVBUSOFF of exiting - * UnorientedDbg.SRC. - */ - tc_src_power_off(port); - - /* Clear PR swap flag */ - TC_CLR_FLAG(port, TC_FLAGS_DO_PR_SWAP); -} - -/** - * Debug Accessory.SNK - * - * Super State Entry Actions: - * Vconn Off - * Place Rd on CC - * Set power role to SINK - */ -static void tc_dbg_acc_snk_entry(const int port) -{ - enum tcpc_cc_voltage_status cc1, cc2; - - print_current_state(port); - - if (TC_CHK_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS)) { - /* - * Both CC1 and CC2 pins shall be independently terminated to - * ground through Rd. - */ - tcpm_set_cc(port, TYPEC_CC_RD); - - /* Change role to sink */ - tc_set_power_role(port, PD_ROLE_SINK); - tcpm_set_msg_header(port, tc[port].power_role, - tc[port].data_role); - - /* - * Maintain VCONN supply state, whether ON or OFF, and its - * data role / usb mux connections. - */ - } else { - /* Get connector orientation */ - tcpm_get_cc(port, &cc1, &cc2); - tc[port].polarity = get_snk_polarity(cc1, cc2); - set_polarity(port, tc[port].polarity); - - /* - * Initial data role for sink is UFP - * This also sets the usb mux - */ - tc_set_data_role(port, PD_ROLE_UFP); - - if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) { - tc[port].typec_curr = - usb_get_typec_current_limit(tc[port].polarity, - cc1, cc2); - typec_set_input_current_limit(port, - tc[port].typec_curr, TYPE_C_VOLTAGE); - charge_manager_update_dualrole(port, - pd_is_port_partner_dualrole(port) ? - CAP_DUALROLE : CAP_DEDICATED); - } - } - - /* Enable PD */ - tc[port].pd_enable = 1; -} - -static void tc_dbg_acc_snk_run(const int port) -{ - - if (!IS_ENABLED(CONFIG_USB_PE_SM)) { - /* Detach detection */ - if (!pd_is_vbus_present(port)) { - set_state_tc(port, TC_UNATTACHED_SNK); - return; - } - - /* Run Sink Power Sub-State */ - sink_power_sub_states(port); - - return; - } - - /* - * Perform Hard Reset - */ - if (TC_CHK_FLAG(port, TC_FLAGS_HARD_RESET)) { - TC_CLR_FLAG(port, TC_FLAGS_HARD_RESET); - tc_perform_snk_hard_reset(port); - } - - /* - * The sink will be powered off during a power role swap but we - * don't want to trigger a disconnect - */ - if (!TC_CHK_FLAG(port, TC_FLAGS_POWER_OFF_SNK) && - !TC_CHK_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS)) { - /* Detach detection */ - if (!pd_is_vbus_present(port)) { - if (IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) - pd_dfp_exit_mode(port, 0, 0); - - set_state_tc(port, TC_UNATTACHED_SNK); - return; - } - - if (!pe_is_explicit_contract(port)) - sink_power_sub_states(port); - } - - /* PD swap commands */ - - /* - * Power Role Swap - */ - if (TC_CHK_FLAG(port, TC_FLAGS_DO_PR_SWAP)) { - /* Clear PR_SWAP flag in exit */ - set_state_tc(port, TC_UNORIENTED_DBG_ACC_SRC); - return; - } - - /* - * Data Role Swap - */ - if (TC_CHK_FLAG(port, TC_FLAGS_REQUEST_DR_SWAP)) { - TC_CLR_FLAG(port, TC_FLAGS_REQUEST_DR_SWAP); - - /* Perform Data Role Swap */ - tc_set_data_role(port, tc[port].data_role == PD_ROLE_UFP ? - PD_ROLE_DFP : PD_ROLE_UFP); - } -} - -static void tc_dbg_acc_snk_exit(const int port) -{ - TC_CLR_FLAG(port, TC_FLAGS_DO_PR_SWAP | TC_FLAGS_POWER_OFF_SNK); - - /* Stop drawing power */ - sink_stop_drawing_current(port); -} - -/** - * Unattached.SRC - * - * Super State is Unattached state - */ -static void tc_unattached_src_entry(const int port) -{ - if (get_last_state_tc(port) != TC_UNATTACHED_SNK) - print_current_state(port); - - if (IS_ENABLED(CONFIG_USBC_PPC)) { - /* There is no sink connected. */ - ppc_sink_is_connected(port, 0); - - /* - * Clear the overcurrent event counter - * since we've detected a disconnect. - */ - ppc_clear_oc_event_counter(port); - } - - if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) - charge_manager_update_dualrole(port, CAP_UNKNOWN); - - if (IS_ENABLED(CONFIG_USB_PE_SM)) { - CLR_ALL_BUT_LPM_FLAGS(port); - tc[port].pd_enable = 0; - } - - tc[port].next_role_swap = get_time().val + PD_T_DRP_SRC; -} - -static void tc_unattached_src_run(const int port) -{ - enum tcpc_cc_voltage_status cc1, cc2; - - if (IS_ENABLED(CONFIG_USB_PE_SM)) { - if (TC_CHK_FLAG(port, TC_FLAGS_HARD_RESET)) { - TC_CLR_FLAG(port, TC_FLAGS_HARD_RESET); - tc_set_data_role(port, PD_ROLE_DFP); - /* Inform Policy Engine that hard reset is complete */ - pe_ps_reset_complete(port); - } - } - - if (IS_ENABLED(CONFIG_USBC_PPC)) { - /* - * If the port is latched off, just continue to - * monitor for a detach. - */ - if (ppc_is_port_latched_off(port)) - return; - } - - /* Check for connection */ - tcpm_get_cc(port, &cc1, &cc2); - - /* - * Transition to AttachWait.SRC when VBUS is vSafe0V and: - * 1) The SRC.Rd state is detected on either CC1 or CC2 pin or - * 2) The SRC.Ra state is detected on both the CC1 and CC2 pins. - * - * A DRP shall transition to Unattached.SNK within tDRPTransition - * after dcSRC.DRP ∙ tDRP - */ - if (cc_is_at_least_one_rd(cc1, cc2) || cc_is_audio_acc(cc1, cc2)) - set_state_tc(port, TC_ATTACH_WAIT_SRC); - else if (get_time().val > tc[port].next_role_swap && - drp_state[port] != PD_DRP_FORCE_SOURCE && - drp_state[port] != PD_DRP_FREEZE) - set_state_tc(port, TC_UNATTACHED_SNK); -#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE - /* - * Attempt TCPC auto DRP toggle - */ - else if (drp_state[port] == PD_DRP_TOGGLE_ON && - TC_CHK_FLAG(port, TC_FLAGS_AUTO_TOGGLE_SUPPORTED) && - cc_is_open(cc1, cc2)) { - set_state_tc(port, TC_DRP_AUTO_TOGGLE); - } -#endif - -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER - else if (drp_state[port] == PD_DRP_FORCE_SOURCE || - drp_state[port] == PD_DRP_TOGGLE_OFF) { - set_state_tc(port, TC_LOW_POWER_MODE); - } -#endif -} - -/** - * AttachWait.SRC - * - * Super State Entry Actions: - * Vconn Off - * Place Rp on CC - * Set power role to SOURCE - */ -static void tc_attach_wait_src_entry(const int port) -{ - print_current_state(port); - - tc[port].cc_state = PD_CC_UNSET; -} - -static void tc_attach_wait_src_run(const int port) -{ - enum tcpc_cc_voltage_status cc1, cc2; - enum pd_cc_states new_cc_state; - - /* Check for connection */ - tcpm_get_cc(port, &cc1, &cc2); - - /* Debug accessory */ - if (cc_is_snk_dbg_acc(cc1, cc2)) { - /* Debug accessory */ - new_cc_state = PD_CC_UFP_DEBUG_ACC; - } else if (cc_is_at_least_one_rd(cc1, cc2)) { - /* UFP attached */ - new_cc_state = PD_CC_UFP_ATTACHED; - } else if (cc_is_audio_acc(cc1, cc2)) { - /* AUDIO Accessory not supported. Just ignore */ - new_cc_state = PD_CC_UFP_AUDIO_ACC; - } else { - /* No UFP */ - set_state_tc(port, TC_UNATTACHED_SNK); - return; - } - - /* Debounce the cc state */ - if (new_cc_state != tc[port].cc_state) { - tc[port].cc_debounce = get_time().val + PD_T_CC_DEBOUNCE; - tc[port].cc_state = new_cc_state; - return; - } - - /* Wait for CC debounce */ - if (get_time().val < tc[port].cc_debounce) - return; - - /* - * The port shall transition to Attached.SRC when VBUS is at vSafe0V - * and the SRC.Rd state is detected on exactly one of the CC1 or CC2 - * pins for at least tCCDebounce. - * - * If the port supports Debug Accessory Mode, it shall transition to - * UnorientedDebugAccessory.SRC when VBUS is at vSafe0V and the SRC.Rd - * state is detected on both the CC1 and CC2 pins for at least - * tCCDebounce. - */ - if (!pd_is_vbus_present(port)) { - if (new_cc_state == PD_CC_UFP_ATTACHED) { - set_state_tc(port, TC_ATTACHED_SRC); - return; - } else if (new_cc_state == PD_CC_UFP_DEBUG_ACC) { - set_state_tc(port, TC_UNORIENTED_DBG_ACC_SRC); - return; - } - } -} - -/** - * Attached.SRC - */ -static void tc_attached_src_entry(const int port) -{ - enum tcpc_cc_voltage_status cc1, cc2; - - print_current_state(port); - - /* Run function relies on timeout being 0 or meaningful */ - tc[port].timeout = 0; - - /* Clear Low Power Mode Request */ - TC_CLR_FLAG(port, TC_FLAGS_LPM_REQUESTED); - -#if defined(CONFIG_USB_PE_SM) - if (TC_CHK_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS)) { - /* Change role to source */ - tc_set_power_role(port, PD_ROLE_SOURCE); - tcpm_set_msg_header(port, - tc[port].power_role, tc[port].data_role); - /* - * Both CC1 and CC2 pins shall be independently terminated to - * ground through Rp. - */ - tcpm_select_rp_value(port, CONFIG_USB_PD_PULLUP); - - /* Enable VBUS */ - pd_set_power_supply_ready(port); - - /* - * Maintain VCONN supply state, whether ON or OFF, and its - * data role / usb mux connections. - */ - } else { - /* Get connector orientation */ - tcpm_get_cc(port, &cc1, &cc2); - tc[port].polarity = (cc1 != TYPEC_CC_VOLT_RD); - set_polarity(port, tc[port].polarity); - - /* - * Initial data role for sink is DFP - * This also sets the usb mux - */ - tc_set_data_role(port, PD_ROLE_DFP); - - /* - * Start sourcing Vconn before Vbus to ensure - * we are within USB Type-C Spec 1.4 tVconnON - */ - if (IS_ENABLED(CONFIG_USBC_VCONN)) - set_vconn(port, 1); - - /* Enable VBUS */ - if (pd_set_power_supply_ready(port)) { - /* Stop sourcing Vconn if Vbus failed */ - if (IS_ENABLED(CONFIG_USBC_VCONN)) - set_vconn(port, 0); - - if (IS_ENABLED(CONFIG_USBC_SS_MUX)) - usb_mux_set(port, TYPEC_MUX_NONE, - USB_SWITCH_DISCONNECT, tc[port].polarity); - } - - tc[port].pd_enable = 0; - tc[port].timeout = get_time().val + - MAX(PD_POWER_SUPPLY_TURN_ON_DELAY, PD_T_VCONN_STABLE); - } -#else - /* Get connector orientation */ - tcpm_get_cc(port, &cc1, &cc2); - tc[port].polarity = (cc1 != TYPEC_CC_VOLT_RD); - set_polarity(port, tc[port].polarity); - - /* - * Initial data role for sink is DFP - * This also sets the usb mux - */ - tc_set_data_role(port, PD_ROLE_DFP); - - /* - * Start sourcing Vconn before Vbus to ensure - * we are within USB Type-C Spec 1.4 tVconnON - */ - if (IS_ENABLED(CONFIG_USBC_VCONN)) - set_vconn(port, 1); - - /* Enable VBUS */ - if (pd_set_power_supply_ready(port)) { - /* Stop sourcing Vconn if Vbus failed */ - if (IS_ENABLED(CONFIG_USBC_VCONN)) - set_vconn(port, 0); - - if (IS_ENABLED(CONFIG_USBC_SS_MUX)) - usb_mux_set(port, TYPEC_MUX_NONE, - USB_SWITCH_DISCONNECT, tc[port].polarity); - } -#endif /* CONFIG_USB_PE_SM */ - - /* Inform PPC that a sink is connected. */ - if (IS_ENABLED(CONFIG_USBC_PPC)) - ppc_sink_is_connected(port, 1); -} - -static void tc_attached_src_run(const int port) -{ - enum tcpc_cc_voltage_status cc1, cc2; - enum pd_cc_states new_cc_state; - -#ifdef CONFIG_USB_PE_SM - /* Enable PD communications after power supply has fully turned on */ - if (tc[port].pd_enable == 0 && - get_time().val > tc[port].timeout) { - - tc[port].pd_enable = 1; - tc[port].timeout = 0; - } - - if (tc[port].pd_enable == 0) - return; - - /* - * Handle Hard Reset from Policy Engine - */ - if (TC_CHK_FLAG(port, TC_FLAGS_HARD_RESET)) { - if (get_time().val < tc[port].timeout) - return; - /* - * This function clears TC_FLAGS_HARD_RESET - * when the hard reset is complete. - */ - tc_perform_src_hard_reset(port); - } -#endif - - /* Check for connection */ - tcpm_get_cc(port, &cc1, &cc2); - - if (tc[port].polarity) - cc1 = cc2; - - if (cc1 == TYPEC_CC_VOLT_OPEN) - new_cc_state = PD_CC_NONE; - else - new_cc_state = PD_CC_UFP_ATTACHED; - - /* Debounce the cc state */ - if (new_cc_state != tc[port].cc_state) { - tc[port].cc_state = new_cc_state; - tc[port].cc_debounce = get_time().val + PD_T_SRC_DISCONNECT; - } - - if (get_time().val < tc[port].cc_debounce) - return; - - /* - * When the SRC.Open state is detected on the monitored CC pin, a DRP - * shall transition to Unattached.SNK unless it strongly prefers the - * Source role. In that case, it shall transition to TryWait.SNK. - * This transition to TryWait.SNK is needed so that two devices that - * both prefer the Source role do not loop endlessly between Source - * and Sink. In other words, a DRP that would enter Try.SRC from - * AttachWait.SNK shall enter TryWait.SNK for a Sink detach from - * Attached.SRC. - */ - if (tc[port].cc_state == PD_CC_NONE && - !TC_CHK_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS) && - !TC_CHK_FLAG(port, TC_FLAGS_DISC_IDENT_IN_PROGRESS)) { - - if (IS_ENABLED(CONFIG_USB_PE_SM)) - if (IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) - pd_dfp_exit_mode(port, 0, 0); - - tc[port].pd_enable = 0; - set_state_tc(port, IS_ENABLED(CONFIG_USB_PD_TRY_SRC) ? - TC_TRY_WAIT_SNK : TC_UNATTACHED_SNK); - } - -#ifdef CONFIG_USB_PE_SM - /* - * PD swap commands - */ - if (tc[port].pd_enable && prl_is_running(port)) { - /* - * Power Role Swap Request - */ - if (TC_CHK_FLAG(port, TC_FLAGS_DO_PR_SWAP)) { - TC_CLR_FLAG(port, TC_FLAGS_DO_PR_SWAP); - return set_state_tc(port, TC_ATTACHED_SNK); - } - - /* - * Data Role Swap Request - */ - if (TC_CHK_FLAG(port, TC_FLAGS_REQUEST_DR_SWAP)) { - TC_CLR_FLAG(port, TC_FLAGS_REQUEST_DR_SWAP); - - /* Perform Data Role Swap */ - tc_set_data_role(port, - tc[port].data_role == PD_ROLE_DFP ? - PD_ROLE_UFP : PD_ROLE_DFP); - } - - if (IS_ENABLED(CONFIG_USBC_VCONN)) { - /* - * VCONN Swap Request - */ - if (TC_CHK_FLAG(port, TC_FLAGS_REQUEST_VC_SWAP_ON)) { - TC_CLR_FLAG(port, TC_FLAGS_REQUEST_VC_SWAP_ON); - set_vconn(port, 1); - pe_vconn_swap_complete(port); - } else if (TC_CHK_FLAG(port, - TC_FLAGS_REQUEST_VC_SWAP_OFF)) { - TC_CLR_FLAG(port, TC_FLAGS_REQUEST_VC_SWAP_OFF); - set_vconn(port, 0); - pe_vconn_swap_complete(port); - } - } - - /* - * A DRP that supports Charge-Through VCONN-Powered USB Devices - * shall transition to CTUnattached.SNK if the connected device - * identifies itself as a Charge-Through VCONN-Powered USB - * Device in its Discover Identity Command response. - */ - - /* - * A DRP that supports Charge-Through VCONN-Powered USB Devices - * shall transition to CTUnattached.SNK if the connected device - * identifies itself as a Charge-Through VCONN-Powered USB - * Device in its Discover Identity Command response. - * - * If it detects that it is connected to a VCONN-Powered USB - * Device, the port may remove VBUS and discharge it to - * vSafe0V, while continuing to remain in this state with VCONN - * applied. - */ - if (TC_CHK_FLAG(port, TC_FLAGS_CTVPD_DETECTED)) { - TC_CLR_FLAG(port, TC_FLAGS_CTVPD_DETECTED); - - /* Clear TC_FLAGS_DISC_IDENT_IN_PROGRESS */ - TC_CLR_FLAG(port, TC_FLAGS_DISC_IDENT_IN_PROGRESS); - - set_state_tc(port, TC_CT_UNATTACHED_SNK); - } - } -#endif -} - -static void tc_attached_src_exit(const int port) -{ - /* - * A port shall cease to supply VBUS within tVBUSOFF of exiting - * Attached.SRC. - */ - tc_src_power_off(port); - - /* Disable VCONN if not power role swapping */ - if (TC_CHK_FLAG(port, TC_FLAGS_VCONN_ON) && - !TC_CHK_FLAG(port, TC_FLAGS_DO_PR_SWAP)) - set_vconn(port, 0); - - /* Clear PR swap flag after checking for Vconn */ - TC_CLR_FLAG(port, TC_FLAGS_DO_PR_SWAP); -} - -#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE -/** - * DrpAutoToggle - */ -static void tc_drp_auto_toggle_entry(const int port) -{ - print_current_state(port); - - /* - * The PD_EXIT_LOW_POWER_EVENT_MASK flag may have been set - * due to a CC event. Clear it now since we haven't engaged - * low power mode. - */ - atomic_clear(task_get_event_bitmap(task_get_current()), - PD_EXIT_LOW_POWER_EVENT_MASK); - - if (drp_state[port] == PD_DRP_TOGGLE_ON) - tcpm_enable_drp_toggle(port); -} - -static void tc_drp_auto_toggle_run(const int port) -{ - enum pd_drp_next_states next_state; - enum tcpc_cc_voltage_status cc1, cc2; - - /* - * If SW decided we should be in a low power state and - * the CC lines did not change, then don't talk with the - * TCPC otherwise we might wake it up. - */ - if (IS_ENABLED(CONFIG_USB_PD_TCPC_LOW_POWER)) { - if (TC_CHK_FLAG(port, TC_FLAGS_LPM_REQUESTED) && - !TC_CHK_FLAG(port, TC_FLAGS_WAKE_FROM_LPM)) { - if (get_time().val > tc[port].low_power_time) - set_state_tc(port, TC_LOW_POWER_MODE); - return; - } - } - - /* Check for connection */ - tcpm_get_cc(port, &cc1, &cc2); - - tc[port].drp_sink_time = get_time().val; - next_state = drp_auto_toggle_next_state(&tc[port].drp_sink_time, - tc[port].power_role, drp_state[port], cc1, cc2); - - /* - * The next state is not determined just by what is - * attached, but also depends on DRP_STATE. Regardless - * of next state, if nothing is attached, then always - * request low power mode. - */ - if (IS_ENABLED(CONFIG_USB_PD_TCPC_LOW_POWER)) { - if (cc1 == TYPEC_CC_VOLT_OPEN && cc2 == TYPEC_CC_VOLT_OPEN && - !tc[port].tasks_preventing_lpm) { - TC_SET_FLAG(port, TC_FLAGS_LPM_REQUESTED); - TC_CLR_FLAG(port, TC_FLAGS_WAKE_FROM_LPM); - } - } - - switch (next_state) { - case DRP_TC_DEFAULT: - set_state_tc(port, PD_DEFAULT_STATE(port)); - break; - case DRP_TC_UNATTACHED_SNK: - set_state_tc(port, TC_UNATTACHED_SNK); - break; - case DRP_TC_UNATTACHED_SRC: - set_state_tc(port, TC_UNATTACHED_SRC); - break; - case DRP_TC_DRP_AUTO_TOGGLE: - /* - * We are staying in PD_STATE_DRP_AUTO_TOGGLE - */ - break; - } -} -#endif /* CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE */ - -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER -static void tc_low_power_mode_entry(const int port) -{ - print_current_state(port); - CPRINTS("TCPC p%d Enter Low Power Mode", port); - tcpm_enter_low_power_mode(port); - TC_SET_FLAG(port, TC_FLAGS_LPM_ENGAGED); -} - -static void tc_low_power_mode_run(const int port) -{ -#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE - if (TC_CHK_FLAG(port, TC_FLAGS_WAKE_FROM_LPM | - TC_FLAGS_POWER_STATE_CHANGE)) { - set_state_tc(port, TC_DRP_AUTO_TOGGLE); - return; - } -#endif - tc_pause_event_loop(port); -} - -static void tc_low_power_mode_exit(const int port) -{ - CPRINTS("TCPC p%d Exit Low Power Mode", port); - TC_CLR_FLAG(port, TC_FLAGS_LPM_REQUESTED | TC_FLAGS_LPM_ENGAGED | - TC_FLAGS_WAKE_FROM_LPM | TC_FLAGS_POWER_STATE_CHANGE); - reset_device_and_notify(port); - tc_start_event_loop(port); -} -#endif - - -/** - * Try.SRC - * - * Super State Entry Actions: - * Vconn Off - * Place Rp on CC - * Set power role to SOURCE - */ -#ifdef CONFIG_USB_PD_TRY_SRC -static void tc_try_src_entry(const int port) -{ - print_current_state(port); - - tc[port].cc_state = PD_CC_UNSET; - tc[port].try_wait_debounce = get_time().val + PD_T_DRP_TRY; - tc[port].timeout = get_time().val + PD_T_TRY_TIMEOUT; -} - -static void tc_try_src_run(const int port) -{ - enum tcpc_cc_voltage_status cc1, cc2; - enum pd_cc_states new_cc_state; - - /* Check for connection */ - tcpm_get_cc(port, &cc1, &cc2); - - if ((cc1 == TYPEC_CC_VOLT_RD && cc2 != TYPEC_CC_VOLT_RD) || - (cc1 != TYPEC_CC_VOLT_RD && cc2 == TYPEC_CC_VOLT_RD)) - new_cc_state = PD_CC_UFP_ATTACHED; - else - new_cc_state = PD_CC_NONE; - - /* Debounce the cc state */ - if (new_cc_state != tc[port].cc_state) { - tc[port].cc_state = new_cc_state; - tc[port].cc_debounce = get_time().val + PD_T_CC_DEBOUNCE; - } - - /* - * The port shall transition to Attached.SRC when the SRC.Rd state is - * detected on exactly one of the CC1 or CC2 pins for at least - * tTryCCDebounce. - */ - if (get_time().val > tc[port].cc_debounce) { - if (new_cc_state == PD_CC_UFP_ATTACHED) - set_state_tc(port, TC_ATTACHED_SRC); - } - - /* - * The port shall transition to TryWait.SNK after tDRPTry and the - * SRC.Rd state has not been detected and VBUS is within vSafe0V, - * or after tTryTimeout and the SRC.Rd state has not been detected. - */ - if (new_cc_state == PD_CC_NONE) { - if ((get_time().val > tc[port].try_wait_debounce && - !pd_is_vbus_present(port)) || - get_time().val > tc[port].timeout) { - set_state_tc(port, TC_TRY_WAIT_SNK); - } - } -} - -/** - * TryWait.SNK - * - * Super State Entry Actions: - * Vconn Off - * Place Rd on CC - * Set power role to SINK - */ -static void tc_try_wait_snk_entry(const int port) -{ - print_current_state(port); - - tc[port].cc_state = PD_CC_UNSET; - tc[port].try_wait_debounce = get_time().val + PD_T_CC_DEBOUNCE; -} - -static void tc_try_wait_snk_run(const int port) -{ - enum tcpc_cc_voltage_status cc1, cc2; - enum pd_cc_states new_cc_state; - - /* Check for connection */ - tcpm_get_cc(port, &cc1, &cc2); - - /* We only care about CCs being open */ - if (cc1 == TYPEC_CC_VOLT_OPEN && cc2 == TYPEC_CC_VOLT_OPEN) - new_cc_state = PD_CC_NONE; - else - new_cc_state = PD_CC_UNSET; - - /* Debounce the cc state */ - if (new_cc_state != tc[port].cc_state) { - tc[port].cc_state = new_cc_state; - tc[port].pd_debounce = get_time().val + PD_T_PD_DEBOUNCE; - } - - /* - * The port shall transition to Unattached.SNK when the state of both - * of the CC1 and CC2 pins is SNK.Open for at least tPDDebounce. - */ - if ((get_time().val > tc[port].pd_debounce) && - (new_cc_state == PD_CC_NONE)) { - set_state_tc(port, TC_UNATTACHED_SNK); - return; - } - - /* - * The port shall transition to Attached.SNK after tCCDebounce if or - * when VBUS is detected. - */ - if (get_time().val > tc[port].try_wait_debounce && - pd_is_vbus_present(port)) - set_state_tc(port, TC_ATTACHED_SNK); -} - -#endif - -#if defined(CONFIG_USB_PE_SM) -/* - * CTUnattached.SNK - */ -static void tc_ct_unattached_snk_entry(int port) -{ - print_current_state(port); - - /* - * Both CC1 and CC2 pins shall be independently terminated to - * ground through Rd. - */ - tcpm_select_rp_value(port, CONFIG_USB_PD_PULLUP); - tcpm_set_cc(port, TYPEC_CC_RD); - tc[port].cc_state = PD_CC_UNSET; - - /* Set power role to sink */ - tc_set_power_role(port, PD_ROLE_SINK); - tcpm_set_msg_header(port, tc[port].power_role, tc[port].data_role); - - /* - * The policy engine is in the disabled state. Disable PD and - * re-enable it - */ - tc[port].pd_enable = 0; - - tc[port].timeout = get_time().val + PD_POWER_SUPPLY_TURN_ON_DELAY; -} - -static void tc_ct_unattached_snk_run(int port) -{ - enum tcpc_cc_voltage_status cc1; - enum tcpc_cc_voltage_status cc2; - enum pd_cc_states new_cc_state; - - if (tc[port].timeout > 0 && get_time().val > tc[port].timeout) { - tc[port].pd_enable = 1; - tc[port].timeout = 0; - } - - if (tc[port].timeout > 0) - return; - - /* Wait until Protocol Layer is ready */ - if (!prl_is_running(port)) - return; - - /* - * Hard Reset is sent when the PE layer is disabled due to a - * CTVPD connection. - */ - if (TC_CHK_FLAG(port, TC_FLAGS_HARD_RESET)) { - TC_CLR_FLAG(port, TC_FLAGS_HARD_RESET); - /* Nothing to do. Just signal hard reset completion */ - pe_ps_reset_complete(port); - } - - /* Check for connection */ - tcpm_get_cc(port, &cc1, &cc2); - - /* We only care about CCs being open */ - if (cc1 == TYPEC_CC_VOLT_OPEN && cc2 == TYPEC_CC_VOLT_OPEN) - new_cc_state = PD_CC_NONE; - else - new_cc_state = PD_CC_UNSET; - - /* Debounce the cc state */ - if (new_cc_state != tc[port].cc_state) { - tc[port].cc_state = new_cc_state; - tc[port].cc_debounce = get_time().val + PD_T_VPDDETACH; - } - - /* - * The port shall transition to Unattached.SNK if the state of - * the CC pin is SNK.Open for tVPDDetach after VBUS is vSafe0V. - */ - if (get_time().val > tc[port].cc_debounce) { - if (new_cc_state == PD_CC_NONE && !pd_is_vbus_present(port)) { - if (IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) - pd_dfp_exit_mode(port, 0, 0); - - set_state_tc(port, TC_UNATTACHED_SNK); - return; - } - } - - /* - * The port shall transition to CTAttached.SNK when VBUS is detected. - */ - if (pd_is_vbus_present(port)) - set_state_tc(port, TC_CT_ATTACHED_SNK); -} - -/** - * CTAttached.SNK - */ -static void tc_ct_attached_snk_entry(int port) -{ - print_current_state(port); - - /* The port shall reject a VCONN swap request. */ - TC_SET_FLAG(port, TC_FLAGS_REJECT_VCONN_SWAP); -} - -static void tc_ct_attached_snk_run(int port) -{ - /* - * Hard Reset is sent when the PE layer is disabled due to a - * CTVPD connection. - */ - if (TC_CHK_FLAG(port, TC_FLAGS_HARD_RESET)) { - TC_CLR_FLAG(port, TC_FLAGS_HARD_RESET); - /* Nothing to do. Just signal hard reset completion */ - pe_ps_reset_complete(port); - } - - /* - * A port that is not in the process of a USB PD Hard Reset shall - * transition to CTUnattached.SNK within tSinkDisconnect when VBUS - * falls below vSinkDisconnect - */ - if (!pd_is_vbus_present(port)) { - set_state_tc(port, TC_CT_UNATTACHED_SNK); - return; - } - - /* - * The port shall operate in one of the Sink Power Sub-States - * and remain within the Sink Power Sub-States, until either VBUS is - * removed or a USB PD contract is established with the source. - */ - if (!pe_is_explicit_contract(port)) - sink_power_sub_states(port); -} - -static void tc_ct_attached_snk_exit(int port) -{ - /* Stop drawing power */ - sink_stop_drawing_current(port); - - TC_CLR_FLAG(port, TC_FLAGS_REJECT_VCONN_SWAP); -} -#endif /* CONFIG_USB_PE_SM */ - -/** - * Super State CC_RD - */ -static void tc_cc_rd_entry(const int port) -{ - /* Disable VCONN */ - if (IS_ENABLED(CONFIG_USBC_VCONN)) - set_vconn(port, 0); - - /* Set power role to sink */ - tc_set_power_role(port, PD_ROLE_SINK); - tcpm_set_msg_header(port, tc[port].power_role, tc[port].data_role); - - /* - * Both CC1 and CC2 pins shall be independently terminated to - * ground through Rd. - */ - tcpm_set_cc(port, TYPEC_CC_RD); -} - - -/** - * Super State CC_RP - */ -static void tc_cc_rp_entry(const int port) -{ - /* Disable VCONN */ - if (IS_ENABLED(CONFIG_USBC_VCONN)) - set_vconn(port, 0); - - /* Set power role to source */ - tc_set_power_role(port, PD_ROLE_SOURCE); - tcpm_set_msg_header(port, tc[port].power_role, tc[port].data_role); - - /* - * Both CC1 and CC2 pins shall be independently pulled - * up through Rp. - */ - tcpm_select_rp_value(port, CONFIG_USB_PD_PULLUP); - tcpm_set_cc(port, TYPEC_CC_RP); -} - -/** - * Super State CC_OPEN - */ -static void tc_cc_open_entry(const int port) -{ - /* Disable VBUS */ - pd_power_supply_reset(port); - - /* Disable VCONN */ - if (TC_CHK_FLAG(port, TC_FLAGS_VCONN_ON)) - set_vconn(port, 0); - - /* Remove terminations from CC */ - tcpm_set_cc(port, TYPEC_CC_OPEN); - - if (IS_ENABLED(CONFIG_USBC_PPC)) { - /* There is no sink connected. */ - ppc_sink_is_connected(port, 0); - - /* - * Clear the overcurrent event counter - * since we've detected a disconnect. - */ - ppc_clear_oc_event_counter(port); - } -} - -void tc_run(const int port) -{ - run_state(port, &tc[port].ctx); -} - -/** - * This function checks the current CC status of the port partner - * and returns true if the attached partner is UFP. - */ -int pd_partner_is_ufp(int port) -{ - return tc[port].cc_state == PD_CC_UFP_ATTACHED || - tc[port].cc_state == PD_CC_UFP_DEBUG_ACC || - tc[port].cc_state == PD_CC_UFP_AUDIO_ACC; -} - -int pd_is_debug_acc(int port) -{ - return tc[port].cc_state == PD_CC_UFP_DEBUG_ACC || - tc[port].cc_state == PD_CC_DFP_DEBUG_ACC; -} - -static void pd_chipset_resume(void) -{ - int i; - - for (i = 0; i < CONFIG_USB_PD_PORT_MAX_COUNT; i++) { - pd_set_dual_role(i, PD_DRP_TOGGLE_ON); - task_set_event(PD_PORT_TO_TASK_ID(i), - PD_EVENT_POWER_STATE_CHANGE, 0); - } - - CPRINTS("PD:S3->S0"); -} -DECLARE_HOOK(HOOK_CHIPSET_RESUME, pd_chipset_resume, HOOK_PRIO_DEFAULT); - -static void pd_chipset_suspend(void) -{ - int i; - - for (i = 0; i < CONFIG_USB_PD_PORT_MAX_COUNT; i++) { - pd_set_dual_role(i, PD_DRP_TOGGLE_OFF); - task_set_event(PD_PORT_TO_TASK_ID(i), - PD_EVENT_POWER_STATE_CHANGE, 0); - } - - CPRINTS("PD:S0->S3"); -} -DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, pd_chipset_suspend, HOOK_PRIO_DEFAULT); - -static void pd_chipset_startup(void) -{ - int i; - - for (i = 0; i < CONFIG_USB_PD_PORT_MAX_COUNT; i++) { - pd_set_dual_role_no_wakeup(i, PD_DRP_TOGGLE_OFF); - task_set_event(PD_PORT_TO_TASK_ID(i), - PD_EVENT_POWER_STATE_CHANGE | - PD_EVENT_UPDATE_DUAL_ROLE, - 0); - } - - CPRINTS("PD:S5->S3"); -} -DECLARE_HOOK(HOOK_CHIPSET_STARTUP, pd_chipset_startup, HOOK_PRIO_DEFAULT); - -static void pd_chipset_shutdown(void) -{ - int i; - - for (i = 0; i < CONFIG_USB_PD_PORT_MAX_COUNT; i++) { - pd_set_dual_role_no_wakeup(i, PD_DRP_FORCE_SINK); - task_set_event(PD_PORT_TO_TASK_ID(i), - PD_EVENT_POWER_STATE_CHANGE | - PD_EVENT_UPDATE_DUAL_ROLE, - 0); - } - - CPRINTS("PD:S3->S5"); -} -DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, pd_chipset_shutdown, HOOK_PRIO_DEFAULT); - - -/* - * Type-C State Hierarchy (Sub-States are listed inside the boxes) - * - * |TC_UNATTACHED ---------| - * | | - * | TC_UNATTACHED_SNK | - * | TC_UNATTACHED_SRC | - * |-----------------------| - * - * |TC_CC_RD --------------| |TC_CC_RP ------------------------| - * | | | | - * | TC_ATTACH_WAIT_SNK | | TC_ATTACH_WAIT_SRC | - * | TC_TRY_WAIT_SNK | | TC_TRY_SRC | - * | TC_DBG_ACC_SNK | | TC_UNORIENTED_DBG_ACC_SRC | - * |-----------------------| |---------------------------------| - * - * |TC_CC_OPEN -----------| - * | | - * | TC_DISABLED | - * | TC_ERROR_RECOVERY | - * |----------------------| - * - * TC_ATTACHED_SNK TC_ATTACHED_SRC TC_DRP_AUTO_TOGGLE TC_LOW_POWER_MODE - * - */ -static const struct usb_state tc_states[] = { - /* Super States */ - [TC_CC_OPEN] = { - .entry = tc_cc_open_entry, - }, - [TC_CC_RD] = { - .entry = tc_cc_rd_entry, - }, - [TC_CC_RP] = { - .entry = tc_cc_rp_entry, - }, - /* Normal States */ - [TC_DISABLED] = { - .entry = tc_disabled_entry, - .run = tc_disabled_run, - .exit = tc_disabled_exit, - .parent = &tc_states[TC_CC_OPEN], - }, - [TC_ERROR_RECOVERY] = { - .entry = tc_error_recovery_entry, - .run = tc_error_recovery_run, - .parent = &tc_states[TC_CC_OPEN], - }, - [TC_UNATTACHED_SNK] = { - .entry = tc_unattached_snk_entry, - .run = tc_unattached_snk_run, - .parent = &tc_states[TC_CC_RD], - }, - [TC_ATTACH_WAIT_SNK] = { - .entry = tc_attach_wait_snk_entry, - .run = tc_attach_wait_snk_run, - .parent = &tc_states[TC_CC_RD], - }, - [TC_ATTACHED_SNK] = { - .entry = tc_attached_snk_entry, - .run = tc_attached_snk_run, - .exit = tc_attached_snk_exit, - }, - [TC_UNORIENTED_DBG_ACC_SRC] = { - .entry = tc_unoriented_dbg_acc_src_entry, - .run = tc_unoriented_dbg_acc_src_run, - .exit = tc_unoriented_dbg_acc_src_exit, - .parent = &tc_states[TC_CC_RP], - }, - [TC_DBG_ACC_SNK] = { - .entry = tc_dbg_acc_snk_entry, - .run = tc_dbg_acc_snk_run, - .exit = tc_dbg_acc_snk_exit, - .parent = &tc_states[TC_CC_RD], - }, - [TC_UNATTACHED_SRC] = { - .entry = tc_unattached_src_entry, - .run = tc_unattached_src_run, - .parent = &tc_states[TC_CC_RP], - }, - [TC_ATTACH_WAIT_SRC] = { - .entry = tc_attach_wait_src_entry, - .run = tc_attach_wait_src_run, - .parent = &tc_states[TC_CC_RP], - }, - [TC_ATTACHED_SRC] = { - .entry = tc_attached_src_entry, - .run = tc_attached_src_run, - .exit = tc_attached_src_exit, - }, -#ifdef CONFIG_USB_PD_TRY_SRC - [TC_TRY_SRC] = { - .entry = tc_try_src_entry, - .run = tc_try_src_run, - .parent = &tc_states[TC_CC_RP], - }, - [TC_TRY_WAIT_SNK] = { - .entry = tc_try_wait_snk_entry, - .run = tc_try_wait_snk_run, - .parent = &tc_states[TC_CC_RD], - }, -#endif /* CONFIG_USB_PD_TRY_SRC */ -#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE - [TC_DRP_AUTO_TOGGLE] = { - .entry = tc_drp_auto_toggle_entry, - .run = tc_drp_auto_toggle_run, - }, -#endif /* CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE */ -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER - [TC_LOW_POWER_MODE] = { - .entry = tc_low_power_mode_entry, - .run = tc_low_power_mode_run, - .exit = tc_low_power_mode_exit, - }, -#endif /* CONFIG_USB_PD_TCPC_LOW_POWER */ -#ifdef CONFIG_USB_PE_SM - [TC_CT_UNATTACHED_SNK] = { - .entry = tc_ct_unattached_snk_entry, - .run = tc_ct_unattached_snk_run, - }, - [TC_CT_ATTACHED_SNK] = { - .entry = tc_ct_attached_snk_entry, - .run = tc_ct_attached_snk_run, - .exit = tc_ct_attached_snk_exit, - }, -#endif -}; - -#ifdef TEST_BUILD -const struct test_sm_data test_tc_sm_data[] = { - { - .base = tc_states, - .size = ARRAY_SIZE(tc_states), - .names = tc_state_names, - .names_size = ARRAY_SIZE(tc_state_names), - }, -}; -const int test_tc_sm_data_size = ARRAY_SIZE(test_tc_sm_data); -#endif diff --git a/common/usbc/usb_tc_vpd_sm.c b/common/usbc/usb_tc_vpd_sm.c deleted file mode 100644 index 8dfed4226b..0000000000 --- a/common/usbc/usb_tc_vpd_sm.c +++ /dev/null @@ -1,399 +0,0 @@ -/* Copyright 2019 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 "common.h" -#include "console.h" -#include "system.h" -#include "task.h" -#include "tcpm.h" -#include "usb_pd.h" -#include "usb_tc_sm.h" -#include "usb_sm.h" -#include "vpd_api.h" - -/* USB Type-C VCONN Powered Device module */ - -#ifdef CONFIG_COMMON_RUNTIME -#define CPRINTF(format, args...) cprintf(CC_USB, format, ## args) -#define CPRINTS(format, args...) cprints(CC_USB, format, ## args) -#else /* CONFIG_COMMON_RUNTIME */ -#define CPRINTF(format, args...) -#define CPRINTS(format, args...) -#endif - -/* Type-C Layer Flags */ -#define TC_FLAGS_VCONN_ON BIT(0) - -/** - * This is the Type-C Port object that contains information needed to - * implement a VCONN Powered Device. - */ -static struct type_c { - /* state machine context */ - struct sm_ctx ctx; - /* Higher-level power deliver state machines are enabled if true. */ - uint8_t pd_enable; - /* port flags, see TC_FLAGS_* */ - uint32_t flags; - /* Time a port shall wait before it can determine it is attached */ - uint64_t cc_debounce; - /* VPD host port cc state */ - enum pd_cc_states host_cc_state; - uint8_t ct_cc; -} tc[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/* List of all TypeC-level states */ -enum usb_tc_state { - /* Normal States */ - TC_DISABLED, - TC_UNATTACHED_SNK, - TC_ATTACH_WAIT_SNK, - TC_ATTACHED_SNK, - /* Super States */ - TC_VBUS_CC_ISO, - TC_HOST_RARD, - TC_HOST_OPEN, -}; -/* Forward declare the full list of states. This is indexed by usb_tc_state */ -static const struct usb_state tc_states[]; - -#ifdef CONFIG_COMMON_RUNTIME -/* List of human readable state names for console debugging */ -static const char * const tc_state_names[] = { - [TC_DISABLED] = "Disabled", - [TC_UNATTACHED_SNK] = "Unattached.SNK", - [TC_ATTACH_WAIT_SNK] = "AttachWait.SNK", - [TC_ATTACHED_SNK] = "Attached.SNK", -}; -#endif - -/* Forward declare private, common functions */ -static void set_state_tc(const int port, enum usb_tc_state new_state); - -/* Public TypeC functions */ - -void tc_state_init(int port) -{ - int res = 0; - - res = tc_restart_tcpc(port); - - CPRINTS("TCPC p%d init %s", port, res ? "failed" : "ready"); - - /* Disable TCPC RX until connection is established */ - tcpm_set_rx_enable(port, 0); - - set_state_tc(port, res ? TC_DISABLED : TC_UNATTACHED_SNK); - - /* Disable pd state machines */ - tc[port].pd_enable = 0; - tc[port].flags = 0; -} - -enum pd_power_role tc_get_power_role(int port) -{ - /* Vconn power device is always the sink */ - return PD_ROLE_SINK; -} - -enum pd_cable_plug tc_get_cable_plug(int port) -{ - /* Vconn power device is always the cable */ - return PD_PLUG_FROM_CABLE; -} - -enum pd_data_role tc_get_data_role(int port) -{ - /* Vconn power device doesn't have a data role, but UFP match SNK */ - return PD_ROLE_UFP; -} - -/* Note tc_set_power_role and tc_set_data_role are unimplemented */ - -uint8_t tc_get_polarity(int port) -{ - /* Does not track polarity yet */ - return 0; -} - -uint8_t tc_get_pd_enabled(int port) -{ - return tc[port].pd_enable; -} - -void tc_event_check(int port, int evt) -{ - /* Do Nothing */ -} - -/* - * Private Functions - */ - -/* Set the TypeC state machine to a new state. */ -static void set_state_tc(const int port, const enum usb_tc_state new_state) -{ - set_state(port, &tc[port].ctx, &tc_states[new_state]); -} - -/* Get the current TypeC state. */ -test_export_static enum usb_tc_state get_state_tc(const int port) -{ - return tc[port].ctx.current - &tc_states[0]; -} - -test_mockable_static void print_current_state(const int port) -{ - CPRINTS("C%d: %s", port, tc_state_names[get_state_tc(port)]); -} - -/** - * Disabled - * - * Super State Entries: - * Enable mcu communication - * Remove the terminations from Host CC - */ -static void tc_disabled_entry(const int port) -{ - print_current_state(port); -} - -static void tc_disabled_run(const int port) -{ - task_wait_event(-1); -} - -static void tc_disabled_exit(const int port) -{ - if (!IS_ENABLED(CONFIG_USB_PD_TCPC)) { - if (tc_restart_tcpc(port) != 0) { - CPRINTS("TCPC p%d restart failed!", port); - return; - } - } - - CPRINTS("TCPC p%d resumed!", port); -} - -/** - * Unattached.SNK - * - * Super State Entry: - * Enable mcu communication - * Place Ra on VCONN and Rd on Host CC - */ -static void tc_unattached_snk_entry(const int port) -{ - print_current_state(port); -} - -static void tc_unattached_snk_run(const int port) -{ - int host_cc; - - /* Check Host CC for connection */ - vpd_host_get_cc(&host_cc); - - /* - * Transition to AttachWait.SNK when a Source connection is - * detected, as indicated by the SNK.Rp state on its Host-side - * port’s CC pin. - */ - if (cc_is_rp(host_cc)) - set_state_tc(port, TC_ATTACH_WAIT_SNK); -} - -/** - * AttachedWait.SNK - * - * Super State Entry: - * Enable mcu communication - * Place Ra on VCONN and Rd on Host CC - */ -static void tc_attach_wait_snk_entry(const int port) -{ - print_current_state(port); - - /* Forces an initial debounce in run function */ - tc[port].host_cc_state = -1; -} - -static void tc_attach_wait_snk_run(const int port) -{ - int host_new_cc_state; - int host_cc; - - /* Check Host CC for connection */ - vpd_host_get_cc(&host_cc); - - if (cc_is_rp(host_cc)) - host_new_cc_state = PD_CC_DFP_ATTACHED; - else - host_new_cc_state = PD_CC_NONE; - - /* Debounce the Host CC state */ - if (tc[port].host_cc_state != host_new_cc_state) { - tc[port].host_cc_state = host_new_cc_state; - if (host_new_cc_state == PD_CC_DFP_ATTACHED) - tc[port].cc_debounce = get_time().val + - PD_T_CC_DEBOUNCE; - else - tc[port].cc_debounce = get_time().val + - PD_T_PD_DEBOUNCE; - - return; - } - - /* Wait for Host CC debounce */ - if (get_time().val < tc[port].cc_debounce) - return; - - /* - * A VCONN-Powered USB Device shall transition to - * Attached.SNK after the state of the Host-side port’s CC pin is - * SNK.Rp for at least tCCDebounce and either host-side VCONN or - * VBUS is detected. - * - * Transition to Unattached.SNK when the state of both the CC1 and - * CC2 pins is SNK.Open for at least tPDDebounce. - */ - if (tc[port].host_cc_state == PD_CC_DFP_ATTACHED && - (vpd_is_vconn_present() || vpd_is_host_vbus_present())) - set_state_tc(port, TC_ATTACHED_SNK); - else if (tc[port].host_cc_state == PD_CC_NONE) - set_state_tc(port, TC_UNATTACHED_SNK); -} - -/** - * Attached.SNK - */ -static void tc_attached_snk_entry(const int port) -{ - print_current_state(port); - - /* Enable PD */ - tc[port].pd_enable = 1; - set_polarity(port, 0); -} - -static void tc_attached_snk_run(const int port) -{ - /* Has host vbus and vconn been removed */ - if (!vpd_is_host_vbus_present() && !vpd_is_vconn_present()) { - set_state_tc(port, TC_UNATTACHED_SNK); - return; - } - - if (vpd_is_vconn_present()) { - if (!(tc[port].flags & TC_FLAGS_VCONN_ON)) { - /* VCONN detected. Remove RA */ - vpd_host_set_pull(TYPEC_CC_RD, 0); - tc[port].flags |= TC_FLAGS_VCONN_ON; - } - } -} - -static void tc_attached_snk_exit(const int port) -{ - /* Disable PD */ - tc[port].pd_enable = 0; - tc[port].flags &= ~TC_FLAGS_VCONN_ON; -} - -/** - * Super State HOST_RARD - */ -static void tc_host_rard_entry(const int port) -{ - /* Place Ra on VCONN and Rd on Host CC */ - vpd_host_set_pull(TYPEC_CC_RA_RD, 0); -} - -/** - * Super State HOST_OPEN - */ -static void tc_host_open_entry(const int port) -{ - /* Remove the terminations from Host CC */ - vpd_host_set_pull(TYPEC_CC_OPEN, 0); -} - -/** - * Super State VBUS_CC_ISO - */ -static void tc_vbus_cc_iso_entry(const int port) -{ - /* Enable mcu communication and cc */ - vpd_mcu_cc_en(1); -} - -void tc_run(const int port) -{ - run_state(port, &tc[port].ctx); -} - -/* - * Type-C State Hierarchy (Sub-States are listed inside the boxes) - * - * | TC_VBUS_CC_ISO ----------------------------------------| - * | | - * | | TC_HOST_RARD -----------| | TC_HOST_OPEN ---------| | - * | | | | | | - * | | TC_UNATTACHED_SNK | | TC_DISABLED | | - * | | TC_ATTACH_WAIT_SNK | |-----------------------| | - * | |-------------------------| | - * |--------------------------------------------------------| - * - * TC_ATTACHED_SNK - */ -static const struct usb_state tc_states[] = { - /* Super States */ - [TC_VBUS_CC_ISO] = { - .entry = tc_vbus_cc_iso_entry, - }, - [TC_HOST_RARD] = { - .entry = tc_host_rard_entry, - .parent = &tc_states[TC_VBUS_CC_ISO], - }, - [TC_HOST_OPEN] = { - .entry = tc_host_open_entry, - .parent = &tc_states[TC_VBUS_CC_ISO], - }, - /* Normal States */ - [TC_DISABLED] = { - .entry = tc_disabled_entry, - .run = tc_disabled_run, - .exit = tc_disabled_exit, - .parent = &tc_states[TC_HOST_OPEN], - }, - [TC_UNATTACHED_SNK] = { - .entry = tc_unattached_snk_entry, - .run = tc_unattached_snk_run, - .parent = &tc_states[TC_HOST_RARD], - }, - [TC_ATTACH_WAIT_SNK] = { - .entry = tc_attach_wait_snk_entry, - .run = tc_attach_wait_snk_run, - .parent = &tc_states[TC_HOST_RARD], - }, - [TC_ATTACHED_SNK] = { - .entry = tc_attached_snk_entry, - .run = tc_attached_snk_run, - .exit = tc_attached_snk_exit, - }, -}; - -#ifdef TEST_BUILD -const struct test_sm_data test_tc_sm_data[] = { - { - .base = tc_states, - .size = ARRAY_SIZE(tc_states), - .names = tc_state_names, - .names_size = ARRAY_SIZE(tc_state_names), - }, -}; -const int test_tc_sm_data_size = ARRAY_SIZE(test_tc_sm_data); -#endif diff --git a/common/usbc/usbc_task.c b/common/usbc/usbc_task.c deleted file mode 100644 index 219cf73b9a..0000000000 --- a/common/usbc/usbc_task.c +++ /dev/null @@ -1,204 +0,0 @@ -/* Copyright 2019 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 "battery.h" -#include "battery_smart.h" -#include "board.h" -#include "charge_manager.h" -#include "charge_state.h" -#include "chipset.h" -#include "common.h" -#include "console.h" -#include "ec_commands.h" -#include "gpio.h" -#include "hooks.h" -#include "host_command.h" -#include "registers.h" -#include "system.h" -#include "task.h" -#include "timer.h" -#include "util.h" -#include "usb_charge.h" -#include "usb_mux.h" -#include "usb_pd.h" -#include "usb_prl_sm.h" -#include "tcpm.h" -#include "usb_pe_sm.h" -#include "usb_prl_sm.h" -#include "usb_sm.h" -#include "usb_tc_sm.h" -#include "usbc_ppc.h" -#include "version.h" - -#define USBC_EVENT_TIMEOUT (5 * MSEC) - -static uint8_t paused; - -int tc_restart_tcpc(int port) -{ - return tcpm_init(port); -} - -void tc_pause_event_loop(int port) -{ - paused = 1; -} - -void tc_start_event_loop(int port) -{ - paused = 0; - task_set_event(PD_PORT_TO_TASK_ID(port), TASK_EVENT_WAKE, 0); -} - -void set_polarity(int port, int polarity) -{ - tcpm_set_polarity(port, polarity); - - if (IS_ENABLED(CONFIG_USBC_PPC_POLARITY)) - ppc_set_polarity(port, polarity); -} - -void set_usb_mux_with_current_data_role(int port) -{ -#ifdef CONFIG_USBC_SS_MUX - /* - * If the SoC is down, then we disconnect the MUX to save power since - * no one cares about the data lines. - */ -#ifdef CONFIG_POWER_COMMON - if (chipset_in_or_transitioning_to_state(CHIPSET_STATE_ANY_OFF)) { - usb_mux_set(port, TYPEC_MUX_NONE, USB_SWITCH_DISCONNECT, - tc_get_polarity(port)); - return; - } -#endif /* CONFIG_POWER_COMMON */ - - /* - * When PD stack is disconnected, then mux should be disconnected, which - * is also what happens in the set_state disconnection code. Once the - * PD state machine progresses out of disconnect, the MUX state will - * be set correctly again. - */ - if (!pd_is_connected(port)) - usb_mux_set(port, TYPEC_MUX_NONE, USB_SWITCH_DISCONNECT, - tc_get_polarity(port)); - /* - * If new data role isn't DFP and we only support DFP, also disconnect. - */ - else if (IS_ENABLED(CONFIG_USBC_SS_MUX_DFP_ONLY) && - tc_get_data_role(port) != PD_ROLE_DFP) - usb_mux_set(port, TYPEC_MUX_NONE, USB_SWITCH_DISCONNECT, - tc_get_polarity(port)); - /* - * Otherwise connect mux since we are in S3+ - */ - else - usb_mux_set(port, TYPEC_MUX_USB, USB_SWITCH_CONNECT, - tc_get_polarity(port)); -#endif /* CONFIG_USBC_SS_MUX */ -} - -/* High-priority interrupt tasks implementations */ -#if defined(HAS_TASK_PD_INT_C0) || defined(HAS_TASK_PD_INT_C1) || \ - defined(HAS_TASK_PD_INT_C2) - -/* Used to conditionally compile code in main pd task. */ -#define HAS_DEFFERED_INTERRUPT_HANDLER - -/* Events for pd_interrupt_handler_task */ -#define PD_PROCESS_INTERRUPT (1<<0) - -static uint8_t pd_int_task_id[CONFIG_USB_PD_PORT_MAX_COUNT]; - -void schedule_deferred_pd_interrupt(const int port) -{ - task_set_event(pd_int_task_id[port], PD_PROCESS_INTERRUPT, 0); -} - -/* - * Main task entry point that handles PD interrupts for a single port - * - * @param p The PD port number for which to handle interrupts (pointer is - * reinterpreted as an integer directly). - */ -void pd_interrupt_handler_task(void *p) -{ - const int port = (int) p; - const int port_mask = (PD_STATUS_TCPC_ALERT_0 << port); - - ASSERT(port >= 0 && port < CONFIG_USB_PD_PORT_MAX_COUNT); - - pd_int_task_id[port] = task_get_current(); - - while (1) { - const int evt = task_wait_event(-1); - - if (evt & PD_PROCESS_INTERRUPT) { - /* - * While the interrupt signal is asserted; we have more - * work to do. This effectively makes the interrupt a - * level-interrupt instead of an edge-interrupt without - * having to enable/disable a real level-interrupt in - * multiple locations. - * - * Also, if the port is disabled do not process - * interrupts. Upon existing suspend, we schedule a - * PD_PROCESS_INTERRUPT to check if we missed anything. - */ - while ((tcpc_get_alert_status() & port_mask) && - pd_is_port_enabled(port)) - tcpc_alert(port); - } - } -} -#endif /* HAS_TASK_PD_INT_C0 || HAS_TASK_PD_INT_C1 || HAS_TASK_PD_INT_C2 */ - -void pd_task(void *u) -{ - int port = TASK_ID_TO_PD_PORT(task_get_current()); - - tc_state_init(port); - - if (IS_ENABLED(CONFIG_USBC_PPC)) - ppc_init(port); - - /* - * Since most boards configure the TCPC interrupt as edge - * and it is possible that the interrupt line was asserted between init - * and calling set_state, we need to process any pending interrupts now. - * Otherwise future interrupts will never fire because another edge - * never happens. Note this needs to happen after set_state() is called. - */ - if (IS_ENABLED(HAS_DEFFERED_INTERRUPT_HANDLER)) - schedule_deferred_pd_interrupt(port); - - - while (1) { - /* wait for next event/packet or timeout expiration */ - const uint32_t evt = - task_wait_event(paused ? -1 : USBC_EVENT_TIMEOUT); - - /* handle events that affect the state machine as a whole */ - tc_event_check(port, evt); - - /* - * run port controller task to check CC and/or read incoming - * messages - */ - if (IS_ENABLED(CONFIG_USB_PD_TCPC)) - tcpc_run(port, evt); - - if (IS_ENABLED(CONFIG_USB_PE_SM)) - /* Run policy engine state machine */ - pe_run(port, evt, tc_get_pd_enabled(port)); - - if (IS_ENABLED(CONFIG_USB_PRL_SM)) - /* Run protocol state machine */ - prl_run(port, evt, tc_get_pd_enabled(port)); - - /* Run TypeC state machine */ - tc_run(port); - } -} diff --git a/common/usbc_ppc.c b/common/usbc_ppc.c deleted file mode 100644 index 816c53d06e..0000000000 --- a/common/usbc_ppc.c +++ /dev/null @@ -1,358 +0,0 @@ -/* Copyright 2017 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. - */ - -/* USB-C Power Path Controller Common Code */ - -#include "atomic.h" -#include "common.h" -#include "console.h" -#include "hooks.h" -#include "timer.h" -#include "usbc_ppc.h" -#include "util.h" - -#ifndef TEST_BUILD -#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) -#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) -#else -#define CPRINTF(args...) -#define CPRINTS(args...) -#endif - -/* - * A per-port table that indicates how many VBUS overcurrent events have - * occurred. This table is cleared after detecting a physical disconnect of the - * sink. - */ -static uint8_t oc_event_cnt_tbl[CONFIG_USB_PD_PORT_MAX_COUNT]; - -static uint32_t connected_ports; - -/* Simple wrappers to dispatch to the drivers. */ - -int ppc_init(int port) -{ - int rv = EC_ERROR_UNIMPLEMENTED; - const struct ppc_config_t *ppc; - - if ((port < 0) || (port >= ppc_cnt)) { - CPRINTS("%s(%d) Invalid port!", __func__, port); - return EC_ERROR_INVAL; - } - - ppc = &ppc_chips[port]; - if (ppc->drv->init) { - rv = ppc->drv->init(port); - if (rv) - CPRINTS("p%d: PPC init failed! (%d)", port, rv); - else - CPRINTS("p%d: PPC init'd.", port); - } - - return rv; -} - -int ppc_add_oc_event(int port) -{ - if ((port < 0) || (port >= ppc_cnt)) { - CPRINTS("%s(%d) Invalid port!", __func__, port); - return EC_ERROR_INVAL; - } - - oc_event_cnt_tbl[port]++; - - /* The port overcurrented, so don't clear it's OC events. */ - atomic_clear(&connected_ports, 1 << port); - - if (oc_event_cnt_tbl[port] >= PPC_OC_CNT_THRESH) - CPRINTS("C%d: OC event limit reached! " - "Source path disabled until physical disconnect.", - port); - return EC_SUCCESS; -} - -static void clear_oc_tbl(void) -{ - int port; - - for (port = 0; port < ppc_cnt; port++) - /* - * Only clear the table if the port partner is no longer - * attached after debouncing. - */ - if ((!(BIT(port) & connected_ports)) && - oc_event_cnt_tbl[port]) { - oc_event_cnt_tbl[port] = 0; - CPRINTS("C%d: OC events cleared", port); - } -} -DECLARE_DEFERRED(clear_oc_tbl); - -int ppc_clear_oc_event_counter(int port) -{ - if ((port < 0) || (port >= ppc_cnt)) { - CPRINTS("%s(%d) Invalid port!", __func__, port); - return EC_ERROR_INVAL; - } - - /* - * If we are clearing our event table in quick succession, we may be in - * an overcurrent loop where we are also detecting a disconnect on the - * CC pins. Therefore, let's not clear it just yet and the let the - * limit be reached. This way, we won't send the hard reset and - * actually detect the physical disconnect. - */ - if (oc_event_cnt_tbl[port]) { - hook_call_deferred(&clear_oc_tbl_data, - PPC_OC_COOLDOWN_DELAY_US); - } - return EC_SUCCESS; -} - -int ppc_is_sourcing_vbus(int port) -{ - int rv = EC_ERROR_UNIMPLEMENTED; - const struct ppc_config_t *ppc; - - if ((port < 0) || (port >= ppc_cnt)) { - CPRINTS("%s(%d) Invalid port!", __func__, port); - return EC_ERROR_INVAL; - } - - ppc = &ppc_chips[port]; - if (ppc->drv->is_sourcing_vbus) - rv = ppc->drv->is_sourcing_vbus(port); - - return rv; -} - -#ifdef CONFIG_USBC_PPC_POLARITY -int ppc_set_polarity(int port, int polarity) -{ - int rv = EC_ERROR_UNIMPLEMENTED; - const struct ppc_config_t *ppc; - - if ((port < 0) || (port >= ppc_cnt)) { - CPRINTS("%s(%d) Invalid port!", __func__, port); - return EC_ERROR_INVAL; - } - - ppc = &ppc_chips[port]; - if (ppc->drv->set_polarity) - rv = ppc->drv->set_polarity(port, polarity); - - return rv; -} -#endif - -int ppc_set_vbus_source_current_limit(int port, enum tcpc_rp_value rp) -{ - int rv = EC_ERROR_UNIMPLEMENTED; - const struct ppc_config_t *ppc; - - if ((port < 0) || (port >= ppc_cnt)) { - CPRINTS("%s(%d) Invalid port!", __func__, port); - return EC_ERROR_INVAL; - } - - ppc = &ppc_chips[port]; - if (ppc->drv->set_vbus_source_current_limit) - rv = ppc->drv->set_vbus_source_current_limit(port, rp); - - return rv; -} - -int ppc_discharge_vbus(int port, int enable) -{ - int rv = EC_ERROR_UNIMPLEMENTED; - const struct ppc_config_t *ppc; - - if ((port < 0) || (port >= ppc_cnt)) { - CPRINTS("%s(%d) Invalid port!", __func__, port); - return EC_ERROR_INVAL; - } - - ppc = &ppc_chips[port]; - if (ppc->drv->discharge_vbus) - rv = ppc->drv->discharge_vbus(port, enable); - - return rv; -} - -int ppc_is_port_latched_off(int port) -{ - if ((port < 0) || (port >= ppc_cnt)) { - CPRINTS("%s(%d) Invalid port!", __func__, port); - return EC_ERROR_INVAL; - } - - return oc_event_cnt_tbl[port] >= PPC_OC_CNT_THRESH; -} - -#ifdef CONFIG_USBC_PPC_SBU -int ppc_set_sbu(int port, int enable) -{ - int rv = EC_ERROR_UNIMPLEMENTED; - const struct ppc_config_t *ppc; - - if ((port < 0) || (port >= ppc_cnt)) { - CPRINTS("%s(%d) Invalid port!", __func__, port); - return EC_ERROR_INVAL; - } - - ppc = &ppc_chips[port]; - if (ppc->drv->set_sbu) - rv = ppc->drv->set_sbu(port, enable); - - return rv; -} -#endif /* defined(CONFIG_USBC_PPC_SBU) */ - -#ifdef CONFIG_USBC_PPC_VCONN -int ppc_set_vconn(int port, int enable) -{ - int rv = EC_ERROR_UNIMPLEMENTED; - const struct ppc_config_t *ppc; - - if ((port < 0) || (port >= ppc_cnt)) { - CPRINTS("%s(%d) Invalid port!", __func__, port); - return EC_ERROR_INVAL; - } - - /* - * Check our OC event counter. If we've exceeded our threshold, then - * let's latch our source path off to prevent continuous cycling. When - * the PD state machine detects a disconnection on the CC lines, we will - * reset our OC event counter. - */ - if (enable && ppc_is_port_latched_off(port)) - return EC_ERROR_ACCESS_DENIED; - - ppc = &ppc_chips[port]; - if (ppc->drv->set_vconn) - rv = ppc->drv->set_vconn(port, enable); - - return rv; -} -#endif - -void ppc_sink_is_connected(int port, int is_connected) -{ - if ((port < 0) || (port >= ppc_cnt)) { - CPRINTS("%s(%d) Invalid port!", __func__, port); - return; - } - - if (is_connected) - atomic_or(&connected_ports, 1 << port); - else - atomic_clear(&connected_ports, 1 << port); -} - -int ppc_vbus_sink_enable(int port, int enable) -{ - int rv = EC_ERROR_UNIMPLEMENTED; - const struct ppc_config_t *ppc; - - if ((port < 0) || (port >= ppc_cnt)) { - CPRINTS("%s(%d) Invalid port!", __func__, port); - return EC_ERROR_INVAL; - } - - ppc = &ppc_chips[port]; - if (ppc->drv->vbus_sink_enable) - rv = ppc->drv->vbus_sink_enable(port, enable); - - return rv; -} - -int ppc_enter_low_power_mode(int port) -{ - int rv = EC_ERROR_UNIMPLEMENTED; - const struct ppc_config_t *ppc; - - if ((port < 0) || (port >= ppc_cnt)) { - CPRINTS("%s(%d) Invalid port!", __func__, port); - return EC_ERROR_INVAL; - } - - ppc = &ppc_chips[port]; - if (ppc->drv->enter_low_power_mode) - rv = ppc->drv->enter_low_power_mode(port); - - return rv; -} - -int ppc_vbus_source_enable(int port, int enable) -{ - int rv = EC_ERROR_UNIMPLEMENTED; - const struct ppc_config_t *ppc; - - if ((port < 0) || (port >= ppc_cnt)) { - CPRINTS("%s(%d) Invalid port!", __func__, port); - return EC_ERROR_INVAL; - } - - /* - * Check our OC event counter. If we've exceeded our threshold, then - * let's latch our source path off to prevent continuous cycling. When - * the PD state machine detects a disconnection on the CC lines, we will - * reset our OC event counter. - */ - if (enable && ppc_is_port_latched_off(port)) - return EC_ERROR_ACCESS_DENIED; - - ppc = &ppc_chips[port]; - if (ppc->drv->vbus_source_enable) - rv = ppc->drv->vbus_source_enable(port, enable); - - return rv; -} - -#ifdef CONFIG_USB_PD_VBUS_DETECT_PPC -int ppc_is_vbus_present(int port) -{ - int rv = EC_ERROR_UNIMPLEMENTED; - const struct ppc_config_t *ppc; - - if ((port < 0) || (port >= ppc_cnt)) { - CPRINTS("%s(%d) Invalid port!", __func__, port); - return EC_ERROR_INVAL; - } - - ppc = &ppc_chips[port]; - - if (ppc->drv->is_vbus_present) - rv = ppc->drv->is_vbus_present(port); - - return rv; -} -#endif /* defined(CONFIG_USB_PD_VBUS_DETECT_PPC) */ - -#ifdef CONFIG_CMD_PPC_DUMP -static int command_ppc_dump(int argc, char **argv) -{ - int port; - int rv = EC_ERROR_UNIMPLEMENTED; - const struct ppc_config_t *ppc; - - if (argc < 2) - return EC_ERROR_PARAM_COUNT; - - port = atoi(argv[1]); - if ((port < 0) || (port >= ppc_cnt)) { - CPRINTS("%s(%d) Invalid port!", __func__, port); - return EC_ERROR_INVAL; - } - - ppc = &ppc_chips[port]; - if (ppc->drv->reg_dump) - rv = ppc->drv->reg_dump(port); - - return rv; -} -DECLARE_CONSOLE_COMMAND(ppc_dump, command_ppc_dump, "<Type-C port>", - "dump the PPC regs"); -#endif /* defined(CONFIG_CMD_PPC_DUMP) */ diff --git a/driver/build.mk b/driver/build.mk index 020ee211a7..db6949f8ca 100644 --- a/driver/build.mk +++ b/driver/build.mk @@ -125,20 +125,6 @@ driver-$(CONFIG_TOUCHPAD_ST)+=touchpad_st.o driver-$(CONFIG_THERMISTOR)+=temp_sensor/thermistor.o driver-$(CONFIG_THERMISTOR_NCP15WB)+=temp_sensor/thermistor_ncp15wb.o -# Type-C port controller (TCPC) drivers -driver-$(CONFIG_USB_PD_TCPM_STUB)+=tcpm/stub.o -driver-$(CONFIG_USB_PD_TCPM_TCPCI)+=tcpm/tcpci.o -driver-$(CONFIG_USB_PD_TCPM_FUSB302)+=tcpm/fusb302.o -driver-$(CONFIG_USB_PD_TCPM_MT6370)+=tcpm/mt6370.o -driver-$(CONFIG_USB_PD_TCPM_ITE83XX)+=tcpm/it83xx.o -driver-$(CONFIG_USB_PD_TCPM_ANX74XX)+=tcpm/anx74xx.o -driver-$(CONFIG_USB_PD_TCPM_ANX7688)+=tcpm/anx7688.o -driver-$(CONFIG_USB_PD_TCPM_ANX7447)+=tcpm/anx7447.o -driver-$(CONFIG_USB_PD_TCPM_PS8751)+=tcpm/ps8xxx.o -driver-$(CONFIG_USB_PD_TCPM_PS8805)+=tcpm/ps8xxx.o -driver-$(CONFIG_USB_PD_TCPM_TUSB422)+=tcpm/tusb422.o -driver-$(CONFIG_USB_PD_TCPM_NCT38XX)+=tcpm/nct38xx.o - # Type-C Retimer drivers driver-$(CONFIG_USBC_RETIMER_INTEL_BB)+=retimer/bb_retimer.o driver-$(CONFIG_USBC_RETIMER_PI3DPX1207)+=retimer/pi3dpx1207.o diff --git a/driver/tcpm/anx7447.c b/driver/tcpm/anx7447.c deleted file mode 100644 index 0af495251c..0000000000 --- a/driver/tcpm/anx7447.c +++ /dev/null @@ -1,680 +0,0 @@ -/* Copyright 2018 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. - */ - -/* ANX7447 port manager */ - -#include "common.h" -#include "anx7447.h" -#include "console.h" -#include "hooks.h" -#include "tcpci.h" -#include "tcpm.h" -#include "timer.h" -#include "usb_mux.h" -#include "usb_pd.h" -#include "util.h" - -#define CPRINTS(format, args...) cprints(CC_USBCHARGE, format, ## args) -#define CPRINTF(format, args...) cprintf(CC_USBCHARGE, format, ## args) - -#define ANX7447_VENDOR_ALERT BIT(15) - -#define ANX7447_REG_STATUS 0x82 -#define ANX7447_REG_STATUS_LINK BIT(0) - -#define ANX7447_REG_HPD 0x83 -#define ANX7447_REG_HPD_HIGH BIT(0) -#define ANX7447_REG_HPD_IRQ BIT(1) -#define ANX7447_REG_HPD_ENABLE BIT(2) - -#define vsafe5v_min (3800/25) -#define vsafe0v_max (800/25) -/* - * These interface are workable while ADC is enabled, before - * calling them should make sure ec driver finished chip initilization. - */ -#define is_equal_greater_safe5v(port) \ - (((anx7447_get_vbus_voltage(port))) > vsafe5v_min) -#define is_equal_greater_safe0v(port) \ - (((anx7447_get_vbus_voltage(port))) > vsafe0v_max) - -struct anx_state { - uint16_t i2c_slave_addr_flags; -}; - -struct anx_usb_mux { - int state; -}; - -static int anx7447_mux_set(int port, mux_state_t mux_state); - -static struct anx_state anx[CONFIG_USB_PD_PORT_MAX_COUNT]; -static struct anx_usb_mux mux[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/* - * ANX7447 has two co-existence I2C slave addresses, TCPC slave address and - * SPI slave address. The registers of TCPC slave address are partly compliant - * with standard USB TCPC specification, and the registers in SPI slave - * address controls the other functions (ex, hpd_level, mux_switch, and - * so on). It can't use tcpc_read() and tcpc_write() to access SPI slave - * address because its slave address has been set as TCPC in the structure - * tcpc_config_t. - * anx7447_reg_write() and anx7447_reg_read() are implemented here to access - * ANX7447 SPI slave address. - */ -const struct anx7447_i2c_addr anx7447_i2c_addrs_flags[] = { - {AN7447_TCPC0_I2C_ADDR_FLAGS, AN7447_SPI0_I2C_ADDR_FLAGS}, - {AN7447_TCPC1_I2C_ADDR_FLAGS, AN7447_SPI1_I2C_ADDR_FLAGS}, - {AN7447_TCPC2_I2C_ADDR_FLAGS, AN7447_SPI2_I2C_ADDR_FLAGS}, - {AN7447_TCPC3_I2C_ADDR_FLAGS, AN7447_SPI3_I2C_ADDR_FLAGS} -}; - -static inline int anx7447_reg_write(int port, int reg, int val) -{ - int rv = i2c_write8(tcpc_config[port].i2c_info.port, - anx[port].i2c_slave_addr_flags, - reg, val); -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER - pd_device_accessed(port); -#endif - return rv; -} - -static inline int anx7447_reg_read(int port, int reg, int *val) -{ - int rv = i2c_read8(tcpc_config[port].i2c_info.port, - anx[port].i2c_slave_addr_flags, - reg, val); -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER - pd_device_accessed(port); -#endif - return rv; -} - -void anx7447_hpd_mode_en(int port) -{ - int reg, rv; - - rv = anx7447_reg_read(port, ANX7447_REG_HPD_CTRL_0, ®); - if (rv) - return; - - reg |= ANX7447_REG_HPD_MODE; - anx7447_reg_write(port, ANX7447_REG_HPD_CTRL_0, reg); -} - -void anx7447_hpd_output_en(int port) -{ - int reg, rv; - - rv = anx7447_reg_read(port, ANX7447_REG_HPD_DEGLITCH_H, ®); - if (rv) - return; - - reg |= ANX7447_REG_HPD_OEN; - anx7447_reg_write(port, ANX7447_REG_HPD_DEGLITCH_H, reg); -} - -void anx7447_set_hpd_level(int port, int hpd_lvl) -{ - int reg, rv; - - rv = anx7447_reg_read(port, ANX7447_REG_HPD_CTRL_0, ®); - if (rv) - return; - - if (hpd_lvl) - reg |= ANX7447_REG_HPD_OUT; - else - reg &= ~ANX7447_REG_HPD_OUT; - anx7447_reg_write(port, ANX7447_REG_HPD_CTRL_0, reg); -} - -#ifdef CONFIG_USB_PD_TCPM_ANX7447_OCM_ERASE_COMMAND -static inline void anx7447_reg_write_and(int port, int reg, int v_and) -{ - int val; - - if (!anx7447_reg_read(port, reg, &val)) - anx7447_reg_write(port, reg, (val & v_and)); -} - -static inline void anx7447_reg_write_or(int port, int reg, int v_or) -{ - int val; - - if (!anx7447_reg_read(port, reg, &val)) - anx7447_reg_write(port, reg, (val | v_or)); -} - -#define ANX7447_FLASH_DONE_TIMEOUT_US (100 * MSEC) - -static int anx7447_wait_for_flash_done(int port) -{ - timestamp_t deadline; - int rv; - int r; - - deadline.val = get_time().val + ANX7447_FLASH_DONE_TIMEOUT_US; - do { - if (timestamp_expired(deadline, NULL)) - return EC_ERROR_TIMEOUT; - rv = anx7447_reg_read(port, ANX7447_REG_R_RAM_CTRL, &r); - if (rv) - return rv; - } while (!(r & ANX7447_R_RAM_CTRL_FLASH_DONE)); - - return EC_SUCCESS; -} - -static int anx7447_flash_write_en(int port) -{ - anx7447_reg_write(port, ANX7447_REG_FLASH_INST_TYPE, - ANX7447_FLASH_INST_TYPE_WRITEENABLE); - anx7447_reg_write_or(port, ANX7447_REG_R_FLASH_RW_CTRL, - ANX7447_R_FLASH_RW_CTRL_GENERAL_INST_EN); - return anx7447_wait_for_flash_done(port); -} - -static int anx7447_flash_op_init(int port) -{ - int rv; - - anx7447_reg_write_or(port, ANX7447_REG_OCM_CTRL_0, - ANX7447_OCM_CTRL_OCM_RESET); - anx7447_reg_write_or(port, ANX7447_REG_ADDR_GPIO_CTRL_0, - ANX7447_ADDR_GPIO_CTRL_0_SPI_WP); - - rv = anx7447_flash_write_en(port); - if (rv) - return rv; - - anx7447_reg_write_and(port, ANX7447_REG_R_FLASH_STATUS_0, - ANX7447_FLASH_STATUS_SPI_STATUS_0); - anx7447_reg_write_or(port, ANX7447_REG_R_FLASH_RW_CTRL, - ANX7447_R_FLASH_RW_CTRL_WRITE_STATUS_EN); - - return anx7447_wait_for_flash_done(port); -} - -static int anx7447_flash_is_empty(int port) -{ - int r; - - anx7447_reg_read(port, ANX7447_REG_OCM_VERSION, &r); - - return ((r == 0) ? 1 : 0); -} - -static int anx7447_flash_erase_internal(int port, int write_console_if_empty) -{ - int rv; - int r; - - tcpc_read(port, TCPC_REG_COMMAND, &r); - usleep(ANX7447_DELAY_IN_US); - - if (anx7447_flash_is_empty(port) == 1) { - if (write_console_if_empty) - CPRINTS("C%d: Nothing to erase!", port); - return EC_SUCCESS; - } - CPRINTS("C%d: Erasing OCM flash...", port); - - rv = anx7447_flash_op_init(port); - if (rv) - return rv; - - usleep(ANX7447_DELAY_IN_US); - - rv = anx7447_flash_write_en(port); - if (rv) - return rv; - - anx7447_reg_write(port, ANX7447_REG_FLASH_ERASE_TYPE, - ANX7447_FLASH_ERASE_TYPE_CHIPERASE); - anx7447_reg_write_or(port, ANX7447_REG_R_FLASH_RW_CTRL, - ANX7447_R_FLASH_RW_CTRL_FLASH_ERASE_EN); - - return anx7447_wait_for_flash_done(port); -} - -int anx7447_flash_erase(int port) -{ - return anx7447_flash_erase_internal(port, - 0 /* suppress console if empty */); -} - -/* Add console command to erase OCM flash if needed. */ -static int command_anx_ocm(int argc, char **argv) -{ - char *e = NULL; - int port; - - if (argc < 2) - return EC_ERROR_PARAM_COUNT; - - /* Get port number from first parameter */ - port = strtoi(argv[1], &e, 0); - if (*e) - return EC_ERROR_PARAM1; - - if (argc > 2) { - int rv; - if (strcasecmp(argv[2], "erase")) - return EC_ERROR_PARAM2; - rv = anx7447_flash_erase_internal( - port, 1 /* write to console if empty */); - if (rv) - ccprintf("C%d: Failed to erase OCM flash (%d)\n", - port, rv); - } - - ccprintf("C%d: OCM flash is %sempty.\n", - port, anx7447_flash_is_empty(port) ? "" : "not "); - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(anx_ocm, command_anx_ocm, - "port [erase]", - "Print OCM status or erases OCM for a given port."); -#endif - -static int anx7447_init(int port) -{ - int rv, reg, i; - - ASSERT(port < CONFIG_USB_PD_PORT_MAX_COUNT); - - memset(&anx[port], 0, sizeof(struct anx_state)); - - /* - * find corresponding anx7447 SPI slave address according to - * specified TCPC slave address - */ - for (i = 0; i < ARRAY_SIZE(anx7447_i2c_addrs_flags); i++) { - if (I2C_GET_ADDR(tcpc_config[port].i2c_info.addr_flags) == - I2C_GET_ADDR( - anx7447_i2c_addrs_flags[i].tcpc_slave_addr_flags)) { - anx[port].i2c_slave_addr_flags = - anx7447_i2c_addrs_flags[i].spi_slave_addr_flags; - break; - } - } - if (!I2C_GET_ADDR(anx[port].i2c_slave_addr_flags)) { - ccprintf("TCPC I2C slave addr 0x%x is invalid for ANX7447\n", - I2C_GET_ADDR(tcpc_config[port] - .i2c_info.addr_flags)); - return EC_ERROR_UNKNOWN; - } - - - rv = tcpci_tcpm_init(port); - if (rv) - return rv; - -#ifdef CONFIG_USB_PD_TCPM_ANX7447_OCM_ERASE_COMMAND - /* Check and print OCM status to console. */ - CPRINTS("C%d: OCM flash is %sempty", - port, anx7447_flash_is_empty(port) ? "" : "not "); -#endif - - /* - * 7447 has a physical pin to detect the presence of VBUS, VBUS_SENSE - * , and 7447 has a VBUS current protection mechanism through another - * pin input VBUS_OCP. To enable VBUS OCP, OVP protection, driver needs - * to set the threshold to the registers VBUS_VOLTAGE_ALARM_HI_CFG - * (0x76 & 0x77) and VBUS_OCP_HI_THRESHOLD (0xDD &0xDE). These values - * could be customized based on different platform design. - * Disable VBUS protection here since the default values of - * VBUS_VOLTAGE_ALARM_HI_CFG and VBUS_OCP_HI_THRESHOLD are zero. - */ - rv = tcpc_read(port, ANX7447_REG_TCPC_CTRL_2, ®); - if (rv) - return rv; - reg &= ~ANX7447_REG_ENABLE_VBUS_PROTECT; - rv = tcpc_write(port, ANX7447_REG_TCPC_CTRL_2, reg); - if (rv) - return rv; - - /* ADC enable, use to monitor VBUS voltage */ - rv = tcpc_read(port, ANX7447_REG_ADC_CTRL_1, ®); - if (rv) - return rv; - reg |= ANX7447_REG_ADCFSM_EN; - rv = tcpc_write(port, ANX7447_REG_ADC_CTRL_1, reg); - if (rv) - return rv; - - /* Set VCONN OCP(Over Current Protection) threshold */ - rv = tcpc_read(port, ANX7447_REG_ANALOG_CTRL_8, ®); - if (rv) - return rv; - reg &= ~ANX7447_REG_VCONN_OCP_MASK; - reg |= ANX7447_REG_VCONN_OCP_440mA; - rv = tcpc_write(port, ANX7447_REG_ANALOG_CTRL_8, reg); - - /* Vconn SW protection time of inrush current */ - rv = tcpc_read(port, ANX7447_REG_ANALOG_CTRL_10, ®); - if (rv) - return rv; - reg &= ~ANX7447_REG_R_VCONN_PWR_PRT_INRUSH_TIME_MASK; - reg |= ANX7447_REG_R_VCONN_PWR_PRT_INRUSH_TIME_2430US; - rv = tcpc_write(port, ANX7447_REG_ANALOG_CTRL_10, reg); - -#ifdef CONFIG_USB_PD_TCPM_MUX - /* - * Run mux_set() here for considering CCD(Case-Closed Debugging) case - * If this TCPC is not also the MUX then don't initialize to NONE - */ - if (!(usb_muxes[port].flags & USB_MUX_FLAG_NOT_TCPC)) - rv |= anx7447_mux_set(port, TYPEC_MUX_NONE); -#endif /* CONFIG_USB_PD_TCPM_MUX */ - - return rv; -} - -static int anx7447_release(int port) -{ - return EC_SUCCESS; -} - -static void anx7447_update_hpd_enable(int port) -{ - int status, reg, rv; - - rv = tcpc_read(port, ANX7447_REG_STATUS, &status); - rv |= tcpc_read(port, ANX7447_REG_HPD, ®); - if (rv) - return; - - if (!(reg & ANX7447_REG_HPD_ENABLE) || - !(status & ANX7447_REG_STATUS_LINK)) { - reg &= ~ANX7447_REG_HPD_IRQ; - tcpc_write(port, ANX7447_REG_HPD, - (status & ANX7447_REG_STATUS_LINK) - ? reg | ANX7447_REG_HPD_ENABLE - : reg & ~ANX7447_REG_HPD_ENABLE); - } -} - -#ifdef CONFIG_USB_PD_VBUS_DETECT_TCPC -static int anx7447_get_vbus_voltage(int port) -{ - int vbus_volt = 0; - - tcpc_read16(port, TCPC_REG_VBUS_VOLTAGE, &vbus_volt); - - return vbus_volt; -} - -int anx7447_set_power_supply_ready(int port) -{ - int count = 0; - - while (is_equal_greater_safe0v(port)) { - if (count >= 10) - break; - msleep(100); - count++; - } - - return tcpc_write(port, TCPC_REG_COMMAND, 0x77); -} -#endif /* CONFIG_USB_PD_VBUS_DETECT_TCPC */ - -int anx7447_power_supply_reset(int port) -{ - return tcpc_write(port, TCPC_REG_COMMAND, 0x66); -} - -int anx7447_board_charging_enable(int port, int enable) -{ - return tcpc_write(port, TCPC_REG_COMMAND, enable ? 0x55 : 0x44); -} - -static void anx7447_tcpc_alert(int port) -{ - int alert, rv; - - rv = tcpc_read16(port, TCPC_REG_ALERT, &alert); - /* process and clear alert status */ - tcpci_tcpc_alert(port); - - if (!rv && (alert & ANX7447_VENDOR_ALERT)) - anx7447_update_hpd_enable(port); -} - -/* - * timestamp of the next possible toggle to ensure the 2-ms spacing - * between IRQ_HPD. - */ -static uint64_t hpd_deadline[CONFIG_USB_PD_PORT_MAX_COUNT]; - -void anx7447_tcpc_update_hpd_status(int port, int hpd_lvl, int hpd_irq) -{ - int reg = 0; - - /* - * All calls within this method need to update to a mux_read/write calls - * that use the secondary address. This is a non-trival change and no - * one is using the anx7447 as a mux only (and probably never will since - * it doesn't have a re-driver). If that changes, we need to update this - * code. - */ - ASSERT(!(usb_muxes[port].flags & USB_MUX_FLAG_NOT_TCPC)); - - anx7447_set_hpd_level(port, hpd_lvl); - - if (hpd_irq) { - uint64_t now = get_time().val; - /* wait for the minimum spacing between IRQ_HPD if needed */ - if (now < hpd_deadline[port]) - usleep(hpd_deadline[port] - now); - - anx7447_reg_read(port, ANX7447_REG_HPD_CTRL_0, ®); - reg &= ~ANX7447_REG_HPD_OUT; - anx7447_reg_write(port, ANX7447_REG_HPD_CTRL_0, reg); - usleep(HPD_DSTREAM_DEBOUNCE_IRQ); - reg |= ANX7447_REG_HPD_OUT; - anx7447_reg_write(port, ANX7447_REG_HPD_CTRL_0, reg); - } - /* enforce 2-ms delay between HPD pulses */ - hpd_deadline[port] = get_time().val + HPD_USTREAM_DEBOUNCE_LVL; -} - -void anx7447_tcpc_clear_hpd_status(int port) -{ - anx7447_hpd_output_en(port); - anx7447_set_hpd_level(port, 0); -} - -#ifdef CONFIG_USB_PD_TCPM_MUX -static int anx7447_mux_init(int port) -{ - ASSERT(port < CONFIG_USB_PD_PORT_MAX_COUNT); - - memset(&mux[port], 0, sizeof(struct anx_usb_mux)); - - /* init hpd status */ - anx7447_hpd_mode_en(port); - anx7447_set_hpd_level(port, 0); - anx7447_hpd_output_en(port); - - /* - * ANX initializes its muxes to (MUX_USB_ENABLED | MUX_DP_ENABLED) - * when reinitialized, we need to force initialize it to - * TYPEC_MUX_NONE - */ - return anx7447_mux_set(port, TYPEC_MUX_NONE); -} - -#ifdef CONFIG_USB_PD_TCPM_ANX7447_AUX_PU_PD -static void anx7447_mux_safemode(int port, int on_off) -{ - int reg; - - mux_read(port, ANX7447_REG_ANALOG_CTRL_9, ®); - - if (on_off) - reg |= ANX7447_REG_SAFE_MODE; - else - reg &= ~(ANX7447_REG_SAFE_MODE); - - mux_write(port, ANX7447_REG_ANALOG_CTRL_9, reg); - CPRINTS("C%d set mux to safemode %s, reg = 0x%x", - port, (on_off) ? "on" : "off", reg); -} - -static inline void anx7447_configure_aux_src(int port, int on_off) -{ - int reg; - - mux_read(port, ANX7447_REG_ANALOG_CTRL_9, ®); - - if (on_off) - reg |= ANX7447_REG_R_AUX_RES_PULL_SRC; - else - reg &= ~(ANX7447_REG_R_AUX_RES_PULL_SRC); - - mux_write(port, ANX7447_REG_ANALOG_CTRL_9, reg); - - CPRINTS("C%d set aux_src to %s, reg = 0x%x", - port, (on_off) ? "on" : "off", reg); -} -#endif - -/* - * Set mux. - * - * sstx and ssrx are the USB superspeed transmit and receive pairs. ml is the - * DisplayPort Main Link. There are four lanes total. For example, DP cases - * connect them all and dock cases connect 2 DP and USB. - * - * a2, a3, a10, a11, b2, b3, b10, b11 are pins on the USB-C connector. - */ -static int anx7447_mux_set(int port, mux_state_t mux_state) -{ - int cc_direction; - mux_state_t mux_type; - int sw_sel = 0x00, aux_sw = 0x00; - int rv; - - cc_direction = mux_state & MUX_POLARITY_INVERTED; - mux_type = mux_state & TYPEC_MUX_DOCK; - CPRINTS("C%d mux_state = 0x%x, mux_type = 0x%x", - port, mux_state, mux_type); - if (cc_direction == 0) { - /* cc1 connection */ - if (mux_type == TYPEC_MUX_DOCK) { - /* ml0-a10/11, ml1-b2/b3, sstx-a2/a3, ssrx-b10/11 */ - sw_sel = 0x21; - /* aux+ <-> sbu1, aux- <-> sbu2 */ - aux_sw = 0x03; - } else if (mux_type == TYPEC_MUX_DP) { - /* ml0-a10/11, ml1-b2/b3, ml2-a2/a3, ml3-b10/11 */ - sw_sel = 0x09; - /* aux+ <-> sbu1, aux- <-> sbu2 */ - aux_sw = 0x03; - } else if (mux_type == TYPEC_MUX_USB) { - /* ssrxp<->b11, ssrxn<->b10, sstxp<->a2, sstxn<->a3 */ - sw_sel = 0x20; - } - } else { - /* cc2 connection */ - if (mux_type == TYPEC_MUX_DOCK) { - /* ml0-b10/11, ml1-a2/b3, sstx-b2/a3, ssrx-a10/11 */ - sw_sel = 0x12; - /* aux+ <-> sbu2, aux- <-> sbu1 */ - aux_sw = 0x0C; - } else if (mux_type == TYPEC_MUX_DP) { - /* ml0-b10/11, ml1-a2/b3, ml2-b2/a3, ml3-a10/11 */ - sw_sel = 0x06; - /* aux+ <-> sbu2, aux- <-> sbu1 */ - aux_sw = 0x0C; - } else if (mux_type == TYPEC_MUX_USB) { - /* ssrxp<->a11, ssrxn<->a10, sstxp<->b2, sstxn<->b3 */ - sw_sel = 0x10; - } - } - - /* - * Once need to configure the Mux, should set the mux to safe mode - * first. After the mux configured, should set mux to normal mode. - */ -#ifdef CONFIG_USB_PD_TCPM_ANX7447_AUX_PU_PD - anx7447_mux_safemode(port, 1); -#endif - rv = mux_write(port, ANX7447_REG_TCPC_SWITCH_0, sw_sel); - rv |= mux_write(port, ANX7447_REG_TCPC_SWITCH_1, sw_sel); - rv |= mux_write(port, ANX7447_REG_TCPC_AUX_SWITCH, aux_sw); - - mux[port].state = mux_state; - -#ifdef CONFIG_USB_PD_TCPM_ANX7447_AUX_PU_PD - /* - * DP and Dock mode: after configured the Mux, change the Mux to - * normal mode, otherwise: keep safe mode. - */ - if (mux_type != TYPEC_MUX_NONE) { - anx7447_configure_aux_src(port, 1); - anx7447_mux_safemode(port, 0); - } else - anx7447_configure_aux_src(port, 0); -#endif - - return rv; -} - -/* current mux state */ -static int anx7447_mux_get(int port, mux_state_t *mux_state) -{ - *mux_state = mux[port].state; - - return EC_SUCCESS; -} -#endif /* CONFIG_USB_PD_TCPM_MUX */ - -/* ANX7447 is a TCPCI compatible port controller */ -const struct tcpm_drv anx7447_tcpm_drv = { - .init = &anx7447_init, - .release = &anx7447_release, - .get_cc = &tcpci_tcpm_get_cc, -#ifdef CONFIG_USB_PD_VBUS_DETECT_TCPC - .get_vbus_level = &tcpci_tcpm_get_vbus_level, -#endif - .select_rp_value = &tcpci_tcpm_select_rp_value, - .set_cc = &tcpci_tcpm_set_cc, - .set_polarity = &tcpci_tcpm_set_polarity, - .set_vconn = &tcpci_tcpm_set_vconn, - .set_msg_header = &tcpci_tcpm_set_msg_header, - .set_rx_enable = &tcpci_tcpm_set_rx_enable, - .get_message_raw = &tcpci_tcpm_get_message_raw, - .transmit = &tcpci_tcpm_transmit, - .tcpc_alert = &anx7447_tcpc_alert, -#ifdef CONFIG_USB_PD_DISCHARGE_TCPC - .tcpc_discharge_vbus = &tcpci_tcpc_discharge_vbus, -#endif -#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE - .drp_toggle = &tcpci_tcpc_drp_toggle, -#endif - .get_chip_info = &tcpci_get_chip_info, -#ifdef CONFIG_USBC_PPC - .set_snk_ctrl = &tcpci_tcpm_set_snk_ctrl, - .set_src_ctrl = &tcpci_tcpm_set_src_ctrl, -#endif -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER - .enter_low_power_mode = &tcpci_enter_low_power_mode, -#endif -}; - -#ifdef CONFIG_USB_PD_TCPM_MUX -const struct usb_mux_driver anx7447_usb_mux_driver = { - .init = anx7447_mux_init, - .set = anx7447_mux_set, - .get = anx7447_mux_get, -}; -#endif /* CONFIG_USB_PD_TCPM_MUX */ - diff --git a/driver/tcpm/anx7447.h b/driver/tcpm/anx7447.h deleted file mode 100644 index 4da300884c..0000000000 --- a/driver/tcpm/anx7447.h +++ /dev/null @@ -1,141 +0,0 @@ -/* Copyright 2018 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. - */ - -/* USB Power delivery port management */ - -#ifndef __CROS_EC_USB_PD_TCPM_ANX7447_H -#define __CROS_EC_USB_PD_TCPM_ANX7447_H - -/* Registers: TCPC slave address used */ -#define ANX7447_REG_TCPC_SWITCH_0 0xB4 -#define ANX7447_REG_TCPC_SWITCH_1 0xB5 -#define ANX7447_REG_TCPC_AUX_SWITCH 0xB6 - -#define ANX7447_REG_INTR_ALERT_MASK_0 0xC9 - -#define ANX7447_REG_TCPC_CTRL_2 0xCD -#define ANX7447_REG_ENABLE_VBUS_PROTECT 0x20 - -#define ANX7447_REG_ADC_CTRL_1 0xBF -#define ANX7447_REG_ADCFSM_EN 0x20 - -/* Registers: SPI slave address used */ -#define ANX7447_REG_HPD_CTRL_0 0x7E -#define ANX7447_REG_HPD_MODE 0x01 -#define ANX7447_REG_HPD_OUT 0x02 - -#define ANX7447_REG_HPD_DEGLITCH_H 0x80 -#define ANX7447_REG_HPD_OEN 0x40 - -#define ANX7447_REG_INTP_CTRL_0 0x9E - -#define ANX7447_REG_ANALOG_CTRL_8 0xA8 -#define ANX7447_REG_VCONN_OCP_MASK 0x0C -#define ANX7447_REG_VCONN_OCP_240mA 0x00 -#define ANX7447_REG_VCONN_OCP_310mA 0x04 -#define ANX7447_REG_VCONN_OCP_370mA 0x08 -#define ANX7447_REG_VCONN_OCP_440mA 0x0C - -#define ANX7447_REG_ANALOG_CTRL_10 0xAA -#define ANX7447_REG_R_VCONN_PWR_PRT_INRUSH_TIME_MASK 0x38 -#define ANX7447_REG_R_VCONN_PWR_PRT_INRUSH_TIME_19US 0x00 -#define ANX7447_REG_R_VCONN_PWR_PRT_INRUSH_TIME_38US 0x08 -#define ANX7447_REG_R_VCONN_PWR_PRT_INRUSH_TIME_76US 0x10 -#define ANX7447_REG_R_VCONN_PWR_PRT_INRUSH_TIME_152US 0x18 -#define ANX7447_REG_R_VCONN_PWR_PRT_INRUSH_TIME_303US 0x20 -#define ANX7447_REG_R_VCONN_PWR_PRT_INRUSH_TIME_607US 0x28 -#define ANX7447_REG_R_VCONN_PWR_PRT_INRUSH_TIME_1210US 0x30 -#define ANX7447_REG_R_VCONN_PWR_PRT_INRUSH_TIME_2430US 0x38 - -#define ANX7447_REG_ANALOG_CTRL_9 0xA9 -#define ANX7447_REG_SAFE_MODE 0x80 -#define ANX7447_REG_R_AUX_RES_PULL_SRC 0x20 - -/* - * This section of defines are only required to support the config option - * CONFIG_USB_PD_TCPM_ANX7447_OCM_ERASE_COMMAND. - */ -/* SPI registers used for OCM flash operations */ -#define ANX7447_DELAY_IN_US (20*1000) - -#define ANX7447_REG_R_RAM_CTRL 0x05 -#define ANX7447_REG_R_FLASH_RW_CTRL 0x30 -#define ANX7447_REG_R_FLASH_STATUS_0 0x31 -#define ANX7447_REG_FLASH_INST_TYPE 0x33 -#define ANX7447_REG_FLASH_ERASE_TYPE 0x34 -#define ANX7447_REG_OCM_CTRL_0 0x6E -#define ANX7447_REG_ADDR_GPIO_CTRL_0 0x88 -#define ANX7447_REG_OCM_VERSION 0xB4 - -/* R_RAM_CTRL bit definitions */ -#define ANX7447_R_RAM_CTRL_FLASH_DONE (1<<7) - -/* R_FLASH_RW_CTRL bit definitions */ -#define ANX7447_R_FLASH_RW_CTRL_GENERAL_INST_EN (1<<6) -#define ANX7447_R_FLASH_RW_CTRL_FLASH_ERASE_EN (1<<5) -#define ANX7447_R_FLASH_RW_CTRL_WRITE_STATUS_EN (1<<2) -#define ANX7447_R_FLASH_RW_CTRL_FLASH_READ (1<<1) -#define ANX7447_R_FLASH_RW_CTRL_FLASH_WRITE (1<<0) - -/* R_FLASH_STATUS_0 definitions */ -#define ANX7447_FLASH_STATUS_SPI_STATUS_0 0x43 - -/* FLASH_ERASE_TYPE bit definitions */ -#define ANX7447_FLASH_INST_TYPE_WRITEENABLE 0x06 -#define ANX7447_FLASH_ERASE_TYPE_CHIPERASE 0x60 - -/* OCM_CTRL_0 bit definitions */ -#define ANX7447_OCM_CTRL_OCM_RESET (1<<6) - -/* ADDR_GPIO_CTRL_0 bit definitions */ -#define ANX7447_ADDR_GPIO_CTRL_0_SPI_WP (1<<7) -#define ANX7447_ADDR_GPIO_CTRL_0_SPI_CLK_ENABLE (1<<6) -/* End of defines used for CONFIG_USB_PD_TCPM_ANX7447_OCM_ERASE_COMMAND */ - -struct anx7447_i2c_addr { - uint16_t tcpc_slave_addr_flags; - uint16_t spi_slave_addr_flags; -}; - -#define AN7447_TCPC0_I2C_ADDR_FLAGS 0x2C -#define AN7447_TCPC1_I2C_ADDR_FLAGS 0x2B -#define AN7447_TCPC2_I2C_ADDR_FLAGS 0x2A -#define AN7447_TCPC3_I2C_ADDR_FLAGS 0x29 - -#define AN7447_SPI0_I2C_ADDR_FLAGS 0x3F -#define AN7447_SPI1_I2C_ADDR_FLAGS 0x37 -#define AN7447_SPI2_I2C_ADDR_FLAGS 0x32 -#define AN7447_SPI3_I2C_ADDR_FLAGS 0x31 - -/* - * Time TEST_R must be held high for a reset - */ -#define ANX74XX_RESET_HOLD_MS 1 -/* - * Time after TEST_R reset to wait for eFuse loading - */ -#define ANX74XX_RESET_FINISH_MS 2 - -int anx7447_set_power_supply_ready(int port); -int anx7447_power_supply_reset(int port); -int anx7447_board_charging_enable(int port, int enable); - -void anx7447_hpd_mode_en(int port); -void anx7447_hpd_output_en(int port); - -extern const struct tcpm_drv anx7447_tcpm_drv; -extern const struct usb_mux_driver anx7447_usb_mux_driver; -void anx7447_tcpc_update_hpd_status(int port, int hpd_lvl, int hpd_irq); -void anx7447_tcpc_clear_hpd_status(int port); - -/** - * Erase OCM flash if it's not empty - * - * @param port: USB-C port number - * @return: EC_SUCCESS or EC_ERROR_* - */ -int anx7447_flash_erase(int port); - -#endif /* __CROS_EC_USB_PD_TCPM_ANX7688_H */ diff --git a/driver/tcpm/anx74xx.c b/driver/tcpm/anx74xx.c deleted file mode 100644 index 0e7ec021d8..0000000000 --- a/driver/tcpm/anx74xx.c +++ /dev/null @@ -1,1180 +0,0 @@ -/* Copyright 2016 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. - * - * Author : Analogix Semiconductor. - */ - -/* Type-C port manager for Analogix's anx74xx chips */ - -#include "console.h" -#include "anx74xx.h" -#include "task.h" -#include "tcpci.h" -#include "tcpm.h" -#include "timer.h" -#include "usb_charge.h" -#include "usb_mux.h" -#include "usb_pd.h" -#include "usb_pd_tcpc.h" -#include "util.h" - -#if !defined(CONFIG_USB_PD_TCPM_TCPCI) -#error "ANX74xx is using part of standard TCPCI control" -#error "Please upgrade your board configuration" -#endif - -#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) -#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) - -struct anx_state { - int polarity; - int vconn_en; - int mux_state; -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER - int prev_mode; -#endif -}; -#define clear_recvd_msg_int(port) do {\ - int reg, rv; \ - rv = tcpc_read(port, ANX74XX_REG_RECVD_MSG_INT, ®); \ - if (!rv) \ - tcpc_write(port, ANX74XX_REG_RECVD_MSG_INT, \ - reg | 0x01); \ - } while (0) - -static struct anx_state anx[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/* Save the selected rp value */ -static int selected_rp[CONFIG_USB_PD_PORT_MAX_COUNT]; - -#ifdef CONFIG_USB_PD_DECODE_SOP -/* Save the message address */ -static int msg_sop[CONFIG_USB_PD_PORT_MAX_COUNT]; -#endif - -static int anx74xx_tcpm_init(int port); - -static void anx74xx_tcpm_set_auto_good_crc(int port, int enable) -{ - int reply_sop_en = 0; - - if (enable) { - reply_sop_en = ANX74XX_REG_REPLY_SOP_EN; -#ifdef CONFIG_USB_PD_DECODE_SOP - /* - * Only the VCONN Source is allowed to communicate - * with the Cable Plugs. - */ - if (anx[port].vconn_en) { - reply_sop_en |= ANX74XX_REG_REPLY_SOP_1_EN | - ANX74XX_REG_REPLY_SOP_2_EN; - } -#endif - } - - tcpc_write(port, ANX74XX_REG_TX_AUTO_GOODCRC_2, reply_sop_en); -} - -static void anx74xx_update_cable_det(int port, int mode) -{ -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER - int reg; - - if (anx[port].prev_mode == mode) - return; - - /* Update power mode */ - anx[port].prev_mode = mode; - - /* Get ANALOG_CTRL_0 for cable det bit */ - if (tcpc_read(port, ANX74XX_REG_ANALOG_CTRL_0, ®)) - return; - - if (mode == ANX74XX_STANDBY_MODE) { - int cc_reg; - - /* - * The ANX4329 enters standby mode by setting PWR_EN signal - * low. In addition, RESET_L must be set low to keep the ANX3429 - * in standby mode. - * - * Clearing bit 7 of ANX74XX_REG_ANALOG_CTRL_0 will cause the - * ANX3429 to clear the cable_det signal that goes from the - * ANX3429 to the EC. If this bit is cleared when a cable is - * attached then cable_det will go high once standby is entered. - * - * In some cases, such as when the chipset power state is - * S3/S5/G3 and a sink only adapter is connected to the port, - * this behavior is undesirable. The constant toggling between - * standby and normal mode means that effectively the ANX3429 is - * not in standby mode only consumes ~1 mW less than just - * remaining in normal mode. However when an E mark cable is - * connected, clearing bit 7 is required so that while the E - * mark cable configuration happens, the USB PD state machine - * will continue to wake up until the USB PD attach event can be - * regtistered. - * - * Therefore, the decision to clear bit 7 is based on the - * current CC status of the port. If the CC status is open for - * both CC lines OR if either CC line is showing Ra, then clear - * bit 7. Not clearing bit 7 has no impact for normal cables and - * prevents the constant toggle of standby<->normal when an - * adapter is connected that isn't allowed to attach. Clearing - * bit 7 when CC status reads Ra for either CC line allows the - * USB PD state machine to be woken until the attach event can - * happen. Note that in the case an E mark cable is connected - * and can't attach (i.e. sink only port <- Emark cable -> sink - * only adapter), then the ANX3429 will toggle indefinitely, - * until either the cable is removed, or the port drp status - * changes so the attach event can occur. - * . - */ - - /* Read CC status to see if cable_det bit should be cleared */ - if (tcpc_read(port, ANX74XX_REG_CC_STATUS, &cc_reg)) - return; - /* If open or either CC line is Ra, then clear cable_det */ - if (!cc_reg || (cc_reg & ANX74XX_CC_RA_MASK && - !(cc_reg & ANX74XX_CC_RD_MASK))) - reg &= ~ANX74XX_REG_R_PIN_CABLE_DET; - } else { - reg |= ANX74XX_REG_R_PIN_CABLE_DET; - } - - tcpc_write(port, ANX74XX_REG_ANALOG_CTRL_0, reg); -#endif -} - -static void anx74xx_set_power_mode(int port, int mode) -{ - /* - * Update PWR_EN and RESET_N signals to the correct level. High for - * Normal mode and low for Standby mode. When transitioning from standby - * to normal mode, must set the PWR_EN and RESET_N before attempting to - * modify cable_det bit of analog_ctrl_0. If going from Normal to - * Standby, updating analog_ctrl_0 must happen before setting PWR_EN and - * RESET_N low. - */ - if (mode == ANX74XX_NORMAL_MODE) { - /* Take chip out of standby mode */ - board_set_tcpc_power_mode(port, mode); - /* Update the cable det signal */ - anx74xx_update_cable_det(port, mode); - } else { - /* Update cable cable det signal */ - anx74xx_update_cable_det(port, mode); - /* - * Delay between setting cable_det low and setting RESET_L low - * as recommended the ANX3429 datasheet. - */ - msleep(1); - /* Put chip into standby mode */ - board_set_tcpc_power_mode(port, mode); - } -} - -#if defined(CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE) && \ - defined(CONFIG_USB_PD_TCPC_LOW_POWER) - -static int anx74xx_tcpc_drp_toggle(int port) -{ - /* - * The ANX3429 always auto-toggles when in low power mode. Since this is - * not configurable, there is nothing to do here. DRP auto-toggle will - * happen once low power mode is set via anx74xx_enter_low_power_mode(). - * Note: this means the ANX3429 auto-toggles in PD_DRP_FORCE_SINK mode, - * which is undesirable (b/72007056). - */ - return EC_SUCCESS; -} - -static int anx74xx_enter_low_power_mode(int port) -{ - anx74xx_set_power_mode(port, ANX74XX_STANDBY_MODE); - return EC_SUCCESS; -} - -#endif - -void anx74xx_tcpc_set_vbus(int port, int enable) -{ - int reg; - - tcpc_read(port, ANX74XX_REG_GPIO_CTRL_4_5, ®); - if (enable) - reg |= ANX74XX_REG_SET_VBUS; - else - reg &= ~ANX74XX_REG_SET_VBUS; - tcpc_write(port, ANX74XX_REG_GPIO_CTRL_4_5, reg); -} - -#ifdef CONFIG_USB_PD_DISCHARGE_TCPC -static void anx74xx_tcpc_discharge_vbus(int port, int enable) -{ - int reg; - - tcpc_read(port, ANX74XX_REG_HPD_CTRL_0, ®); - if (enable) - reg |= ANX74XX_REG_DISCHARGE_CTRL; - else - reg &= ~ANX74XX_REG_DISCHARGE_CTRL; - tcpc_write(port, ANX74XX_REG_HPD_CTRL_0, reg); -} -#endif - -/* - * timestamp of the next possible toggle to ensure the 2-ms spacing - * between IRQ_HPD. - */ -static uint64_t hpd_deadline[CONFIG_USB_PD_PORT_MAX_COUNT]; - -void anx74xx_tcpc_update_hpd_status(int port, int hpd_lvl, int hpd_irq) -{ - int reg; - - mux_read(port, ANX74XX_REG_HPD_CTRL_0, ®); - if (hpd_lvl) - reg |= ANX74XX_REG_HPD_OUT_DATA; - else - reg &= ~ANX74XX_REG_HPD_OUT_DATA; - mux_write(port, ANX74XX_REG_HPD_CTRL_0, reg); - - if (hpd_irq) { - uint64_t now = get_time().val; - /* wait for the minimum spacing between IRQ_HPD if needed */ - if (now < hpd_deadline[port]) - usleep(hpd_deadline[port] - now); - - mux_read(port, ANX74XX_REG_HPD_CTRL_0, ®); - reg &= ~ANX74XX_REG_HPD_OUT_DATA; - mux_write(port, ANX74XX_REG_HPD_CTRL_0, reg); - usleep(HPD_DSTREAM_DEBOUNCE_IRQ); - reg |= ANX74XX_REG_HPD_OUT_DATA; - mux_write(port, ANX74XX_REG_HPD_CTRL_0, reg); - } - /* enforce 2-ms delay between HPD pulses */ - hpd_deadline[port] = get_time().val + HPD_USTREAM_DEBOUNCE_LVL; -} - -void anx74xx_tcpc_clear_hpd_status(int port) -{ - int reg; - - tcpc_read(port, ANX74XX_REG_HPD_CTRL_0, ®); - reg &= 0xcf; - tcpc_write(port, ANX74XX_REG_HPD_CTRL_0, reg); -} - -#ifdef CONFIG_USB_PD_TCPM_MUX -static int anx74xx_tcpm_mux_init(int port) -{ - /* Nothing to do here, ANX initializes its muxes - * as (MUX_USB_ENABLED | MUX_DP_ENABLED) - */ - anx[port].mux_state = MUX_USB_ENABLED | MUX_DP_ENABLED; - - return EC_SUCCESS; -} - -static int anx74xx_tcpm_mux_enter_safe_mode(int port) -{ - int reg; - - if (mux_read(port, ANX74XX_REG_ANALOG_CTRL_2, ®)) - return EC_ERROR_UNKNOWN; - if (mux_write(port, ANX74XX_REG_ANALOG_CTRL_2, reg | - ANX74XX_REG_MODE_TRANS)) - return EC_ERROR_UNKNOWN; - - - return EC_SUCCESS; -} - -static int anx74xx_tcpm_mux_exit_safe_mode(int port) -{ - int reg; - - if (mux_read(port, ANX74XX_REG_ANALOG_CTRL_2, ®)) - return EC_ERROR_UNKNOWN; - if (mux_write(port, ANX74XX_REG_ANALOG_CTRL_2, reg & - ~ANX74XX_REG_MODE_TRANS)) - return EC_ERROR_UNKNOWN; - - - return EC_SUCCESS; -} - -static int anx74xx_tcpm_mux_exit(int port) -{ - int reg; - - /* - * Safe mode must be entered before any changes are made to the mux - * settings used to enable ALT_DP mode. This funciton is called either - * from anx74xx_tcpm_mux_set when TYPEC_MUX_NONE is selected as the new - * mux state, or when both cc lines are determined to be - * TYPEC_CC_VOLT_OPEN. Therefore, safe mode must be entered and exited - * here so that both entry paths are handled. - */ - if (anx74xx_tcpm_mux_enter_safe_mode(port)) - return EC_ERROR_UNKNOWN; - - /* Disconnect aux from sbu */ - if (mux_read(port, ANX74XX_REG_ANALOG_CTRL_2, ®)) - return EC_ERROR_UNKNOWN; - if (mux_write(port, ANX74XX_REG_ANALOG_CTRL_2, reg & 0xf)) - return EC_ERROR_UNKNOWN; - - /* Clear Bit[7:0] R_SWITCH */ - if (mux_write(port, ANX74XX_REG_ANALOG_CTRL_1, 0x0)) - return EC_ERROR_UNKNOWN; - /* Clear Bit[7:4] R_SWITCH_H */ - if (mux_read(port, ANX74XX_REG_ANALOG_CTRL_5, ®)) - return EC_ERROR_UNKNOWN; - if (mux_write(port, ANX74XX_REG_ANALOG_CTRL_5, reg & 0x0f)) - return EC_ERROR_UNKNOWN; - - /* Exit safe mode */ - if (anx74xx_tcpm_mux_exit_safe_mode(port)) - return EC_ERROR_UNKNOWN; - - return EC_SUCCESS; -} - - -static int anx74xx_mux_aux_to_sbu(int port, int polarity, int enabled) -{ - int reg; - const int aux_mask = ANX74XX_REG_AUX_SWAP_SET_CC2 | - ANX74XX_REG_AUX_SWAP_SET_CC1; - - /* - * Get the current value of analog_ctrl_2 register. Note, that safe mode - * is enabled and exited by the calling function, so only have to worry - * about setting the correct value for the upper 4 bits of analog_ctrl_2 - * here. - */ - if (mux_read(port, ANX74XX_REG_ANALOG_CTRL_2, ®)) - return EC_ERROR_UNKNOWN; - - /* Assume aux_p/n lines are not connected */ - reg &= ~aux_mask; - - if (enabled) { - /* If enabled, connect aux to sbu based on desired polarity */ - if (polarity) - reg |= ANX74XX_REG_AUX_SWAP_SET_CC2; - else - reg |= ANX74XX_REG_AUX_SWAP_SET_CC1; - } - /* Write new aux <-> sbu settings */ - if (mux_write(port, ANX74XX_REG_ANALOG_CTRL_2, reg)) - return EC_ERROR_UNKNOWN; - - return EC_SUCCESS; -} - -static int anx74xx_tcpm_mux_set(int i2c_addr, mux_state_t mux_state) -{ - int ctrl5; - int ctrl1 = 0; - int rv; - int port = i2c_addr; - - if (!(mux_state & ~MUX_POLARITY_INVERTED)) { - anx[port].mux_state = mux_state; - return anx74xx_tcpm_mux_exit(port); - } - - rv = mux_read(port, ANX74XX_REG_ANALOG_CTRL_5, &ctrl5); - if (rv) - return EC_ERROR_UNKNOWN; - ctrl5 &= 0x0f; - - if (mux_state & MUX_USB_ENABLED) { - /* Connect USB SS switches */ - if (mux_state & MUX_POLARITY_INVERTED) { - ctrl1 = ANX74XX_REG_MUX_SSRX_RX2; - ctrl5 |= ANX74XX_REG_MUX_SSTX_TX2; - } else { - ctrl1 = ANX74XX_REG_MUX_SSRX_RX1; - ctrl5 |= ANX74XX_REG_MUX_SSTX_TX1; - } - if (mux_state & MUX_DP_ENABLED) { - /* Set pin assignment D */ - if (mux_state & MUX_POLARITY_INVERTED) - ctrl1 |= (ANX74XX_REG_MUX_ML0_RX1 | - ANX74XX_REG_MUX_ML1_TX1); - else - ctrl1 |= (ANX74XX_REG_MUX_ML0_RX2 | - ANX74XX_REG_MUX_ML1_TX2); - } - /* Keep ML0/ML1 unconnected if DP is not enabled */ - } else if (mux_state & MUX_DP_ENABLED) { - /* Set pin assignment C */ - if (mux_state & MUX_POLARITY_INVERTED) { - ctrl1 = (ANX74XX_REG_MUX_ML0_RX1 | - ANX74XX_REG_MUX_ML1_TX1 | - ANX74XX_REG_MUX_ML3_RX2); - ctrl5 |= ANX74XX_REG_MUX_ML2_TX2; - } else { - ctrl1 = (ANX74XX_REG_MUX_ML0_RX2 | - ANX74XX_REG_MUX_ML1_TX2 | - ANX74XX_REG_MUX_ML3_RX1); - ctrl5 |= ANX74XX_REG_MUX_ML2_TX1; - } - } else if (!mux_state) { - return anx74xx_tcpm_mux_exit(port); - } else { - return EC_ERROR_UNIMPLEMENTED; - } - - /* - * Safe mode must be entererd prior to any changes to the mux related to - * ALT_DP mode. Therefore, first enable safe mode prior to updating the - * values for analog_ctrl_1, analog_ctrl_5, and analog_ctrl_2. - */ - if (anx74xx_tcpm_mux_enter_safe_mode(port)) - return EC_ERROR_UNKNOWN; - - /* Write updated pin assignment */ - rv = mux_write(port, ANX74XX_REG_ANALOG_CTRL_1, ctrl1); - /* Write Rswitch config bits */ - rv |= mux_write(port, ANX74XX_REG_ANALOG_CTRL_5, ctrl5); - if (rv) - return EC_ERROR_UNKNOWN; - - /* Configure DP aux to sbu settings */ - if (anx74xx_mux_aux_to_sbu(port, mux_state & MUX_POLARITY_INVERTED, - mux_state & MUX_DP_ENABLED)) - return EC_ERROR_UNKNOWN; - - /* Exit safe mode */ - if (anx74xx_tcpm_mux_exit_safe_mode(port)) - return EC_ERROR_UNKNOWN; - - anx[port].mux_state = mux_state; - - return EC_SUCCESS; -} - -/* current mux state */ -static int anx74xx_tcpm_mux_get(int port, mux_state_t *mux_state) -{ - *mux_state = anx[port].mux_state; - - return EC_SUCCESS; -} - -const struct usb_mux_driver anx74xx_tcpm_usb_mux_driver = { - .init = anx74xx_tcpm_mux_init, - .set = anx74xx_tcpm_mux_set, - .get = anx74xx_tcpm_mux_get, -}; -#endif /* CONFIG_USB_PD_TCPM_MUX */ - -static int anx74xx_init_analog(int port) -{ - int reg, rv = EC_SUCCESS; - - /* Analog settings for chip */ - rv |= tcpc_write(port, ANX74XX_REG_HPD_CONTROL, - ANX74XX_REG_HPD_OP_MODE); - rv |= tcpc_write(port, ANX74XX_REG_HPD_CTRL_0, - ANX74XX_REG_HPD_DEFAULT); - if (rv) - return rv; - rv = tcpc_read(port, ANX74XX_REG_GPIO_CTRL_4_5, ®); - if (rv) - return rv; - reg &= ANX74XX_REG_VBUS_GPIO_MODE; - reg |= ANX74XX_REG_VBUS_OP_ENABLE; - rv = tcpc_write(port, ANX74XX_REG_GPIO_CTRL_4_5, reg); - if (rv) - return rv; - rv = tcpc_read(port, ANX74XX_REG_CC_SOFTWARE_CTRL, ®); - if (rv) - return rv; - reg |= ANX74XX_REG_TX_MODE_ENABLE; - rv = tcpc_write(port, ANX74XX_REG_CC_SOFTWARE_CTRL, reg); - - return rv; -} - -static int anx74xx_send_message(int port, uint16_t header, - const uint32_t *payload, - int type, - uint8_t len) -{ - int reg, rv = EC_SUCCESS; - uint8_t *buf = NULL; - int num_retry = 0, i = 0; - /* If sending Soft_reset, clear received message */ - /* Soft Reset Message type = 1101 and Number of Data Object = 0 */ - if ((header & 0x700f) == 0x000d) { - /* - * When sending soft reset, - * the Rx buffer of ANX3429 shall be clear - */ - rv = tcpc_read(port, ANX74XX_REG_CTRL_FW, ®); - rv |= tcpc_write( - port, ANX74XX_REG_CTRL_FW, reg | CLEAR_RX_BUFFER); - if (rv) - return EC_ERROR_UNKNOWN; - tcpc_write(port, ANX74XX_REG_RECVD_MSG_INT, 0xFF); - } - /* Inform chip about message length and TX type - * type->bit-0..2, len->bit-3..7 - */ - reg = (len << 3) & 0xf8; - reg |= type & 0x07; - rv |= tcpc_write(port, ANX74XX_REG_TX_CTRL_2, reg); - if (rv) - return EC_ERROR_UNKNOWN; - - /* Enqueue Header */ - rv = tcpc_write(port, ANX74XX_REG_TX_HEADER_L, (header & 0xff)); - if (rv) - return EC_ERROR_UNKNOWN; - rv = tcpc_write(port, ANX74XX_REG_TX_HEADER_H, (header >> 8)); - if (rv) - return EC_ERROR_UNKNOWN; - /* Enqueue payload */ - if (len > 2) { - len -= 2; - buf = (uint8_t *)payload; - while (1) { - if (i < 18) - rv = tcpc_write(port, - ANX74XX_REG_TX_START_ADDR_0 + i, - *buf); - else - rv = tcpc_write(port, - ANX74XX_REG_TX_START_ADDR_1 + i - 18, - *buf); - if (rv) { - num_retry++; - } else { - buf++; - len--; - num_retry = 0; - i++; - } - if (len == 0 || num_retry >= 3) - break; - } - /* If enqueue failed, do not request anx to transmit - * messages, FIFO will get cleared in next call - * before enqueue. - * num_retry = 0, refer to success - */ - if (num_retry) - return EC_ERROR_UNKNOWN; - } - /* Request a data transmission - * This bit will be cleared by ANX after TX success - */ - rv = tcpc_read(port, ANX74XX_REG_CTRL_COMMAND, ®); - if (rv) - return EC_ERROR_UNKNOWN; - reg |= ANX74XX_REG_TX_SEND_DATA_REQ; - rv |= tcpc_write(port, ANX74XX_REG_CTRL_COMMAND, reg); - - return rv; -} - -static int anx74xx_read_pd_obj(int port, - uint8_t *buf, - int plen) -{ - int rv = EC_SUCCESS, i; - int reg, addr = ANX74XX_REG_PD_RX_DATA_OBJ; - - /* Read PD data objects from ANX */ - for (i = 0; i < plen ; i++) { - /* Register sequence changes for last two bytes, if - * plen is greater than 26 - */ - if (i == 26) - addr = ANX74XX_REG_PD_RX_DATA_OBJ_M; - rv = tcpc_read(port, addr + i, ®); - if (rv) - break; - buf[i] = reg; - } - clear_recvd_msg_int(port); - return rv; -} - -static int anx74xx_check_cc_type(int cc_reg) -{ - int cc; - - switch (cc_reg & ANX74XX_REG_CC_STATUS_MASK) { - case BIT_VALUE_OF_SRC_CC_RD: - cc = TYPEC_CC_VOLT_RD; - break; - - case BIT_VALUE_OF_SRC_CC_RA: - cc = TYPEC_CC_VOLT_RA; - break; - - case BIT_VALUE_OF_SNK_CC_DEFAULT: - cc = TYPEC_CC_VOLT_RP_DEF; - break; - - case BIT_VALUE_OF_SNK_CC_1_P_5: - cc = TYPEC_CC_VOLT_RP_1_5; - break; - - case BIT_VALUE_OF_SNK_CC_3_P_0: - cc = TYPEC_CC_VOLT_RP_3_0; - break; - - default: - /* If no bits are set, then nothing is attached */ - cc = TYPEC_CC_VOLT_OPEN; - } - - return cc; -} - -static int anx74xx_tcpm_get_cc(int port, enum tcpc_cc_voltage_status *cc1, - enum tcpc_cc_voltage_status *cc2) -{ - int rv = EC_SUCCESS; - int reg = 0; - - /* Read tcpc cc status register */ - rv |= tcpc_read(port, ANX74XX_REG_CC_STATUS, ®); - /* Check for cc1 type */ - *cc1 = anx74xx_check_cc_type(reg); - /* - * Check for cc2 type (note cc2 bits are upper 4 of cc status - * register. - */ - *cc2 = anx74xx_check_cc_type(reg >> 4); - - /* clear HPD status*/ - if (!(*cc1) && !(*cc2)) { - anx74xx_tcpc_clear_hpd_status(port); -#ifdef CONFIG_USB_PD_TCPM_MUX - anx74xx_tcpm_mux_exit(port); -#endif - } - - return EC_SUCCESS; -} - -static int anx74xx_rp_control(int port, int rp) -{ - int reg; - int rv; - - rv = tcpc_read(port, ANX74XX_REG_ANALOG_CTRL_6, ®); - if (rv) - return EC_ERROR_UNKNOWN; - - /* clear Bit[0,1] R_RP to default Rp's value */ - reg &= ~0x03; - - switch (rp) { - case TYPEC_RP_1A5: - /* Set Rp strength to 12K for presenting 1.5A */ - reg |= ANX74XX_REG_CC_PULL_RP_12K; - break; - case TYPEC_RP_3A0: - /* Set Rp strength to 4K for presenting 3A */ - reg |= ANX74XX_REG_CC_PULL_RP_4K; - break; - case TYPEC_RP_USB: - default: - /* default: Set Rp strength to 36K */ - break; - } - - return tcpc_write(port, ANX74XX_REG_ANALOG_CTRL_6, reg); -} - -static int anx74xx_tcpm_select_rp_value(int port, int rp) -{ - /* For ANX3429 cannot get cc correctly when Rp != USB_Default */ - selected_rp[port] = rp; - return EC_SUCCESS; -} - - -static int anx74xx_cc_software_ctrl(int port, int enable) -{ - int rv; - int reg; - - rv = tcpc_read(port, ANX74XX_REG_CC_SOFTWARE_CTRL, ®); - if (rv) - return EC_ERROR_UNKNOWN; - - if (enable) - reg |= ANX74XX_REG_CC_SW_CTRL_ENABLE; - else - reg &= ~ANX74XX_REG_CC_SW_CTRL_ENABLE; - - rv |= tcpc_write(port, ANX74XX_REG_CC_SOFTWARE_CTRL, reg); - return rv; -} - -static int anx74xx_tcpm_set_cc(int port, int pull) -{ - int rv = EC_SUCCESS; - int reg; - - /* Enable CC software Control */ - rv = anx74xx_cc_software_ctrl(port, 1); - if (rv) - return EC_ERROR_UNKNOWN; - - switch (pull) { - case TYPEC_CC_RP: - /* Enable Rp */ - rv |= tcpc_read(port, ANX74XX_REG_ANALOG_STATUS, ®); - if (rv) - return EC_ERROR_UNKNOWN; - reg |= ANX74XX_REG_CC_PULL_RP; - rv |= tcpc_write(port, ANX74XX_REG_ANALOG_STATUS, reg); - break; - case TYPEC_CC_RD: - /* Enable Rd */ - rv |= tcpc_read(port, ANX74XX_REG_ANALOG_STATUS, ®); - if (rv) - return EC_ERROR_UNKNOWN; - reg &= ANX74XX_REG_CC_PULL_RD; - rv |= tcpc_write(port, ANX74XX_REG_ANALOG_STATUS, reg); - break; - default: - rv = EC_ERROR_UNKNOWN; - break; - } - - return rv; -} - -static int anx74xx_tcpm_set_polarity(int port, int polarity) -{ - int reg, mux_state, rv = EC_SUCCESS; - - rv |= tcpc_read(port, ANX74XX_REG_CC_SOFTWARE_CTRL, ®); - if (polarity) /* Inform ANX to use CC2 */ - reg &= ~ANX74XX_REG_SELECT_CC1; - else /* Inform ANX to use CC1 */ - reg |= ANX74XX_REG_SELECT_CC1; - rv |= tcpc_write(port, ANX74XX_REG_CC_SOFTWARE_CTRL, reg); - - anx[port].polarity = polarity; - - /* Update mux polarity */ -#ifdef CONFIG_USB_PD_TCPM_MUX - mux_state = anx[port].mux_state & ~MUX_POLARITY_INVERTED; - if (polarity) - mux_state |= MUX_POLARITY_INVERTED; - anx74xx_tcpm_mux_set(port, mux_state); -#endif - return rv; -} - -static int anx74xx_tcpm_set_vconn(int port, int enable) -{ - int reg, rv = EC_SUCCESS; - - /* switch VCONN to Non CC line */ - rv |= tcpc_read(port, ANX74XX_REG_INTP_VCONN_CTRL, ®); - if (rv) - return EC_ERROR_UNKNOWN; - if (enable) { - if (anx[port].polarity) - reg |= ANX74XX_REG_VCONN_1_ENABLE; - else - reg |= ANX74XX_REG_VCONN_2_ENABLE; - } else { - reg &= ANX74XX_REG_VCONN_DISABLE; - } - rv |= tcpc_write(port, ANX74XX_REG_INTP_VCONN_CTRL, reg); - anx[port].vconn_en = enable; - -#ifdef CONFIG_USB_PD_DECODE_SOP - rv |= tcpc_read(port, ANX74XX_REG_TX_AUTO_GOODCRC_2, ®); - if (rv) - return EC_ERROR_UNKNOWN; - - if (reg & ANX74XX_REG_REPLY_SOP_EN) { - if (enable) { - reg |= ANX74XX_REG_REPLY_SOP_1_EN | - ANX74XX_REG_REPLY_SOP_2_EN; - } else { - reg &= ~(ANX74XX_REG_REPLY_SOP_1_EN | - ANX74XX_REG_REPLY_SOP_2_EN); - } - - tcpc_write(port, ANX74XX_REG_TX_AUTO_GOODCRC_2, reg); - } -#endif - return rv; -} - -static int anx74xx_tcpm_set_msg_header(int port, int power_role, int data_role) -{ - return tcpc_write(port, ANX74XX_REG_TX_AUTO_GOODCRC_1, - ANX74XX_REG_AUTO_GOODCRC_SET(!!data_role, !!power_role)); -} - -static int anx74xx_tcpm_set_rx_enable(int port, int enable) -{ - int reg, rv; - - rv = tcpc_read(port, ANX74XX_REG_IRQ_SOURCE_RECV_MSG_MASK, ®); - if (rv) - return rv; - if (enable) { - reg &= ~(ANX74XX_REG_IRQ_CC_MSG_INT); - anx74xx_tcpm_set_auto_good_crc(port, 1); - anx74xx_rp_control(port, selected_rp[port]); - } else { - /* Disable RX message by masking interrupt */ - reg |= (ANX74XX_REG_IRQ_CC_MSG_INT); - anx74xx_tcpm_set_auto_good_crc(port, 0); - anx74xx_rp_control(port, TYPEC_RP_USB); - } - /*When this function was call, the interrupt status shall be cleared*/ - tcpc_write(port, ANX74XX_REG_IRQ_SOURCE_RECV_MSG, 0); - - return tcpc_write(port, ANX74XX_REG_IRQ_SOURCE_RECV_MSG_MASK, reg); -} - -#ifdef CONFIG_USB_PD_VBUS_DETECT_TCPC -static int anx74xx_tcpm_get_vbus_level(int port) -{ - int reg = 0; - - tcpc_read(port, ANX74XX_REG_ANALOG_STATUS, ®); - return ((reg & ANX74XX_REG_VBUS_STATUS) ? 1 : 0); -} -#endif - -static int anx74xx_tcpm_get_message_raw(int port, uint32_t *payload, int *head) -{ - int reg; - int len; - - /* Fetch the header */ - if (tcpc_read16(port, ANX74XX_REG_PD_HEADER, ®)) { - clear_recvd_msg_int(port); - return EC_ERROR_UNKNOWN; - } - *head = reg; -#ifdef CONFIG_USB_PD_DECODE_SOP - *head |= PD_HEADER_SOP(msg_sop[port]); -#endif - - len = PD_HEADER_CNT(*head) * 4; - if (!len) { - clear_recvd_msg_int(port); - return EC_SUCCESS; - } - - /* Receive message : assuming payload have enough - * memory allocated - */ - return anx74xx_read_pd_obj(port, (uint8_t *)payload, len); -} - -static int anx74xx_tcpm_transmit(int port, enum tcpm_transmit_type type, - uint16_t header, - const uint32_t *data) -{ - uint8_t len = 0; - int ret = 0, reg = 0; - - switch (type) { - /* ANX is aware of type */ - case TCPC_TX_SOP: - case TCPC_TX_SOP_PRIME: - case TCPC_TX_SOP_PRIME_PRIME: - len = PD_HEADER_CNT(header) * 4 + 2; - ret = anx74xx_send_message(port, header, - data, type, len); - break; - case TCPC_TX_HARD_RESET: - /* Request HARD RESET */ - tcpc_read(port, ANX74XX_REG_TX_CTRL_1, ®); - reg |= ANX74XX_REG_TX_HARD_RESET_REQ; - ret = tcpc_write(port, ANX74XX_REG_TX_CTRL_1, reg); - /*After Hard Reset, TCPM shall disable goodCRC*/ - anx74xx_tcpm_set_auto_good_crc(port, 0); - break; - case TCPC_TX_CABLE_RESET: - /* Request CABLE RESET */ - tcpc_read(port, ANX74XX_REG_TX_CTRL_1, ®); - reg |= ANX74XX_REG_TX_CABLE_RESET_REQ; - ret = tcpc_write(port, ANX74XX_REG_TX_CTRL_1, reg); - break; - case TCPC_TX_BIST_MODE_2: - /* Request BIST MODE 2 */ - reg = ANX74XX_REG_TX_BIST_START - | ANX74XX_REG_TX_BIXT_FOREVER | (0x02 << 4); - ret = tcpc_write(port, ANX74XX_REG_TX_BIST_CTRL, reg); - msleep(1); - ret = tcpc_write(port, ANX74XX_REG_TX_BIST_CTRL, - reg | ANX74XX_REG_TX_BIST_ENABLE); - msleep(30); - tcpc_read(port, ANX74XX_REG_TX_BIST_CTRL, ®); - ret = tcpc_write(port, ANX74XX_REG_TX_BIST_CTRL, - reg | ANX74XX_REG_TX_BIST_STOP); - ret = tcpc_write(port, ANX74XX_REG_TX_BIST_CTRL, - reg & (~ANX74XX_REG_TX_BIST_STOP)); - ret = tcpc_write(port, ANX74XX_REG_TX_BIST_CTRL, 0); - break; - default: - return EC_ERROR_UNIMPLEMENTED; - } - - return ret; -} - -/* - * Don't let the TCPC try to pull from the RX buffer forever. We typical only - * have 1 or 2 messages waiting. - */ -#define MAX_ALLOW_FAILED_RX_READS 10 - -void anx74xx_tcpc_alert(int port) -{ - int reg; - int failed_attempts; - - /* Clear soft irq bit */ - tcpc_write(port, ANX74XX_REG_IRQ_EXT_SOURCE_3, - ANX74XX_REG_CLEAR_SOFT_IRQ); - - /* Read main alert register for pending alerts */ - reg = 0; - tcpc_read(port, ANX74XX_REG_IRQ_SOURCE_RECV_MSG, ®); - - /* Prioritize TX completion because PD state machine is waiting */ - if (reg & ANX74XX_REG_IRQ_GOOD_CRC_INT) - pd_transmit_complete(port, TCPC_TX_COMPLETE_SUCCESS); - - if (reg & ANX74XX_REG_IRQ_TX_FAIL_INT) - pd_transmit_complete(port, TCPC_TX_COMPLETE_FAILED); - - /* Pull all RX messages from TCPC into EC memory */ - failed_attempts = 0; - while (reg & ANX74XX_REG_IRQ_CC_MSG_INT) { - if (tcpm_enqueue_message(port)) - ++failed_attempts; - if (tcpc_read(port, ANX74XX_REG_IRQ_SOURCE_RECV_MSG, ®)) - ++failed_attempts; - - /* Ensure we don't loop endlessly */ - if (failed_attempts >= MAX_ALLOW_FAILED_RX_READS) { - CPRINTF("C%d Cannot consume RX buffer after %d failed " - "attempts!", port, failed_attempts); - /* - * The port is in a bad state, we don't want to consume - * all EC resources so suspend the port for a little - * while. - */ - pd_set_suspend(port, 1); - pd_deferred_resume(port); - return; - } - } - - /* Clear all pending alerts */ - tcpc_write(port, ANX74XX_REG_RECVD_MSG_INT, reg); - - if (reg & ANX74XX_REG_IRQ_CC_STATUS_INT) - /* CC status changed, wake task */ - task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_CC, 0); - - /* Read and clear extended alert register 1 */ - reg = 0; - tcpc_read(port, ANX74XX_REG_IRQ_EXT_SOURCE_1, ®); - tcpc_write(port, ANX74XX_REG_IRQ_EXT_SOURCE_1, reg); - -#ifdef CONFIG_USB_PD_DECODE_SOP - if (reg & ANX74XX_REG_EXT_SOP) - msg_sop[port] = PD_MSG_SOP; - else if (reg & ANX74XX_REG_EXT_SOP_PRIME) - msg_sop[port] = PD_MSG_SOPP; -#endif - - /* Check for Hard Reset done bit */ - if (reg & ANX74XX_REG_ALERT_TX_HARD_RESETOK) - /* ANX hardware clears the request bit */ - pd_transmit_complete(port, TCPC_TX_COMPLETE_SUCCESS); - - /* Read and clear TCPC extended alert register 2 */ - reg = 0; - tcpc_read(port, ANX74XX_REG_IRQ_EXT_SOURCE_2, ®); - tcpc_write(port, ANX74XX_REG_IRQ_EXT_SOURCE_2, reg); - -#ifdef CONFIG_USB_PD_DECODE_SOP - if (reg & ANX74XX_REG_EXT_SOP_PRIME_PRIME) - msg_sop[port] = PD_MSG_SOPPP; -#endif - - if (reg & ANX74XX_REG_EXT_HARD_RST) { - /* hard reset received */ - pd_execute_hard_reset(port); - task_wake(PD_PORT_TO_TASK_ID(port)); - } -} - -static int anx74xx_tcpm_init(int port) -{ - int rv = 0, reg; - - memset(&anx[port], 0, sizeof(struct anx_state)); - /* Bring chip in normal mode to work */ - anx74xx_set_power_mode(port, ANX74XX_NORMAL_MODE); - - /* Initialize analog section of ANX */ - rv |= anx74xx_init_analog(port); - - /* disable all interrupts */ - rv |= tcpc_write(port, ANX74XX_REG_IRQ_EXT_MASK_1, - ANX74XX_REG_CLEAR_SET_BITS); - - /* Initialize interrupt open-drain */ - rv |= tcpc_read(port, ANX74XX_REG_INTP_VCONN_CTRL, ®); - if (tcpc_config[port].flags & TCPC_FLAGS_ALERT_OD) - reg |= ANX74XX_REG_R_INTERRUPT_OPEN_DRAIN; - else - reg &= ~ANX74XX_REG_R_INTERRUPT_OPEN_DRAIN; - rv |= tcpc_write(port, ANX74XX_REG_INTP_VCONN_CTRL, reg); - - /* Initialize interrupt polarity */ - reg = tcpc_config[port].flags & TCPC_FLAGS_ALERT_ACTIVE_HIGH ? - ANX74XX_REG_IRQ_POL_HIGH : ANX74XX_REG_IRQ_POL_LOW; - rv |= tcpc_write(port, ANX74XX_REG_IRQ_STATUS, reg); - - /* unmask interrupts */ - rv |= tcpc_read(port, ANX74XX_REG_IRQ_EXT_MASK_1, ®); - reg &= (~ANX74XX_REG_ALERT_TX_MSG_ERROR); - reg &= (~ANX74XX_REG_ALERT_TX_CABLE_RESETOK); - reg &= (~ANX74XX_REG_ALERT_TX_HARD_RESETOK); - rv |= tcpc_write(port, ANX74XX_REG_IRQ_EXT_MASK_1, reg); - - rv |= tcpc_read(port, ANX74XX_REG_IRQ_EXT_MASK_2, ®); - reg &= (~ANX74XX_REG_EXT_HARD_RST); - rv |= tcpc_write(port, ANX74XX_REG_IRQ_EXT_MASK_2, reg); - - /* HPD pin output enable*/ - rv |= tcpc_write(port, ANX74XX_REG_HPD_CTRL_0, ANX74XX_REG_HPD_DEFAULT); - - if (rv) - return EC_ERROR_UNKNOWN; - - /* Set AVDD10_BMC to 1.08 */ - rv |= tcpc_read(port, ANX74XX_REG_ANALOG_CTRL_5, ®); - if (rv) - return EC_ERROR_UNKNOWN; - rv = tcpc_write(port, ANX74XX_REG_ANALOG_CTRL_5, (reg & 0xf3)); - if (rv) - return EC_ERROR_UNKNOWN; - - /* Decrease BMC TX lowest swing voltage */ - rv |= tcpc_read(port, ANX74XX_REG_ANALOG_CTRL_11, ®); - if (rv) - return EC_ERROR_UNKNOWN; - rv = tcpc_write(port, ANX74XX_REG_ANALOG_CTRL_11, (reg & 0x3f) | 0x40); - if (rv) - return EC_ERROR_UNKNOWN; - - /* Set BMC TX cap slew rate to 400ns */ - rv = tcpc_write(port, ANX74XX_REG_ANALOG_CTRL_12, 0x4); - if (rv) - return EC_ERROR_UNKNOWN; - - tcpm_get_chip_info(port, 1, NULL); - - return EC_SUCCESS; -} - -static int anx74xx_get_chip_info(int port, int live, - struct ec_response_pd_chip_info_v1 **chip_info) -{ - int rv = tcpci_get_chip_info(port, live, chip_info); - int val; - - if (rv) - return rv; - - if ((*chip_info)->fw_version_number == 0 || - (*chip_info)->fw_version_number == -1 || live) { - rv = tcpc_read(port, ANX74XX_REG_FW_VERSION, &val); - - if (rv) - return rv; - - (*chip_info)->fw_version_number = val; - } - -#ifdef CONFIG_USB_PD_TCPM_ANX3429 - /* - * Min firmware version of ANX3429 to ensure that false SOP' detection - * doesn't occur for e-marked cables. See b/116255749#comment8 and - * b/64752060#comment11 - */ - (*chip_info)->min_req_fw_version_number = 0x16; -#endif - - return rv; -} - -/* - * Dissociate from the TCPC. - */ - -static int anx74xx_tcpm_release(int port) -{ - return EC_SUCCESS; -} - -const struct tcpm_drv anx74xx_tcpm_drv = { - .init = &anx74xx_tcpm_init, - .release = &anx74xx_tcpm_release, - .get_cc = &anx74xx_tcpm_get_cc, -#ifdef CONFIG_USB_PD_VBUS_DETECT_TCPC - .get_vbus_level = &anx74xx_tcpm_get_vbus_level, -#endif - .select_rp_value = &anx74xx_tcpm_select_rp_value, - .set_cc = &anx74xx_tcpm_set_cc, - .set_polarity = &anx74xx_tcpm_set_polarity, - .set_vconn = &anx74xx_tcpm_set_vconn, - .set_msg_header = &anx74xx_tcpm_set_msg_header, - .set_rx_enable = &anx74xx_tcpm_set_rx_enable, - .get_message_raw = &anx74xx_tcpm_get_message_raw, - .transmit = &anx74xx_tcpm_transmit, - .tcpc_alert = &anx74xx_tcpc_alert, -#ifdef CONFIG_USB_PD_DISCHARGE_TCPC - .tcpc_discharge_vbus = &anx74xx_tcpc_discharge_vbus, -#endif - .get_chip_info = &anx74xx_get_chip_info, -#if defined(CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE) && \ - defined(CONFIG_USB_PD_TCPC_LOW_POWER) - .drp_toggle = &anx74xx_tcpc_drp_toggle, - .enter_low_power_mode = &anx74xx_enter_low_power_mode, -#endif -}; - -#ifdef CONFIG_CMD_I2C_STRESS_TEST_TCPC -struct i2c_stress_test_dev anx74xx_i2c_stress_test_dev = { - .reg_info = { - .read_reg = ANX74XX_REG_VENDOR_ID_L, - .read_val = ANX74XX_VENDOR_ID & 0xFF, - .write_reg = ANX74XX_REG_CC_SOFTWARE_CTRL, - }, - .i2c_read = &tcpc_i2c_read, - .i2c_write = &tcpc_i2c_write, -}; -#endif /* CONFIG_CMD_I2C_STRESS_TEST_TCPC */ diff --git a/driver/tcpm/anx74xx.h b/driver/tcpm/anx74xx.h deleted file mode 100644 index 2648013abf..0000000000 --- a/driver/tcpm/anx74xx.h +++ /dev/null @@ -1,226 +0,0 @@ -/* Copyright 2016 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. - * - * Author : Analogix Semiconductor. - */ - -/* USB Power delivery port management */ - -#ifndef __CROS_EC_USB_PD_TCPM_ANX74XX_H -#define __CROS_EC_USB_PD_TCPM_ANX74XX_H - -/* I2C interface */ -#define ANX74XX_I2C_ADDR1_FLAGS 0x28 -#define ANX74XX_I2C_ADDR2_FLAGS 0x39 -#define ANX74XX_I2C_ADDR3_FLAGS 0x3E -#define ANX74XX_I2C_ADDR4_FLAGS 0x40 - -#define ANX74XX_REG_IRQ_POL_LOW 0x00 -#define ANX74XX_REG_IRQ_POL_HIGH 0x02 - -#define ANX74XX_REG_VENDOR_ID_L 0x00 -#define ANX74XX_REG_VENDOR_ID_H 0x01 -#define ANX74XX_VENDOR_ID 0xAAAA - -/* ANX F/W version:0x50:0x44 which contains otp firmware version */ -#define ANX74XX_REG_FW_VERSION 0x44 - -#define ANX74XX_REG_IRQ_STATUS 0x53 - -#define ANX74XX_REG_INTP_VCONN_CTRL 0x33 -#define ANX74XX_REG_VCONN_DISABLE 0x0f -#define ANX74XX_REG_VCONN_1_ENABLE BIT(4) -#define ANX74XX_REG_VCONN_2_ENABLE BIT(5) -#define ANX74XX_REG_R_INTERRUPT_OPEN_DRAIN BIT(2) - -#define ANX74XX_STANDBY_MODE (0) -#define ANX74XX_NORMAL_MODE (1) - -#define ANX74XX_REG_TX_CTRL_1 0x81 -#define ANX74XX_REG_TX_HARD_RESET_REQ BIT(1) -#define ANX74XX_REG_TX_CABLE_RESET_REQ BIT(2) - -#define ANX74XX_REG_TX_CTRL_2 0x82 -#define ANX74XX_REG_TX_WR_FIFO 0x83 -#define ANX74XX_REG_TX_FIFO_CTRL 0x9a -#define ANX74XX_REG_TX_HEADER_L 0x2c -#define ANX74XX_REG_TX_HEADER_H 0x2d -#define ANX74XX_REG_TX_START_ADDR_0 0x6d -#define ANX74XX_REG_TX_START_ADDR_1 0xd0 - -#define ANX74XX_REG_CTRL_COMMAND 0xdb -#define ANX74XX_REG_TX_SEND_DATA_REQ BIT(0) -#define ANX74XX_REG_TX_HARD_RST_REQ BIT(1) - -#define ANX74XX_REG_TX_BIST_CTRL 0x9D -#define ANX74XX_REG_TX_BIST_MODE BIT(4) -#define ANX74XX_REG_TX_BIST_STOP BIT(3) -#define ANX74XX_REG_TX_BIXT_FOREVER BIT(2) -#define ANX74XX_REG_TX_BIST_ENABLE BIT(1) -#define ANX74XX_REG_TX_BIST_START BIT(0) - -#define ANX74XX_REG_PD_HEADER 0x69 -#define ANX74XX_REG_PD_RX_DATA_OBJ 0x11 -#define ANX74XX_REG_PD_RX_DATA_OBJ_M 0x4d - -#define ANX74XX_REG_ANALOG_STATUS 0x40 -#define ANX74XX_REG_VBUS_STATUS BIT(4) -#define ANX74XX_REG_CC_PULL_RD 0xfd -#define ANX74XX_REG_CC_PULL_RP 0x02 - - -#define ANX74XX_REG_TX_AUTO_GOODCRC_2 0x94 -#define ANX74XX_REG_REPLY_SOP_EN BIT(3) -#define ANX74XX_REG_REPLY_SOP_1_EN BIT(4) -#define ANX74XX_REG_REPLY_SOP_2_EN BIT(5) - -#define ANX74XX_REG_TX_AUTO_GOODCRC_1 0x9c -#define ANX74XX_REG_SPEC_REV_BIT_POS (3) -#define ANX74XX_REG_DATA_ROLE_BIT_POS (2) -#define ANX74XX_REG_PWR_ROLE_BIT_POS (1) -#define ANX74XX_REG_AUTO_GOODCRC_EN BIT(0) -#define ANX74XX_REG_AUTO_GOODCRC_SET(drole, prole) \ - ((PD_REV20 << ANX74XX_REG_SPEC_REV_BIT_POS) | \ - ((drole) << ANX74XX_REG_DATA_ROLE_BIT_POS) | \ - ((prole) << ANX74XX_REG_PWR_ROLE_BIT_POS) | \ - ANX74XX_REG_AUTO_GOODCRC_EN) - - -#define ANX74XX_REG_ANALOG_CTRL_0 0x41 -#define ANX74XX_REG_R_PIN_CABLE_DET BIT(7) - -#define ANX74XX_REG_ANALOG_CTRL_1 0x42 -#define ANX74XX_REG_ANALOG_CTRL_5 0x46 -#define ANX74XX_REG_ANALOG_CTRL_6 0x47 -#define ANX74XX_REG_CC_PULL_RP_36K 0x00 -#define ANX74XX_REG_CC_PULL_RP_12K 0x01 -#define ANX74XX_REG_CC_PULL_RP_4K 0x02 - -#define ANX74XX_REG_R_SWITCH_CC_CLR 0x0f -#define ANX74XX_REG_R_SWITCH_CC2_SET 0x10 -#define ANX74XX_REG_R_SWITCH_CC1_SET 0x20 -#define ANX74XX_REG_AUX_SWAP_SET_CC1 0x30 -#define ANX74XX_REG_AUX_SWAP_SET_CC2 0xc0 - -#define ANX74XX_REG_ANALOG_CTRL_11 0x4c -#define ANX74XX_REG_ANALOG_CTRL_12 0x4d - -#define ANX74XX_REG_MUX_ML0_RX2 BIT(0) -#define ANX74XX_REG_MUX_ML0_RX1 BIT(1) -#define ANX74XX_REG_MUX_ML3_RX2 BIT(2) -#define ANX74XX_REG_MUX_ML3_RX1 BIT(3) -#define ANX74XX_REG_MUX_SSRX_RX2 BIT(4) -#define ANX74XX_REG_MUX_SSRX_RX1 BIT(5) -#define ANX74XX_REG_MUX_ML1_TX2 BIT(6) -#define ANX74XX_REG_MUX_ML1_TX1 BIT(7) - -#define ANX74XX_REG_MUX_ML2_TX2 BIT(4) -#define ANX74XX_REG_MUX_ML2_TX1 BIT(5) -#define ANX74XX_REG_MUX_SSTX_TX2 BIT(6) -#define ANX74XX_REG_MUX_SSTX_TX1 BIT(7) - -#define ANX74XX_REG_CC_SOFTWARE_CTRL 0x4a -#define ANX74XX_REG_CC_SW_CTRL_ENABLE 0x01 -#define ANX74XX_REG_TX_MODE_ENABLE 0x04 - -#define ANX74XX_REG_SELECT_CC1 0x02 - -#define ANX74XX_REG_GPIO_CTRL_4_5 0x3f -#define ANX74XX_REG_VBUS_OP_ENABLE 0x04 -#define ANX74XX_REG_VBUS_GPIO_MODE 0xfe - -#define ANX74XX_REG_IRQ_EXT_MASK_1 0x3b -#define ANX74XX_REG_IRQ_EXT_MASK_2 0x3c -#define ANX74XX_REG_IRQ_EXT_SOURCE_1 0x3e -#define ANX74XX_REG_EXT_SOP BIT(6) -#define ANX74XX_REG_EXT_SOP_PRIME BIT(7) -#define ANX74XX_REG_IRQ_EXT_SOURCE_2 0x4e -#define ANX74XX_REG_EXT_SOP_PRIME_PRIME BIT(0) -#define ANX74XX_REG_EXT_HARD_RST BIT(2) -#define ANX74XX_REG_IRQ_EXT_SOURCE_3 0x4f -#define ANX74XX_REG_CLEAR_SOFT_IRQ BIT(2) - -#define ANX74XX_REG_IRQ_SOURCE_RECV_MSG 0x6b -#define ANX74XX_REG_IRQ_CC_MSG_INT BIT(0) -#define ANX74XX_REG_IRQ_CC_STATUS_INT BIT(1) -#define ANX74XX_REG_IRQ_GOOD_CRC_INT BIT(2) -#define ANX74XX_REG_IRQ_TX_FAIL_INT BIT(3) -#define ANX74XX_REG_IRQ_SOURCE_RECV_MSG_MASK 0x6c - -#define ANX74XX_REG_CLEAR_SET_BITS 0xff -#define ANX74XX_REG_ALERT_HARD_RST_RECV BIT(6) -#define ANX74XX_REG_ALERT_MSG_RECV BIT(5) -#define ANX74XX_REG_ALERT_TX_MSG_ERROR BIT(4) -#define ANX74XX_REG_ALERT_TX_ACK_RECV BIT(3) -#define ANX74XX_REG_ALERT_TX_CABLE_RESETOK BIT(2) -#define ANX74XX_REG_ALERT_TX_HARD_RESETOK BIT(1) -#define ANX74XX_REG_ALERT_CC_CHANGE BIT(0) - -#define ANX74XX_REG_ANALOG_CTRL_2 0x43 -#define ANX74XX_REG_MODE_TRANS 0x01 - -#define ANX74XX_REG_SET_VBUS 0x20 - -#define ANX74XX_REG_ANALOG_CTRL_7 0x48 -#define ANX74XX_REG_STATUS_CC_RD 0x01 -#define ANX74XX_REG_STATUS_CC_RA 0x03 -#define ANX74XX_REG_STATUS_CC1(reg) ((reg & 0x0C) >> 2) -#define ANX74XX_REG_STATUS_CC2(reg) ((reg & 0x03) >> 0) - -#define ANX74XX_REG_HPD_CONTROL 0xfd - -#define ANX74XX_REG_HPD_CTRL_0 0x36 -#define ANX74XX_REG_DISCHARGE_CTRL 0x80 -#define ANX74XX_REG_HPD_OP_MODE 0x08 -#define ANX74XX_REG_HPD_DEFAULT 0x00 -#define ANX74XX_REG_HPD_OUT_DATA 0x10 - -#define ANX74XX_REG_RECVD_MSG_INT 0x98 -#define ANX74XX_REG_CC_STATUS 0x99 -#define ANX74XX_REG_CTRL_FW 0x2E -#define CLEAR_RX_BUFFER (1) -#define ANX74XX_REG_POWER_DOWN_CTRL 0x0d -#define ANX74XX_REG_STATUS_CC1_VRD_USB BIT(7) -#define ANX74XX_REG_STATUS_CC1_VRD_1P5 BIT(6) -#define ANX74XX_REG_STATUS_CC1_VRD_3P0 BIT(5) -#define ANX74XX_REG_STATUS_CC2_VRD_USB BIT(4) -#define ANX74XX_REG_STATUS_CC2_VRD_1P5 BIT(3) -#define ANX74XX_REG_STATUS_CC2_VRD_3P0 BIT(2) - -/* defined in the inter-bock Spec: 4.2.10 CC Detect Status */ -#define ANX74XX_REG_CC_STATUS_MASK 0xf -#define BIT_VALUE_OF_SRC_CC_RD 0x01 -#define BIT_VALUE_OF_SRC_CC_RA 0x02 -#define BIT_VALUE_OF_SNK_CC_DEFAULT 0x04 -#define BIT_VALUE_OF_SNK_CC_1_P_5 0x08 -#define BIT_VALUE_OF_SNK_CC_3_P_0 0x0C -#define ANX74XX_CC_RA_MASK (BIT_VALUE_OF_SRC_CC_RA | \ - (BIT_VALUE_OF_SRC_CC_RA << 4)) -#define ANX74XX_CC_RD_MASK (BIT_VALUE_OF_SRC_CC_RD | \ - (BIT_VALUE_OF_SRC_CC_RD << 4)) - -/* - * RESETN low to PWR_EN low delay - */ -#define ANX74XX_RST_L_PWR_L_DELAY_MS 1 -/* - * minimum power off-to-on delay to reset chip - */ -#define ANX74XX_PWR_L_PWR_H_DELAY_MS 10 -/* - * parameter T4: PWR_EN high to RESETN high delay - */ -#define ANX74XX_PWR_H_RST_H_DELAY_MS 10 - -extern const struct tcpm_drv anx74xx_tcpm_drv; -extern const struct usb_mux_driver anx74xx_tcpm_usb_mux_driver; -void anx74xx_tcpc_set_vbus(int port, int enable); -void anx74xx_tcpc_update_hpd_status(int port, int hpd_lvl, int hpd_irq); -void anx74xx_tcpc_clear_hpd_status(int port); - -#ifdef CONFIG_CMD_I2C_STRESS_TEST_TCPC -extern struct i2c_stress_test_dev anx74xx_i2c_stress_test_dev; -#endif - -#endif /* __CROS_EC_USB_PD_TCPM_ANX74XX_H */ diff --git a/driver/tcpm/anx7688.c b/driver/tcpm/anx7688.c deleted file mode 100644 index fe1d2498d4..0000000000 --- a/driver/tcpm/anx7688.c +++ /dev/null @@ -1,213 +0,0 @@ -/* Copyright 2016 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. - */ - -/* ANX7688 port manager */ - -#include "hooks.h" -#include "tcpci.h" -#include "tcpm.h" -#include "timer.h" -#include "usb_mux.h" - -#if defined(CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE) || \ - defined(CONFIG_USB_PD_TCPC_LOW_POWER) || \ - defined(CONFIG_USB_PD_DISCHARGE_TCPC) -#error "Unsupported config options of anx7688 PD driver" -#endif - -#define ANX7688_VENDOR_ALERT BIT(15) - -#define ANX7688_REG_STATUS 0x82 -#define ANX7688_REG_STATUS_LINK BIT(0) - -#define ANX7688_REG_HPD 0x83 -#define ANX7688_REG_HPD_HIGH BIT(0) -#define ANX7688_REG_HPD_IRQ BIT(1) -#define ANX7688_REG_HPD_ENABLE BIT(2) - -#define ANX7688_USBC_ADDR_FLAGS 0x28 -#define ANX7688_REG_RAMCTRL 0xe7 -#define ANX7688_REG_RAMCTRL_BOOT_DONE BIT(6) - -static int anx7688_init(int port) -{ - int rv = 0; - int mask = 0; - - /* - * 7688 POWER_STATUS[6] is not reliable for tcpci_tcpm_init() to poll - * due to it is default 0 in HW, and we cannot write TCPC until it is - * ready, or something goes wrong. (Issue 52772) - * Instead we poll TCPC 0x50:0xe7 bit6 here to make sure bootdone is - * ready(50ms). Then PD main flow can process cc debounce in 50ms ~ - * 100ms to follow cts. - */ - while (1) { - rv = i2c_read8(I2C_PORT_TCPC, ANX7688_USBC_ADDR_FLAGS, - ANX7688_REG_RAMCTRL, &mask); - - if (rv == EC_SUCCESS && (mask & ANX7688_REG_RAMCTRL_BOOT_DONE)) - break; - msleep(10); - } - - rv = tcpci_tcpm_drv.init(port); - if (rv) - return rv; - - rv = tcpc_read16(port, TCPC_REG_ALERT_MASK, &mask); - if (rv) - return rv; - - /* enable vendor specific alert */ - mask |= ANX7688_VENDOR_ALERT; - rv = tcpc_write16(port, TCPC_REG_ALERT_MASK, mask); - return rv; -} - -static int anx7688_release(int port) -{ - return EC_ERROR_UNIMPLEMENTED; -} - -static void anx7688_update_hpd_enable(int port) -{ - int status, reg, rv; - - rv = tcpc_read(port, ANX7688_REG_STATUS, &status); - rv |= tcpc_read(port, ANX7688_REG_HPD, ®); - if (rv) - return; - - if (!(reg & ANX7688_REG_HPD_ENABLE) || - !(status & ANX7688_REG_STATUS_LINK)) { - reg &= ~ANX7688_REG_HPD_IRQ; - tcpc_write(port, ANX7688_REG_HPD, - (status & ANX7688_REG_STATUS_LINK) - ? reg | ANX7688_REG_HPD_ENABLE - : reg & ~ANX7688_REG_HPD_ENABLE); - } -} - -int anx7688_hpd_disable(int port) -{ - return tcpc_write(port, ANX7688_REG_HPD, 0); -} - -int anx7688_update_hpd(int port, int level, int irq) -{ - int reg, rv; - - rv = tcpc_read(port, ANX7688_REG_HPD, ®); - if (rv) - return rv; - - if (level) - reg |= ANX7688_REG_HPD_HIGH; - else - reg &= ~ANX7688_REG_HPD_HIGH; - - if (irq) - reg |= ANX7688_REG_HPD_IRQ; - else - reg &= ~ANX7688_REG_HPD_IRQ; - - return tcpc_write(port, ANX7688_REG_HPD, reg); -} - -int anx7688_enable_cable_detection(int port) -{ - return tcpc_write(port, TCPC_REG_COMMAND, 0xff); -} - -int anx7688_set_power_supply_ready(int port) -{ - return tcpc_write(port, TCPC_REG_COMMAND, 0x77); -} - -int anx7688_power_supply_reset(int port) -{ - return tcpc_write(port, TCPC_REG_COMMAND, 0x66); -} - -static void anx7688_tcpc_alert(int port) -{ - int alert, rv; - - rv = tcpc_read16(port, TCPC_REG_ALERT, &alert); - /* process and clear alert status */ - tcpci_tcpc_alert(port); - - if (!rv && (alert & ANX7688_VENDOR_ALERT)) - anx7688_update_hpd_enable(port); -} - -static int anx7688_mux_set(int port, mux_state_t mux_state) -{ - int reg = 0; - int rv, polarity; - - rv = mux_read(port, TCPC_REG_CONFIG_STD_OUTPUT, ®); - if (rv != EC_SUCCESS) - return rv; - - reg &= ~TCPC_REG_CONFIG_STD_OUTPUT_MUX_MASK; - if (mux_state & MUX_USB_ENABLED) - reg |= TCPC_REG_CONFIG_STD_OUTPUT_MUX_USB; - if (mux_state & MUX_DP_ENABLED) - reg |= TCPC_REG_CONFIG_STD_OUTPUT_MUX_DP; - - /* ANX7688 needs to set bit0 */ - rv = mux_read(port, TCPC_REG_TCPC_CTRL, &polarity); - if (rv != EC_SUCCESS) - return rv; - - /* copy the polarity from TCPC_CTRL[0], take care clear then set */ - reg &= ~TCPC_REG_TCPC_CTRL_POLARITY(1); - reg |= TCPC_REG_TCPC_CTRL_POLARITY(polarity); - return mux_write(port, TCPC_REG_CONFIG_STD_OUTPUT, reg); -} - -#ifdef CONFIG_USB_PD_VBUS_DETECT_TCPC -static int anx7688_tcpm_get_vbus_level(int port) -{ - int reg = 0; - - /* On ANX7688, POWER_STATUS.VBusPresent (bit 2) is averaged 16 times, so - * its value may not be set to 1 quickly enough during power role swap. - * Therefore, we use a proprietary register to read the unfiltered VBus - * value. See crosbug.com/p/55221 . - */ - i2c_read8(I2C_PORT_TCPC, 0x28, 0x40, ®); - return ((reg & 0x10) ? 1 : 0); -} -#endif - -/* ANX7688 is a TCPCI compatible port controller */ -const struct tcpm_drv anx7688_tcpm_drv = { - .init = &anx7688_init, - .release = &anx7688_release, - .get_cc = &tcpci_tcpm_get_cc, -#ifdef CONFIG_USB_PD_VBUS_DETECT_TCPC - .get_vbus_level = &anx7688_tcpm_get_vbus_level, -#endif - .select_rp_value = &tcpci_tcpm_select_rp_value, - .set_cc = &tcpci_tcpm_set_cc, - .set_polarity = &tcpci_tcpm_set_polarity, - .set_vconn = &tcpci_tcpm_set_vconn, - .set_msg_header = &tcpci_tcpm_set_msg_header, - .set_rx_enable = &tcpci_tcpm_set_rx_enable, - .get_message_raw = &tcpci_tcpm_get_message_raw, - .transmit = &tcpci_tcpm_transmit, - .tcpc_alert = &anx7688_tcpc_alert, -}; - -#ifdef CONFIG_USB_PD_TCPM_MUX -const struct usb_mux_driver anx7688_usb_mux_driver = { - .init = tcpci_tcpm_mux_init, - .set = anx7688_mux_set, - .get = tcpci_tcpm_mux_get, -}; -#endif /* CONFIG_USB_PD_TCPM_MUX */ diff --git a/driver/tcpm/anx7688.h b/driver/tcpm/anx7688.h deleted file mode 100644 index 534e4155b1..0000000000 --- a/driver/tcpm/anx7688.h +++ /dev/null @@ -1,21 +0,0 @@ -/* Copyright 2016 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. - */ - -/* USB Power delivery port management */ - -#ifndef __CROS_EC_USB_PD_TCPM_ANX7688_H -#define __CROS_EC_USB_PD_TCPM_ANX7688_H - -int anx7688_update_hpd(int port, int level, int irq); -int anx7688_set_dp_pin_mode(int port, int pin_mode); -int anx7688_enable_cable_detection(int port); -int anx7688_set_power_supply_ready(int port); -int anx7688_power_supply_reset(int port); -int anx7688_hpd_disable(int port); - -extern struct tcpm_drv anx7688_tcpm_drv; -extern struct usb_mux_driver anx7688_usb_mux_driver; - -#endif /* __CROS_EC_USB_PD_TCPM_ANX7688_H */ diff --git a/driver/tcpm/fusb302.c b/driver/tcpm/fusb302.c deleted file mode 100644 index 66c131fb37..0000000000 --- a/driver/tcpm/fusb302.c +++ /dev/null @@ -1,1036 +0,0 @@ -/* 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. - * - * Author: Gabe Noblesmith - */ - -/* Type-C port manager for Fairchild's FUSB302 */ - -#include "console.h" -#include "fusb302.h" -#include "task.h" -#include "hooks.h" -#include "tcpm.h" -#include "timer.h" -#include "usb_charge.h" -#include "usb_pd.h" -#include "usb_pd_tcpc.h" -#include "util.h" - -#if defined(CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE) || \ - defined(CONFIG_USB_PD_DISCHARGE_TCPC) -#error "Unsupported config options of fusb302 PD driver" -#endif - -#define PACKET_IS_GOOD_CRC(head) (PD_HEADER_TYPE(head) == PD_CTRL_GOOD_CRC && \ - PD_HEADER_CNT(head) == 0) - -static struct fusb302_chip_state { - int cc_polarity; - int vconn_enabled; - /* 1 = pulling up (DFP) 0 = pulling down (UFP) */ - int pulling_up; - int rx_enable; - uint8_t mdac_vnc; - uint8_t mdac_rd; -} state[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/* - * Bring the FUSB302 out of reset after Hard Reset signaling. This will - * automatically flush both the Rx and Tx FIFOs. - */ -static void fusb302_pd_reset(int port) -{ - tcpc_write(port, TCPC_REG_RESET, TCPC_REG_RESET_PD_RESET); -} - -/* - * Flush our Rx FIFO. To prevent packet framing issues, this function should - * only be called when Rx is disabled. - */ -static void fusb302_flush_rx_fifo(int port) -{ - /* - * other bits in the register _should_ be 0 - * until the day we support other SOP* types... - * then we'll have to keep a shadow of what this register - * value should be so we don't clobber it here! - */ - tcpc_write(port, TCPC_REG_CONTROL1, TCPC_REG_CONTROL1_RX_FLUSH); -} - -static void fusb302_flush_tx_fifo(int port) -{ - int reg; - - tcpc_read(port, TCPC_REG_CONTROL0, ®); - reg |= TCPC_REG_CONTROL0_TX_FLUSH; - tcpc_write(port, TCPC_REG_CONTROL0, reg); -} - -static void fusb302_auto_goodcrc_enable(int port, int enable) -{ - int reg; - - tcpc_read(port, TCPC_REG_SWITCHES1, ®); - - if (enable) - reg |= TCPC_REG_SWITCHES1_AUTO_GCRC; - else - reg &= ~TCPC_REG_SWITCHES1_AUTO_GCRC; - - tcpc_write(port, TCPC_REG_SWITCHES1, reg); -} - -/* Convert BC LVL values (in FUSB302) to Type-C CC Voltage Status */ -static int convert_bc_lvl(int port, int bc_lvl) -{ - /* assume OPEN unless one of the following conditions is true... */ - int ret = TYPEC_CC_VOLT_OPEN; - - if (state[port].pulling_up) { - if (bc_lvl == 0x00) - ret = TYPEC_CC_VOLT_RA; - else if (bc_lvl < 0x3) - ret = TYPEC_CC_VOLT_RD; - } else { - if (bc_lvl == 0x1) - ret = TYPEC_CC_VOLT_RP_DEF; - else if (bc_lvl == 0x2) - ret = TYPEC_CC_VOLT_RP_1_5; - else if (bc_lvl == 0x3) - ret = TYPEC_CC_VOLT_RP_3_0; - } - - return ret; -} - -static int measure_cc_pin_source(int port, int cc_measure) -{ - int switches0_reg; - int reg; - int cc_lvl; - - /* Read status register */ - tcpc_read(port, TCPC_REG_SWITCHES0, ®); - /* Save current value */ - switches0_reg = reg; - /* Clear pull-up register settings and measure bits */ - reg &= ~(TCPC_REG_SWITCHES0_MEAS_CC1 | TCPC_REG_SWITCHES0_MEAS_CC2); - /* Set desired pullup register bit */ - if (cc_measure == TCPC_REG_SWITCHES0_MEAS_CC1) - reg |= TCPC_REG_SWITCHES0_CC1_PU_EN; - else - reg |= TCPC_REG_SWITCHES0_CC2_PU_EN; - /* Set CC measure bit */ - reg |= cc_measure; - - /* Set measurement switch */ - tcpc_write(port, TCPC_REG_SWITCHES0, reg); - - /* Set MDAC for Open vs Rd/Ra comparison */ - tcpc_write(port, TCPC_REG_MEASURE, state[port].mdac_vnc); - - /* Wait on measurement */ - usleep(250); - - /* Read status register */ - tcpc_read(port, TCPC_REG_STATUS0, ®); - - /* Assume open */ - cc_lvl = TYPEC_CC_VOLT_OPEN; - - /* CC level is below the 'no connect' threshold (vOpen) */ - if ((reg & TCPC_REG_STATUS0_COMP) == 0) { - /* Set MDAC for Rd vs Ra comparison */ - tcpc_write(port, TCPC_REG_MEASURE, state[port].mdac_rd); - - /* Wait on measurement */ - usleep(250); - - /* Read status register */ - tcpc_read(port, TCPC_REG_STATUS0, ®); - - cc_lvl = (reg & TCPC_REG_STATUS0_COMP) ? TYPEC_CC_VOLT_RD - : TYPEC_CC_VOLT_RA; - } - - /* Restore SWITCHES0 register to its value prior */ - tcpc_write(port, TCPC_REG_SWITCHES0, switches0_reg); - - return cc_lvl; -} - -/* Determine cc pin state for source when in manual detect mode */ -static void detect_cc_pin_source_manual(int port, - enum tcpc_cc_voltage_status *cc1_lvl, - enum tcpc_cc_voltage_status *cc2_lvl) -{ - int cc1_measure = TCPC_REG_SWITCHES0_MEAS_CC1; - int cc2_measure = TCPC_REG_SWITCHES0_MEAS_CC2; - - if (state[port].vconn_enabled) { - /* If VCONN enabled, measure cc_pin that matches polarity */ - if (state[port].cc_polarity) - *cc2_lvl = measure_cc_pin_source(port, cc2_measure); - else - *cc1_lvl = measure_cc_pin_source(port, cc1_measure); - } else { - /* If VCONN not enabled, measure both cc1 and cc2 */ - *cc1_lvl = measure_cc_pin_source(port, cc1_measure); - *cc2_lvl = measure_cc_pin_source(port, cc2_measure); - } - -} - -/* Determine cc pin state for sink */ -static void detect_cc_pin_sink(int port, enum tcpc_cc_voltage_status *cc1, - enum tcpc_cc_voltage_status *cc2) -{ - int reg; - int orig_meas_cc1; - int orig_meas_cc2; - int bc_lvl_cc1; - int bc_lvl_cc2; - - /* - * Measure CC1 first. - */ - tcpc_read(port, TCPC_REG_SWITCHES0, ®); - - /* save original state to be returned to later... */ - if (reg & TCPC_REG_SWITCHES0_MEAS_CC1) - orig_meas_cc1 = 1; - else - orig_meas_cc1 = 0; - - if (reg & TCPC_REG_SWITCHES0_MEAS_CC2) - orig_meas_cc2 = 1; - else - orig_meas_cc2 = 0; - - - /* Disable CC2 measurement switch, enable CC1 measurement switch */ - reg &= ~TCPC_REG_SWITCHES0_MEAS_CC2; - reg |= TCPC_REG_SWITCHES0_MEAS_CC1; - - tcpc_write(port, TCPC_REG_SWITCHES0, reg); - - /* CC1 is now being measured by FUSB302. */ - - /* Wait on measurement */ - usleep(250); - - tcpc_read(port, TCPC_REG_STATUS0, &bc_lvl_cc1); - - /* mask away unwanted bits */ - bc_lvl_cc1 &= (TCPC_REG_STATUS0_BC_LVL0 | TCPC_REG_STATUS0_BC_LVL1); - - /* - * Measure CC2 next. - */ - - tcpc_read(port, TCPC_REG_SWITCHES0, ®); - - /* Disable CC1 measurement switch, enable CC2 measurement switch */ - reg &= ~TCPC_REG_SWITCHES0_MEAS_CC1; - reg |= TCPC_REG_SWITCHES0_MEAS_CC2; - - tcpc_write(port, TCPC_REG_SWITCHES0, reg); - - /* CC2 is now being measured by FUSB302. */ - - /* Wait on measurement */ - usleep(250); - - tcpc_read(port, TCPC_REG_STATUS0, &bc_lvl_cc2); - - /* mask away unwanted bits */ - bc_lvl_cc2 &= (TCPC_REG_STATUS0_BC_LVL0 | TCPC_REG_STATUS0_BC_LVL1); - - *cc1 = convert_bc_lvl(port, bc_lvl_cc1); - *cc2 = convert_bc_lvl(port, bc_lvl_cc2); - - /* return MEAS_CC1/2 switches to original state */ - tcpc_read(port, TCPC_REG_SWITCHES0, ®); - if (orig_meas_cc1) - reg |= TCPC_REG_SWITCHES0_MEAS_CC1; - else - reg &= ~TCPC_REG_SWITCHES0_MEAS_CC1; - if (orig_meas_cc2) - reg |= TCPC_REG_SWITCHES0_MEAS_CC2; - else - reg &= ~TCPC_REG_SWITCHES0_MEAS_CC2; - - tcpc_write(port, TCPC_REG_SWITCHES0, reg); -} - -/* Parse header bytes for the size of packet */ -static int get_num_bytes(uint16_t header) -{ - int rv; - - /* Grab the Number of Data Objects field.*/ - rv = PD_HEADER_CNT(header); - - /* Multiply by four to go from 32-bit words -> bytes */ - rv *= 4; - - /* Plus 2 for header */ - rv += 2; - - return rv; -} - -static int fusb302_send_message(int port, uint16_t header, const uint32_t *data, - uint8_t *buf, int buf_pos) -{ - int rv; - int reg; - int len; - - len = get_num_bytes(header); - - /* - * packsym tells the TXFIFO that the next X bytes are payload, - * and should not be interpreted as special tokens. - * The 5 LSBs represent X, the number of bytes. - */ - reg = FUSB302_TKN_PACKSYM; - reg |= (len & 0x1F); - - buf[buf_pos++] = reg; - - /* write in the header */ - reg = header; - buf[buf_pos++] = reg & 0xFF; - - reg >>= 8; - buf[buf_pos++] = reg & 0xFF; - - /* header is done, subtract from length to make this for-loop simpler */ - len -= 2; - - /* write data objects, if present */ - memcpy(&buf[buf_pos], data, len); - buf_pos += len; - - /* put in the CRC */ - buf[buf_pos++] = FUSB302_TKN_JAMCRC; - - /* put in EOP */ - buf[buf_pos++] = FUSB302_TKN_EOP; - - /* Turn transmitter off after sending message */ - buf[buf_pos++] = FUSB302_TKN_TXOFF; - - /* Start transmission */ - reg = FUSB302_TKN_TXON; - buf[buf_pos++] = FUSB302_TKN_TXON; - - /* burst write for speed! */ - rv = tcpc_xfer(port, buf, buf_pos, 0, 0); - - return rv; -} - -static int fusb302_tcpm_select_rp_value(int port, int rp) -{ - int reg; - int rv; - uint8_t vnc, rd; - - rv = tcpc_read(port, TCPC_REG_CONTROL0, ®); - if (rv) - return rv; - - /* Set the current source for Rp value */ - reg &= ~TCPC_REG_CONTROL0_HOST_CUR_MASK; - switch (rp) { - case TYPEC_RP_1A5: - reg |= TCPC_REG_CONTROL0_HOST_CUR_1A5; - vnc = TCPC_REG_MEASURE_MDAC_MV(PD_SRC_1_5_VNC_MV); - rd = TCPC_REG_MEASURE_MDAC_MV(PD_SRC_1_5_RD_THRESH_MV); - break; - case TYPEC_RP_3A0: - reg |= TCPC_REG_CONTROL0_HOST_CUR_3A0; - vnc = TCPC_REG_MEASURE_MDAC_MV(PD_SRC_3_0_VNC_MV); - rd = TCPC_REG_MEASURE_MDAC_MV(PD_SRC_3_0_RD_THRESH_MV); - break; - case TYPEC_RP_USB: - default: - reg |= TCPC_REG_CONTROL0_HOST_CUR_USB; - vnc = TCPC_REG_MEASURE_MDAC_MV(PD_SRC_DEF_VNC_MV); - rd = TCPC_REG_MEASURE_MDAC_MV(PD_SRC_DEF_RD_THRESH_MV); - } - state[port].mdac_vnc = vnc; - state[port].mdac_rd = rd; - return tcpc_write(port, TCPC_REG_CONTROL0, reg); -} - -static int fusb302_tcpm_init(int port) -{ - int reg; - - /* set default */ - state[port].cc_polarity = -1; - - /* set the voltage threshold for no connect detection (vOpen) */ - state[port].mdac_vnc = TCPC_REG_MEASURE_MDAC_MV(PD_SRC_DEF_VNC_MV); - /* set the voltage threshold for Rd vs Ra detection */ - state[port].mdac_rd = TCPC_REG_MEASURE_MDAC_MV(PD_SRC_DEF_RD_THRESH_MV); - - /* all other variables assumed to default to 0 */ - - /* Restore default settings */ - tcpc_write(port, TCPC_REG_RESET, TCPC_REG_RESET_SW_RESET); - - /* Turn on retries and set number of retries */ - tcpc_read(port, TCPC_REG_CONTROL3, ®); - reg |= TCPC_REG_CONTROL3_AUTO_RETRY; - reg |= (PD_RETRY_COUNT & 0x3) << - TCPC_REG_CONTROL3_N_RETRIES_POS; - tcpc_write(port, TCPC_REG_CONTROL3, reg); - - /* Create interrupt masks */ - reg = 0xFF; - /* CC level changes */ - reg &= ~TCPC_REG_MASK_BC_LVL; - /* collisions */ - reg &= ~TCPC_REG_MASK_COLLISION; - /* misc alert */ - reg &= ~TCPC_REG_MASK_ALERT; -#ifdef CONFIG_USB_PD_VBUS_DETECT_TCPC - /* TODO(crbug.com/791109): Clean up VBUS notification. */ - - /* VBUS threshold crossed (~4.0V) */ - reg &= ~TCPC_REG_MASK_VBUSOK; -#endif - tcpc_write(port, TCPC_REG_MASK, reg); - - reg = 0xFF; - /* when all pd message retries fail... */ - reg &= ~TCPC_REG_MASKA_RETRYFAIL; - /* when fusb302 send a hard reset. */ - reg &= ~TCPC_REG_MASKA_HARDSENT; - /* when fusb302 receives GoodCRC ack for a pd message */ - reg &= ~TCPC_REG_MASKA_TX_SUCCESS; - /* when fusb302 receives a hard reset */ - reg &= ~TCPC_REG_MASKA_HARDRESET; - tcpc_write(port, TCPC_REG_MASKA, reg); - - reg = 0xFF; - /* when fusb302 sends GoodCRC to ack a pd message */ - reg &= ~TCPC_REG_MASKB_GCRCSENT; - tcpc_write(port, TCPC_REG_MASKB, reg); - - /* Interrupt Enable */ - tcpc_read(port, TCPC_REG_CONTROL0, ®); - reg &= ~TCPC_REG_CONTROL0_INT_MASK; - tcpc_write(port, TCPC_REG_CONTROL0, reg); - - /* Set VCONN switch defaults */ - tcpm_set_polarity(port, 0); - tcpm_set_vconn(port, 0); - - /* Turn on the power! */ - /* TODO: Reduce power consumption */ - tcpc_write(port, TCPC_REG_POWER, TCPC_REG_POWER_PWR_ALL); - -#if defined(CONFIG_USB_PD_VBUS_DETECT_TCPC) && defined(CONFIG_USB_CHARGER) - /* Wait for the reference voltage to stablize */ - usleep(250); - /* - * Initialize VBUS supplier when VBUS is already present before - * init (e.g. Cold reboot with charger plugged). - */ - tcpc_read(port, TCPC_REG_STATUS0, ®); - if (reg & TCPC_REG_STATUS0_VBUSOK) - usb_charger_vbus_change(port, 1); -#endif - - return 0; -} - -static int fusb302_tcpm_release(int port) -{ - return EC_ERROR_UNIMPLEMENTED; -} - -static int fusb302_tcpm_get_cc(int port, enum tcpc_cc_voltage_status *cc1, - enum tcpc_cc_voltage_status *cc2) -{ - if (state[port].pulling_up) { - /* Source mode? */ - detect_cc_pin_source_manual(port, cc1, cc2); - } else { - /* Sink mode? */ - detect_cc_pin_sink(port, cc1, cc2); - } - - return 0; -} - -static int fusb302_tcpm_set_cc(int port, int pull) -{ - int reg; - - /* NOTE: FUSB302 toggles a single pull-up between CC1 and CC2 */ - /* NOTE: FUSB302 Does not support Ra. */ - switch (pull) { - case TYPEC_CC_RP: - /* enable the pull-up we know to be necessary */ - tcpc_read(port, TCPC_REG_SWITCHES0, ®); - - reg &= ~(TCPC_REG_SWITCHES0_CC2_PU_EN | - TCPC_REG_SWITCHES0_CC1_PU_EN | - TCPC_REG_SWITCHES0_CC1_PD_EN | - TCPC_REG_SWITCHES0_CC2_PD_EN | - TCPC_REG_SWITCHES0_VCONN_CC1 | - TCPC_REG_SWITCHES0_VCONN_CC2); - - reg |= TCPC_REG_SWITCHES0_CC1_PU_EN | - TCPC_REG_SWITCHES0_CC2_PU_EN; - - if (state[port].vconn_enabled) - reg |= state[port].cc_polarity ? - TCPC_REG_SWITCHES0_VCONN_CC1 : - TCPC_REG_SWITCHES0_VCONN_CC2; - - tcpc_write(port, TCPC_REG_SWITCHES0, reg); - - state[port].pulling_up = 1; - break; - case TYPEC_CC_RD: - /* Enable UFP Mode */ - - /* turn off toggle */ - tcpc_read(port, TCPC_REG_CONTROL2, ®); - reg &= ~TCPC_REG_CONTROL2_TOGGLE; - tcpc_write(port, TCPC_REG_CONTROL2, reg); - - /* enable pull-downs, disable pullups */ - tcpc_read(port, TCPC_REG_SWITCHES0, ®); - - reg &= ~(TCPC_REG_SWITCHES0_CC2_PU_EN); - reg &= ~(TCPC_REG_SWITCHES0_CC1_PU_EN); - reg |= (TCPC_REG_SWITCHES0_CC1_PD_EN); - reg |= (TCPC_REG_SWITCHES0_CC2_PD_EN); - tcpc_write(port, TCPC_REG_SWITCHES0, reg); - - state[port].pulling_up = 0; - break; - case TYPEC_CC_OPEN: - /* Disable toggling */ - tcpc_read(port, TCPC_REG_CONTROL2, ®); - reg &= ~TCPC_REG_CONTROL2_TOGGLE; - tcpc_write(port, TCPC_REG_CONTROL2, reg); - - /* Ensure manual switches are opened */ - tcpc_read(port, TCPC_REG_SWITCHES0, ®); - reg &= ~TCPC_REG_SWITCHES0_CC1_PU_EN; - reg &= ~TCPC_REG_SWITCHES0_CC2_PU_EN; - reg &= ~TCPC_REG_SWITCHES0_CC1_PD_EN; - reg &= ~TCPC_REG_SWITCHES0_CC2_PD_EN; - tcpc_write(port, TCPC_REG_SWITCHES0, reg); - - state[port].pulling_up = 0; - break; - default: - /* Unsupported... */ - return EC_ERROR_UNIMPLEMENTED; - } - return 0; -} - -static int fusb302_tcpm_set_polarity(int port, int polarity) -{ - /* Port polarity : 0 => CC1 is CC line, 1 => CC2 is CC line */ - int reg; - - tcpc_read(port, TCPC_REG_SWITCHES0, ®); - - /* clear VCONN switch bits */ - reg &= ~TCPC_REG_SWITCHES0_VCONN_CC1; - reg &= ~TCPC_REG_SWITCHES0_VCONN_CC2; - - if (state[port].vconn_enabled) { - /* set VCONN switch to be non-CC line */ - if (polarity) - reg |= TCPC_REG_SWITCHES0_VCONN_CC1; - else - reg |= TCPC_REG_SWITCHES0_VCONN_CC2; - } - - /* clear meas_cc bits (RX line select) */ - reg &= ~TCPC_REG_SWITCHES0_MEAS_CC1; - reg &= ~TCPC_REG_SWITCHES0_MEAS_CC2; - - /* set rx polarity */ - if (polarity) - reg |= TCPC_REG_SWITCHES0_MEAS_CC2; - else - reg |= TCPC_REG_SWITCHES0_MEAS_CC1; - - tcpc_write(port, TCPC_REG_SWITCHES0, reg); - - tcpc_read(port, TCPC_REG_SWITCHES1, ®); - - /* clear tx_cc bits */ - reg &= ~TCPC_REG_SWITCHES1_TXCC1_EN; - reg &= ~TCPC_REG_SWITCHES1_TXCC2_EN; - - /* set tx polarity */ - if (polarity) - reg |= TCPC_REG_SWITCHES1_TXCC2_EN; - else - reg |= TCPC_REG_SWITCHES1_TXCC1_EN; - - tcpc_write(port, TCPC_REG_SWITCHES1, reg); - - /* Save the polarity for later */ - state[port].cc_polarity = polarity; - - return 0; -} - -static int fusb302_tcpm_set_vconn(int port, int enable) -{ - /* - * FUSB302 does not have dedicated VCONN Enable switch. - * We'll get through this by disabling both of the - * VCONN - CC* switches to disable, and enabling the - * saved polarity when enabling. - * Therefore at startup, tcpm_set_polarity should be called first, - * or else live with the default put into tcpm_init. - */ - int reg; - - /* save enable state for later use */ - state[port].vconn_enabled = enable; - - if (enable) { - /* set to saved polarity */ - tcpm_set_polarity(port, state[port].cc_polarity); - -#ifdef CONFIG_USB_PD_DECODE_SOP - if (state[port].rx_enable) { - if (tcpc_read(port, TCPC_REG_CONTROL1, ®)) - return EC_ERROR_UNKNOWN; - - reg |= (TCPC_REG_CONTROL1_ENSOP1 | - TCPC_REG_CONTROL1_ENSOP2); - tcpc_write(port, TCPC_REG_CONTROL1, reg); - } -#endif - } else { - - tcpc_read(port, TCPC_REG_SWITCHES0, ®); - - /* clear VCONN switch bits */ - reg &= ~TCPC_REG_SWITCHES0_VCONN_CC1; - reg &= ~TCPC_REG_SWITCHES0_VCONN_CC2; - - tcpc_write(port, TCPC_REG_SWITCHES0, reg); - -#ifdef CONFIG_USB_PD_DECODE_SOP - if (state[port].rx_enable) { - if (tcpc_read(port, TCPC_REG_CONTROL1, ®)) - return EC_ERROR_UNKNOWN; - - reg &= ~(TCPC_REG_CONTROL1_ENSOP1 | - TCPC_REG_CONTROL1_ENSOP2); - tcpc_write(port, TCPC_REG_CONTROL1, reg); - } -#endif - } - - return 0; -} - -static int fusb302_tcpm_set_msg_header(int port, int power_role, int data_role) -{ - int reg; - - tcpc_read(port, TCPC_REG_SWITCHES1, ®); - - reg &= ~TCPC_REG_SWITCHES1_POWERROLE; - reg &= ~TCPC_REG_SWITCHES1_DATAROLE; - - if (power_role) - reg |= TCPC_REG_SWITCHES1_POWERROLE; - if (data_role) - reg |= TCPC_REG_SWITCHES1_DATAROLE; - - tcpc_write(port, TCPC_REG_SWITCHES1, reg); - - return 0; -} - -static int fusb302_tcpm_set_rx_enable(int port, int enable) -{ - int reg; - - state[port].rx_enable = enable; - - /* Get current switch state */ - tcpc_read(port, TCPC_REG_SWITCHES0, ®); - - /* Clear CC1/CC2 measure bits */ - reg &= ~TCPC_REG_SWITCHES0_MEAS_CC1; - reg &= ~TCPC_REG_SWITCHES0_MEAS_CC2; - - if (enable) { - switch (state[port].cc_polarity) { - /* if CC polarity hasnt been determined, can't enable */ - case -1: - return EC_ERROR_UNKNOWN; - case 0: - reg |= TCPC_REG_SWITCHES0_MEAS_CC1; - break; - case 1: - reg |= TCPC_REG_SWITCHES0_MEAS_CC2; - break; - default: - /* "shouldn't get here" */ - return EC_ERROR_UNKNOWN; - } - tcpc_write(port, TCPC_REG_SWITCHES0, reg); - - /* Disable BC_LVL interrupt when enabling PD comm */ - if (!tcpc_read(port, TCPC_REG_MASK, ®)) - tcpc_write(port, TCPC_REG_MASK, - reg | TCPC_REG_MASK_BC_LVL); - - /* flush rx fifo in case messages have been coming our way */ - fusb302_flush_rx_fifo(port); - - - } else { - tcpc_write(port, TCPC_REG_SWITCHES0, reg); - - /* Enable BC_LVL interrupt when disabling PD comm */ - if (!tcpc_read(port, TCPC_REG_MASK, ®)) - tcpc_write(port, TCPC_REG_MASK, - reg & ~TCPC_REG_MASK_BC_LVL); - } - -#ifdef CONFIG_USB_PD_DECODE_SOP - /* - * Only the VCONN Source is allowed to communicate - * with the Cable Plugs. - */ - if (state[port].vconn_enabled) { - if (tcpc_read(port, TCPC_REG_CONTROL1, ®)) - return EC_ERROR_UNKNOWN; - - reg |= (TCPC_REG_CONTROL1_ENSOP1 | TCPC_REG_CONTROL1_ENSOP2); - tcpc_write(port, TCPC_REG_CONTROL1, reg); - } -#endif - - fusb302_auto_goodcrc_enable(port, enable); - - return 0; -} - -/* Return true if our Rx FIFO is empty */ -static int fusb302_rx_fifo_is_empty(int port) -{ - int reg; - - return (!tcpc_read(port, TCPC_REG_STATUS1, ®)) && - (reg & TCPC_REG_STATUS1_RX_EMPTY); -} - -static int fusb302_tcpm_get_message_raw(int port, uint32_t *payload, int *head) -{ - /* - * This is the buffer that will get the burst-read data - * from the fusb302. - * - * It's re-used in a couple different spots, the worst of which - * is the PD packet (not header) and CRC. - * maximum size necessary = 28 + 4 = 32 - */ - uint8_t buf[32]; - int rv, len; - - /* Read until we have a non-GoodCRC packet or an empty FIFO */ - do { - buf[0] = TCPC_REG_FIFOS; - tcpc_lock(port, 1); - - /* - * PART 1 OF BURST READ: Write in register address. - * Issue a START, no STOP. - */ - rv = tcpc_xfer_unlocked(port, buf, 1, 0, 0, I2C_XFER_START); - - /* - * PART 2 OF BURST READ: Read up to the header. - * Issue a repeated START, no STOP. - * only grab three bytes so we can get the header - * and determine how many more bytes we need to read. - * TODO: Check token to ensure valid packet. - */ - rv |= tcpc_xfer_unlocked(port, 0, 0, buf, 3, I2C_XFER_START); - - /* Grab the header */ - *head = (buf[1] & 0xFF); - *head |= ((buf[2] << 8) & 0xFF00); - - /* figure out packet length, subtract header bytes */ - len = get_num_bytes(*head) - 2; - - /* - * PART 3 OF BURST READ: Read everything else. - * No START, but do issue a STOP at the end. - * add 4 to len to read CRC out - */ - rv |= tcpc_xfer_unlocked(port, 0, 0, buf, len+4, I2C_XFER_STOP); - - tcpc_lock(port, 0); - } while (!rv && PACKET_IS_GOOD_CRC(*head) && - !fusb302_rx_fifo_is_empty(port)); - - if (!rv) { - /* Discard GoodCRC packets */ - if (PACKET_IS_GOOD_CRC(*head)) - rv = EC_ERROR_UNKNOWN; - else - memcpy(payload, buf, len); - } - -#ifdef CONFIG_USB_PD_DECODE_SOP - { - int reg; - - if (tcpc_read(port, TCPC_REG_STATUS1, ®)) - return EC_ERROR_UNKNOWN; - - if (reg & TCPC_REG_STATUS1_RXSOP1) - *head |= PD_HEADER_SOP(PD_MSG_SOPP); - else if (reg & TCPC_REG_STATUS1_RXSOP2) - *head |= PD_HEADER_SOP(PD_MSG_SOPPP); - } -#endif - - return rv; -} - -static int fusb302_tcpm_transmit(int port, enum tcpm_transmit_type type, - uint16_t header, const uint32_t *data) -{ - /* - * this is the buffer that will be burst-written into the fusb302 - * maximum size necessary = - * 1: FIFO register address - * 4: SOP* tokens - * 1: Token that signifies "next X bytes are not tokens" - * 30: 2 for header and up to 7*4 = 28 for rest of message - * 1: "Insert CRC" Token - * 1: EOP Token - * 1: "Turn transmitter off" token - * 1: "Star Transmission" Command - * - - * 40: 40 bytes worst-case - */ - uint8_t buf[40]; - int buf_pos = 0; - - int reg; - - /* Flush the TXFIFO */ - fusb302_flush_tx_fifo(port); - - switch (type) { - case TCPC_TX_SOP: - - /* put register address first for of burst tcpc write */ - buf[buf_pos++] = TCPC_REG_FIFOS; - - /* Write the SOP Ordered Set into TX FIFO */ - buf[buf_pos++] = FUSB302_TKN_SYNC1; - buf[buf_pos++] = FUSB302_TKN_SYNC1; - buf[buf_pos++] = FUSB302_TKN_SYNC1; - buf[buf_pos++] = FUSB302_TKN_SYNC2; - - return fusb302_send_message(port, header, data, buf, buf_pos); - case TCPC_TX_HARD_RESET: - /* Simply hit the SEND_HARD_RESET bit */ - tcpc_read(port, TCPC_REG_CONTROL3, ®); - reg |= TCPC_REG_CONTROL3_SEND_HARDRESET; - tcpc_write(port, TCPC_REG_CONTROL3, reg); - - break; - case TCPC_TX_BIST_MODE_2: - /* Hit the BIST_MODE2 bit and start TX */ - tcpc_read(port, TCPC_REG_CONTROL1, ®); - reg |= TCPC_REG_CONTROL1_BIST_MODE2; - tcpc_write(port, TCPC_REG_CONTROL1, reg); - - tcpc_read(port, TCPC_REG_CONTROL0, ®); - reg |= TCPC_REG_CONTROL0_TX_START; - tcpc_write(port, TCPC_REG_CONTROL0, reg); - - task_wait_event(PD_T_BIST_TRANSMIT); - - /* Clear BIST mode bit, TX_START is self-clearing */ - tcpc_read(port, TCPC_REG_CONTROL1, ®); - reg &= ~TCPC_REG_CONTROL1_BIST_MODE2; - tcpc_write(port, TCPC_REG_CONTROL1, reg); - - break; - default: - return EC_ERROR_UNIMPLEMENTED; - } - - return 0; -} - -#ifdef CONFIG_USB_PD_VBUS_DETECT_TCPC -static int fusb302_tcpm_get_vbus_level(int port) -{ - int reg; - - /* Read status register */ - tcpc_read(port, TCPC_REG_STATUS0, ®); - - return (reg & TCPC_REG_STATUS0_VBUSOK) ? 1 : 0; -} -#endif - -void fusb302_tcpc_alert(int port) -{ - /* interrupt has been received */ - int interrupt; - int interrupta; - int interruptb; - - /* reading interrupt registers clears them */ - - tcpc_read(port, TCPC_REG_INTERRUPT, &interrupt); - tcpc_read(port, TCPC_REG_INTERRUPTA, &interrupta); - tcpc_read(port, TCPC_REG_INTERRUPTB, &interruptb); - - /* - * Ignore BC_LVL changes when transmitting / receiving PD, - * since CC level will constantly change. - */ - if (state[port].rx_enable) - interrupt &= ~TCPC_REG_INTERRUPT_BC_LVL; - - if (interrupt & TCPC_REG_INTERRUPT_BC_LVL) { - /* CC Status change */ - task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_CC, 0); - } - - if (interrupt & TCPC_REG_INTERRUPT_COLLISION) { - /* packet sending collided */ - pd_transmit_complete(port, TCPC_TX_COMPLETE_FAILED); - } - -#ifdef CONFIG_USB_PD_VBUS_DETECT_TCPC - if (interrupt & TCPC_REG_INTERRUPT_VBUSOK) { - /* VBUS crossed threshold */ -#ifdef CONFIG_USB_CHARGER - usb_charger_vbus_change(port, - fusb302_tcpm_get_vbus_level(port)); -#else - if (!fusb302_tcpm_get_vbus_level(port)) - pd_vbus_low(port); -#endif - task_wake(PD_PORT_TO_TASK_ID(port)); - hook_notify(HOOK_AC_CHANGE); - } -#endif - - /* GoodCRC was received, our FIFO is now non-empty */ - if (interrupta & TCPC_REG_INTERRUPTA_TX_SUCCESS) { - pd_transmit_complete(port, TCPC_TX_COMPLETE_SUCCESS); - } - - if (interrupta & TCPC_REG_INTERRUPTA_RETRYFAIL) { - /* all retries have failed to get a GoodCRC */ - pd_transmit_complete(port, TCPC_TX_COMPLETE_FAILED); - } - - if (interrupta & TCPC_REG_INTERRUPTA_HARDSENT) { - /* hard reset has been sent */ - - /* bring FUSB302 out of reset */ - fusb302_pd_reset(port); - - pd_transmit_complete(port, TCPC_TX_COMPLETE_SUCCESS); - } - - if (interrupta & TCPC_REG_INTERRUPTA_HARDRESET) { - /* hard reset has been received */ - - /* bring FUSB302 out of reset */ - fusb302_pd_reset(port); - - pd_execute_hard_reset(port); - - task_wake(PD_PORT_TO_TASK_ID(port)); - } - - if (interruptb & TCPC_REG_INTERRUPTB_GCRCSENT) { - /* Packet received and GoodCRC sent */ - /* (this interrupt fires after the GoodCRC finishes) */ - if (state[port].rx_enable) { - /* Pull all RX messages from TCPC into EC memory */ - while (!fusb302_rx_fifo_is_empty(port)) - tcpm_enqueue_message(port); - } else { - /* flush rx fifo if rx isn't enabled */ - fusb302_flush_rx_fifo(port); - } - } - -} - -/* For BIST receiving */ -void tcpm_set_bist_test_data(int port) -{ - int reg; - - /* Read control3 register */ - tcpc_read(port, TCPC_REG_CONTROL3, ®); - - /* Set the BIST_TMODE bit (Clears on Hard Reset) */ - reg |= TCPC_REG_CONTROL3_BIST_TMODE; - - /* Write the updated value */ - tcpc_write(port, TCPC_REG_CONTROL3, reg); -} - -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER -static int fusb302_tcpm_enter_low_power_mode(int port) -{ - return tcpc_write(port, TCPC_REG_POWER, TCPC_REG_POWER_PWR_LOW); -} -#endif - -const struct tcpm_drv fusb302_tcpm_drv = { - .init = &fusb302_tcpm_init, - .release = &fusb302_tcpm_release, - .get_cc = &fusb302_tcpm_get_cc, -#ifdef CONFIG_USB_PD_VBUS_DETECT_TCPC - .get_vbus_level = &fusb302_tcpm_get_vbus_level, -#endif - .select_rp_value = &fusb302_tcpm_select_rp_value, - .set_cc = &fusb302_tcpm_set_cc, - .set_polarity = &fusb302_tcpm_set_polarity, - .set_vconn = &fusb302_tcpm_set_vconn, - .set_msg_header = &fusb302_tcpm_set_msg_header, - .set_rx_enable = &fusb302_tcpm_set_rx_enable, - .get_message_raw = &fusb302_tcpm_get_message_raw, - .transmit = &fusb302_tcpm_transmit, - .tcpc_alert = &fusb302_tcpc_alert, -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER - .enter_low_power_mode = &fusb302_tcpm_enter_low_power_mode, -#endif -}; diff --git a/driver/tcpm/fusb302.h b/driver/tcpm/fusb302.h deleted file mode 100644 index ec418407f7..0000000000 --- a/driver/tcpm/fusb302.h +++ /dev/null @@ -1,213 +0,0 @@ -/* 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. - * - * Author: Gabe Noblesmith - */ - -/* USB Power delivery port management */ -/* For Fairchild FUSB302 */ -#ifndef __CROS_EC_DRIVER_TCPM_FUSB302_H -#define __CROS_EC_DRIVER_TCPM_FUSB302_H - -/* Chip Device ID - 302A or 302B */ -#define FUSB302_DEVID_302A 0x08 -#define FUSB302_DEVID_302B 0x09 - -/* I2C slave address varies by part number */ -/* FUSB302BUCX / FUSB302BMPX */ -#define FUSB302_I2C_SLAVE_ADDR_FLAGS 0x22 -/* FUSB302B01MPX */ -#define FUSB302_I2C_SLAVE_ADDR_B01_FLAGS 0x23 -/* FUSB302B10MPX */ -#define FUSB302_I2C_SLAVE_ADDR_B10_FLAGS 0x24 -/* FUSB302B11MPX */ -#define FUSB302_I2C_SLAVE_ADDR_B11_FLAGS 0x25 - -/* Default retry count for transmitting */ -#define PD_RETRY_COUNT 3 - -#define TCPC_REG_DEVICE_ID 0x01 - -#define TCPC_REG_SWITCHES0 0x02 -#define TCPC_REG_SWITCHES0_CC2_PU_EN (1<<7) -#define TCPC_REG_SWITCHES0_CC1_PU_EN (1<<6) -#define TCPC_REG_SWITCHES0_VCONN_CC2 (1<<5) -#define TCPC_REG_SWITCHES0_VCONN_CC1 (1<<4) -#define TCPC_REG_SWITCHES0_MEAS_CC2 (1<<3) -#define TCPC_REG_SWITCHES0_MEAS_CC1 (1<<2) -#define TCPC_REG_SWITCHES0_CC2_PD_EN (1<<1) -#define TCPC_REG_SWITCHES0_CC1_PD_EN (1<<0) - -#define TCPC_REG_SWITCHES1 0x03 -#define TCPC_REG_SWITCHES1_POWERROLE (1<<7) -#define TCPC_REG_SWITCHES1_SPECREV1 (1<<6) -#define TCPC_REG_SWITCHES1_SPECREV0 (1<<5) -#define TCPC_REG_SWITCHES1_DATAROLE (1<<4) -#define TCPC_REG_SWITCHES1_AUTO_GCRC (1<<2) -#define TCPC_REG_SWITCHES1_TXCC2_EN (1<<1) -#define TCPC_REG_SWITCHES1_TXCC1_EN (1<<0) - -#define TCPC_REG_MEASURE 0x04 -#define TCPC_REG_MEASURE_VBUS (1<<6) -/* - * MDAC reference voltage step size is 42 mV. Round our thresholds to reduce - * maximum error, which also matches suggested thresholds in datasheet - * (Table 3. Host Interrupt Summary). - */ -#define TCPC_REG_MEASURE_MDAC_MV(mv) (DIV_ROUND_NEAREST((mv), 42) & 0x3f) - -#define TCPC_REG_CONTROL0 0x06 -#define TCPC_REG_CONTROL0_TX_FLUSH (1<<6) -#define TCPC_REG_CONTROL0_INT_MASK (1<<5) -#define TCPC_REG_CONTROL0_HOST_CUR_MASK (3<<2) -#define TCPC_REG_CONTROL0_HOST_CUR_3A0 (3<<2) -#define TCPC_REG_CONTROL0_HOST_CUR_1A5 (2<<2) -#define TCPC_REG_CONTROL0_HOST_CUR_USB (1<<2) -#define TCPC_REG_CONTROL0_TX_START (1<<0) - -#define TCPC_REG_CONTROL1 0x07 -#define TCPC_REG_CONTROL1_ENSOP2DB (1<<6) -#define TCPC_REG_CONTROL1_ENSOP1DB (1<<5) -#define TCPC_REG_CONTROL1_BIST_MODE2 (1<<4) -#define TCPC_REG_CONTROL1_RX_FLUSH (1<<2) -#define TCPC_REG_CONTROL1_ENSOP2 (1<<1) -#define TCPC_REG_CONTROL1_ENSOP1 (1<<0) - -#define TCPC_REG_CONTROL2 0x08 -/* two-bit field, valid values below */ -#define TCPC_REG_CONTROL2_MODE (1<<1) -#define TCPC_REG_CONTROL2_MODE_DFP (0x3) -#define TCPC_REG_CONTROL2_MODE_UFP (0x2) -#define TCPC_REG_CONTROL2_MODE_DRP (0x1) -#define TCPC_REG_CONTROL2_MODE_POS (1) -#define TCPC_REG_CONTROL2_TOGGLE (1<<0) - -#define TCPC_REG_CONTROL3 0x09 -#define TCPC_REG_CONTROL3_SEND_HARDRESET (1<<6) -#define TCPC_REG_CONTROL3_BIST_TMODE (1<<5) /* 302B Only */ -#define TCPC_REG_CONTROL3_AUTO_HARDRESET (1<<4) -#define TCPC_REG_CONTROL3_AUTO_SOFTRESET (1<<3) -/* two-bit field */ -#define TCPC_REG_CONTROL3_N_RETRIES (1<<1) -#define TCPC_REG_CONTROL3_N_RETRIES_POS (1) -#define TCPC_REG_CONTROL3_N_RETRIES_SIZE (2) -#define TCPC_REG_CONTROL3_AUTO_RETRY (1<<0) - -#define TCPC_REG_MASK 0x0A -#define TCPC_REG_MASK_VBUSOK (1<<7) -#define TCPC_REG_MASK_ACTIVITY (1<<6) -#define TCPC_REG_MASK_COMP_CHNG (1<<5) -#define TCPC_REG_MASK_CRC_CHK (1<<4) -#define TCPC_REG_MASK_ALERT (1<<3) -#define TCPC_REG_MASK_WAKE (1<<2) -#define TCPC_REG_MASK_COLLISION (1<<1) -#define TCPC_REG_MASK_BC_LVL (1<<0) - -#define TCPC_REG_POWER 0x0B -#define TCPC_REG_POWER_PWR (1<<0) /* four-bit field */ -#define TCPC_REG_POWER_PWR_LOW 0x1 /* Bandgap + Wake circuitry */ -#define TCPC_REG_POWER_PWR_MEDIUM 0x3 /* LOW + Receiver + Current refs */ -#define TCPC_REG_POWER_PWR_HIGH 0x7 /* MEDIUM + Measure block */ -#define TCPC_REG_POWER_PWR_ALL 0xF /* HIGH + Internal Oscillator */ - -#define TCPC_REG_RESET 0x0C -#define TCPC_REG_RESET_PD_RESET (1<<1) -#define TCPC_REG_RESET_SW_RESET (1<<0) - -#define TCPC_REG_MASKA 0x0E -#define TCPC_REG_MASKA_OCP_TEMP (1<<7) -#define TCPC_REG_MASKA_TOGDONE (1<<6) -#define TCPC_REG_MASKA_SOFTFAIL (1<<5) -#define TCPC_REG_MASKA_RETRYFAIL (1<<4) -#define TCPC_REG_MASKA_HARDSENT (1<<3) -#define TCPC_REG_MASKA_TX_SUCCESS (1<<2) -#define TCPC_REG_MASKA_SOFTRESET (1<<1) -#define TCPC_REG_MASKA_HARDRESET (1<<0) - -#define TCPC_REG_MASKB 0x0F -#define TCPC_REG_MASKB_GCRCSENT (1<<0) - -#define TCPC_REG_STATUS0A 0x3C -#define TCPC_REG_STATUS0A_SOFTFAIL (1<<5) -#define TCPC_REG_STATUS0A_RETRYFAIL (1<<4) -#define TCPC_REG_STATUS0A_POWER (1<<2) /* two-bit field */ -#define TCPC_REG_STATUS0A_RX_SOFT_RESET (1<<1) -#define TCPC_REG_STATUS0A_RX_HARD_RESEt (1<<0) - -#define TCPC_REG_STATUS1A 0x3D -/* three-bit field, valid values below */ -#define TCPC_REG_STATUS1A_TOGSS (1<<3) -#define TCPC_REG_STATUS1A_TOGSS_RUNNING 0x0 -#define TCPC_REG_STATUS1A_TOGSS_SRC1 0x1 -#define TCPC_REG_STATUS1A_TOGSS_SRC2 0x2 -#define TCPC_REG_STATUS1A_TOGSS_SNK1 0x5 -#define TCPC_REG_STATUS1A_TOGSS_SNK2 0x6 -#define TCPC_REG_STATUS1A_TOGSS_AA 0x7 -#define TCPC_REG_STATUS1A_TOGSS_POS (3) -#define TCPC_REG_STATUS1A_TOGSS_MASK (0x7) - -#define TCPC_REG_STATUS1A_RXSOP2DB (1<<2) -#define TCPC_REG_STATUS1A_RXSOP1DB (1<<1) -#define TCPC_REG_STATUS1A_RXSOP (1<<0) - -#define TCPC_REG_INTERRUPTA 0x3E -#define TCPC_REG_INTERRUPTA_OCP_TEMP (1<<7) -#define TCPC_REG_INTERRUPTA_TOGDONE (1<<6) -#define TCPC_REG_INTERRUPTA_SOFTFAIL (1<<5) -#define TCPC_REG_INTERRUPTA_RETRYFAIL (1<<4) -#define TCPC_REG_INTERRUPTA_HARDSENT (1<<3) -#define TCPC_REG_INTERRUPTA_TX_SUCCESS (1<<2) -#define TCPC_REG_INTERRUPTA_SOFTRESET (1<<1) -#define TCPC_REG_INTERRUPTA_HARDRESET (1<<0) - -#define TCPC_REG_INTERRUPTB 0x3F -#define TCPC_REG_INTERRUPTB_GCRCSENT (1<<0) - -#define TCPC_REG_STATUS0 0x40 -#define TCPC_REG_STATUS0_VBUSOK (1<<7) -#define TCPC_REG_STATUS0_ACTIVITY (1<<6) -#define TCPC_REG_STATUS0_COMP (1<<5) -#define TCPC_REG_STATUS0_CRC_CHK (1<<4) -#define TCPC_REG_STATUS0_ALERT (1<<3) -#define TCPC_REG_STATUS0_WAKE (1<<2) -#define TCPC_REG_STATUS0_BC_LVL1 (1<<1) /* two-bit field */ -#define TCPC_REG_STATUS0_BC_LVL0 (1<<0) /* two-bit field */ - -#define TCPC_REG_STATUS1 0x41 -#define TCPC_REG_STATUS1_RXSOP2 (1<<7) -#define TCPC_REG_STATUS1_RXSOP1 (1<<6) -#define TCPC_REG_STATUS1_RX_EMPTY (1<<5) -#define TCPC_REG_STATUS1_RX_FULL (1<<4) -#define TCPC_REG_STATUS1_TX_EMPTY (1<<3) -#define TCPC_REG_STATUS1_TX_FULL (1<<2) - -#define TCPC_REG_INTERRUPT 0x42 -#define TCPC_REG_INTERRUPT_VBUSOK (1<<7) -#define TCPC_REG_INTERRUPT_ACTIVITY (1<<6) -#define TCPC_REG_INTERRUPT_COMP_CHNG (1<<5) -#define TCPC_REG_INTERRUPT_CRC_CHK (1<<4) -#define TCPC_REG_INTERRUPT_ALERT (1<<3) -#define TCPC_REG_INTERRUPT_WAKE (1<<2) -#define TCPC_REG_INTERRUPT_COLLISION (1<<1) -#define TCPC_REG_INTERRUPT_BC_LVL (1<<0) - -#define TCPC_REG_FIFOS 0x43 - -/* Tokens defined for the FUSB302 TX FIFO */ -enum fusb302_txfifo_tokens { - FUSB302_TKN_TXON = 0xA1, - FUSB302_TKN_SYNC1 = 0x12, - FUSB302_TKN_SYNC2 = 0x13, - FUSB302_TKN_SYNC3 = 0x1B, - FUSB302_TKN_RST1 = 0x15, - FUSB302_TKN_RST2 = 0x16, - FUSB302_TKN_PACKSYM = 0x80, - FUSB302_TKN_JAMCRC = 0xFF, - FUSB302_TKN_EOP = 0x14, - FUSB302_TKN_TXOFF = 0xFE, -}; - -extern const struct tcpm_drv fusb302_tcpm_drv; - -#endif /* __CROS_EC_DRIVER_TCPM_FUSB302_H */ diff --git a/driver/tcpm/it83xx.c b/driver/tcpm/it83xx.c deleted file mode 100644 index 14e7571dc6..0000000000 --- a/driver/tcpm/it83xx.c +++ /dev/null @@ -1,644 +0,0 @@ -/* Copyright 2016 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. - */ - -/* TCPM for MCU also running TCPC */ - -#include "common.h" -#include "config.h" -#include "console.h" -#include "it83xx_pd.h" -#include "registers.h" -#include "system.h" -#include "task.h" -#include "timer.h" -#include "util.h" -#include "usb_pd.h" -#include "usb_pd_tcpm.h" -#include "hooks.h" - -#if defined(CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE) || \ - defined(CONFIG_USB_PD_VBUS_DETECT_TCPC) || \ - defined(CONFIG_USB_PD_TCPC_LOW_POWER) || \ - defined(CONFIG_USB_PD_DISCHARGE_TCPC) -#error "Unsupported config options of IT83xx PD driver" -#endif - -/* Wait time for vconn power switch to turn off. */ -#ifndef PD_IT83XX_VCONN_TURN_OFF_DELAY_US -#define PD_IT83XX_VCONN_TURN_OFF_DELAY_US 500 -#endif - -const struct usbpd_ctrl_t usbpd_ctrl_regs[] = { - {&IT83XX_GPIO_GPCRF4, &IT83XX_GPIO_GPCRF5, IT83XX_IRQ_USBPD0}, - {&IT83XX_GPIO_GPCRH1, &IT83XX_GPIO_GPCRH2, IT83XX_IRQ_USBPD1}, -}; -BUILD_ASSERT(ARRAY_SIZE(usbpd_ctrl_regs) == USBPD_PORT_COUNT); - -/* - * This function disables integrated pd module and enables 5.1K resistor for - * dead battery. A EC reset or calling _init() is able to re-active pd module. - */ -void it83xx_disable_pd_module(int port) -{ - /* This only apply to PD port. */ - if (*usbpd_ctrl_regs[port].cc1 == IT83XX_USBPD_CC_PIN_CONFIG && - *usbpd_ctrl_regs[port].cc2 == IT83XX_USBPD_CC_PIN_CONFIG) { - /* Disable PD PHY */ - IT83XX_USBPD_GCR(port) &= ~(BIT(0) | BIT(4)); - /* Power down CC1/CC2 */ - IT83XX_USBPD_CCGCR(port) |= 0x1f; - /* Disable CC1/CC2 voltage detector */ - IT83XX_USBPD_CCCSR(port) = 0xff; - /* Connect 5.1K resistor to CC1/CC2 for dead battery. */ - IT83XX_USBPD_CCPSR(port) = 0x33; - } -} - -static enum tcpc_cc_voltage_status it83xx_get_cc( - enum usbpd_port port, - enum usbpd_cc_pin cc_pin) -{ - enum usbpd_ufp_volt_status ufp_volt; - enum usbpd_dfp_volt_status dfp_volt; - enum tcpc_cc_voltage_status cc_state = TYPEC_CC_VOLT_OPEN; - int pull; - - pull = (cc_pin == USBPD_CC_PIN_1) ? - USBPD_GET_CC1_PULL_REGISTER_SELECTION(port) : - USBPD_GET_CC2_PULL_REGISTER_SELECTION(port); - - /* select Rp */ - if (pull) - CLEAR_MASK(cc_state, BIT(2)); - /* select Rd */ - else - SET_MASK(cc_state, BIT(2)); - - /* sink */ - if (USBPD_GET_POWER_ROLE(port) == USBPD_POWER_ROLE_CONSUMER) { - if (cc_pin == USBPD_CC_PIN_1) - ufp_volt = IT83XX_USBPD_UFPVDR(port) & 0x7; - else - ufp_volt = (IT83XX_USBPD_UFPVDR(port) >> 4) & 0x7; - - switch (ufp_volt) { - case USBPD_UFP_STATE_SNK_DEF: - cc_state |= (TYPEC_CC_VOLT_RP_DEF & 3); - break; - case USBPD_UFP_STATE_SNK_1_5: - cc_state |= (TYPEC_CC_VOLT_RP_1_5 & 3); - break; - case USBPD_UFP_STATE_SNK_3_0: - cc_state |= (TYPEC_CC_VOLT_RP_3_0 & 3); - break; - case USBPD_UFP_STATE_SNK_OPEN: - cc_state = TYPEC_CC_VOLT_OPEN; - break; - default: - cc_state = TYPEC_CC_VOLT_OPEN; - break; - } - /* source */ - } else { - if (cc_pin == USBPD_CC_PIN_1) - dfp_volt = IT83XX_USBPD_DFPVDR(port) & 0xf; - else - dfp_volt = (IT83XX_USBPD_DFPVDR(port) >> 4) & 0xf; - - switch (dfp_volt) { - case USBPD_DFP_STATE_SRC_RA: - cc_state |= TYPEC_CC_VOLT_RA; - break; - case USBPD_DFP_STATE_SRC_RD: - cc_state |= TYPEC_CC_VOLT_RD; - break; - case USBPD_DFP_STATE_SRC_OPEN: - cc_state = TYPEC_CC_VOLT_OPEN; - break; - default: - cc_state = TYPEC_CC_VOLT_OPEN; - break; - } - } - - return cc_state; -} - -static int it83xx_tcpm_get_message_raw(int port, uint32_t *buf, int *head) -{ - int cnt = PD_HEADER_CNT(IT83XX_USBPD_RMH(port)); - - if (!USBPD_IS_RX_DONE(port)) - return EC_ERROR_UNKNOWN; - - /* store header */ - *head = IT83XX_USBPD_RMH(port); - /* check data message */ - if (cnt) - memcpy(buf, (uint32_t *)&IT83XX_USBPD_RDO0(port), cnt * 4); - /* - * Note: clear RX done interrupt after get the data. - * If clear this bit, USBPD receives next packet - */ - IT83XX_USBPD_MRSR(port) = USBPD_REG_MASK_RX_MSG_VALID; - - return EC_SUCCESS; -} - -static enum tcpc_transmit_complete it83xx_tx_data( - enum usbpd_port port, - enum tcpm_transmit_type type, - uint16_t header, - const uint32_t *buf) -{ - int r; - uint32_t evt; - uint8_t length = PD_HEADER_CNT(header); - - /* set message header */ - IT83XX_USBPD_TMHLR(port) = (uint8_t)header; - IT83XX_USBPD_TMHHR(port) = (header >> 8); - - /* - * SOP type bit[6~4]: - * on bx version and before: - * x00b=SOP, x01b=SOP', x10b=SOP", bit[6] is reserved. - * on dx version: - * 000b=SOP, 001b=SOP', 010b=SOP", 011b=Debug SOP', 100b=Debug SOP''. - */ - IT83XX_USBPD_MTSR1(port) = - (IT83XX_USBPD_MTSR1(port) & ~0x70) | ((type & 0x7) << 4); - /* bit7: transmit message is send to cable or not */ - if (TCPC_TX_SOP == type) - IT83XX_USBPD_MTSR0(port) &= ~USBPD_REG_MASK_CABLE_ENABLE; - else - IT83XX_USBPD_MTSR0(port) |= USBPD_REG_MASK_CABLE_ENABLE; - /* clear msg length */ - IT83XX_USBPD_MTSR1(port) &= (~0x7); - /* Limited by PD_HEADER_CNT() */ - ASSERT(length <= 0x7); - - if (length) { - /* set data bit */ - IT83XX_USBPD_MTSR0(port) |= BIT(4); - /* set data length setting */ - IT83XX_USBPD_MTSR1(port) |= length; - /* set data */ - memcpy((uint32_t *)&IT83XX_USBPD_TDO(port), buf, length * 4); - } - - for (r = 0; r <= PD_RETRY_COUNT; r++) { - /* Start TX */ - USBPD_KICK_TX_START(port); - evt = task_wait_event_mask(TASK_EVENT_PHY_TX_DONE, - PD_T_TCPC_TX_TIMEOUT); - /* check TX status */ - if (USBPD_IS_TX_ERR(port) || (evt & TASK_EVENT_TIMER)) { - /* - * If discard, means HW doesn't send the msg and resend. - */ - if (USBPD_IS_TX_DISCARD(port)) - continue; - /* - * Or port partner doesn't respond GoodCRC - */ - else - return TCPC_TX_COMPLETE_FAILED; - } else { - break; - } - } - - if (r > PD_RETRY_COUNT) - return TCPC_TX_COMPLETE_DISCARDED; - - return TCPC_TX_COMPLETE_SUCCESS; -} - -static enum tcpc_transmit_complete it83xx_send_hw_reset(enum usbpd_port port, - enum tcpm_transmit_type reset_type) -{ - if (reset_type == TCPC_TX_CABLE_RESET) - IT83XX_USBPD_MTSR0(port) |= USBPD_REG_MASK_CABLE_ENABLE; - else - IT83XX_USBPD_MTSR0(port) &= ~USBPD_REG_MASK_CABLE_ENABLE; - - /* send hard reset */ - USBPD_SEND_HARD_RESET(port); - usleep(MSEC); - - if (IT83XX_USBPD_MTSR0(port) & USBPD_REG_MASK_SEND_HW_RESET) - return TCPC_TX_COMPLETE_FAILED; - - return TCPC_TX_COMPLETE_SUCCESS; -} - -static void it83xx_send_bist_mode2_pattern(enum usbpd_port port) -{ - USBPD_ENABLE_SEND_BIST_MODE_2(port); - usleep(PD_T_BIST_TRANSMIT); - USBPD_DISABLE_SEND_BIST_MODE_2(port); -} - -static void it83xx_enable_vconn(enum usbpd_port port, int enabled) -{ - enum usbpd_cc_pin cc_pin; - - if (USBPD_GET_PULL_CC_SELECTION(port)) - cc_pin = USBPD_CC_PIN_1; - else - cc_pin = USBPD_CC_PIN_2; - - if (enabled) { - /* Disable unused CC to become VCONN */ - if (cc_pin == USBPD_CC_PIN_1) { - IT83XX_USBPD_CCCSR(port) = USBPD_CC2_DISCONNECTED(port); - IT83XX_USBPD_CCPSR(port) = (IT83XX_USBPD_CCPSR(port) - & ~USBPD_REG_MASK_DISCONNECT_POWER_CC2) - | USBPD_REG_MASK_DISCONNECT_POWER_CC1; - } else { - IT83XX_USBPD_CCCSR(port) = USBPD_CC1_DISCONNECTED(port); - IT83XX_USBPD_CCPSR(port) = (IT83XX_USBPD_CCPSR(port) - & ~USBPD_REG_MASK_DISCONNECT_POWER_CC1) - | USBPD_REG_MASK_DISCONNECT_POWER_CC2; - } - } else { - /* Enable cc1 and cc2 */ - IT83XX_USBPD_CCCSR(port) &= ~0xaa; - IT83XX_USBPD_CCPSR(port) |= - (USBPD_REG_MASK_DISCONNECT_POWER_CC1 | - USBPD_REG_MASK_DISCONNECT_POWER_CC2); - } -} - -static void it83xx_enable_cc(enum usbpd_port port, int enable) -{ - if (enable) - CLEAR_MASK(IT83XX_USBPD_CCGCR(port), BIT(4)); - else - SET_MASK(IT83XX_USBPD_CCGCR(port), BIT(4)); -} - -static void it83xx_set_power_role(enum usbpd_port port, int power_role) -{ - /* PD_ROLE_SINK 0, PD_ROLE_SOURCE 1 */ - if (power_role == PD_ROLE_SOURCE) { - /* - * bit[2,3] BMC Rx threshold setting - * 00b: power neutral - * 01b: sinking power => - * High to low Y3Rx threshold = 0.38, - * Low to high Y3Rx threshold = 0.54. - * 10b: sourcing power => - * High to low Y3Rx threshold = 0.64, - * Low to high Y3Rx threshold = 0.79. - */ - IT83XX_USBPD_CCADCR(port) = 0x08; - /* bit0: source */ - SET_MASK(IT83XX_USBPD_PDMSR(port), BIT(0)); - /* bit1: CC1 select Rp */ - SET_MASK(IT83XX_USBPD_CCGCR(port), BIT(1)); - /* bit3: CC2 select Rp */ - SET_MASK(IT83XX_USBPD_BMCSR(port), BIT(3)); - } else { - /* - * bit[2,3] BMC Rx threshold setting - * 00b: power neutral - * 01b: sinking power => - * High to low Y3Rx threshold = 0.38, - * Low to high Y3Rx threshold = 0.54 - * 10b: sourcing power => - * High to low Y3Rx threshold = 0.64, - * Low to high Y3Rx threshold = 0.79 - */ - IT83XX_USBPD_CCADCR(port) = 0x04; - /* bit0: sink */ - CLEAR_MASK(IT83XX_USBPD_PDMSR(port), BIT(0)); - /* bit1: CC1 select Rd */ - CLEAR_MASK(IT83XX_USBPD_CCGCR(port), BIT(1)); - /* bit3: CC2 select Rd */ - CLEAR_MASK(IT83XX_USBPD_BMCSR(port), BIT(3)); - } -} - -static void it83xx_set_data_role(enum usbpd_port port, int pd_role) -{ - /* 0: PD_ROLE_UFP 1: PD_ROLE_DFP */ - IT83XX_USBPD_PDMSR(port) = - (IT83XX_USBPD_PDMSR(port) & ~0xc) | ((pd_role & 0x1) << 2); -} - -static void it83xx_init(enum usbpd_port port, int role) -{ -#ifdef IT83XX_USBPD_CC_PARAMETER_RELOAD - /* bit7: Reload CC parameter setting. */ - IT83XX_USBPD_CCPSR0(port) |= BIT(7); -#endif - /* reset and disable HW auto generate message header */ - IT83XX_USBPD_GCR(port) = BIT(5); - USBPD_SW_RESET(port); - /* set SOP: receive SOP message only. - * bit[7]: SOP" support enable. - * bit[6]: SOP' support enable. - * bit[5]: SOP support enable. - */ - IT83XX_USBPD_PDMSR(port) = USBPD_REG_MASK_SOP_ENABLE; - /* W/C status */ - IT83XX_USBPD_ISR(port) = 0xff; - /* enable cc, select cc1 and Rd. */ - IT83XX_USBPD_CCGCR(port) = 0xd; - /* change data role as the same power role */ - it83xx_set_data_role(port, role); - /* set power role */ - it83xx_set_power_role(port, role); - /* disable all interrupts */ - IT83XX_USBPD_IMR(port) = 0xff; - /* enable tx done and reset detect interrupt */ - IT83XX_USBPD_IMR(port) &= ~(USBPD_REG_MASK_MSG_TX_DONE | - USBPD_REG_MASK_HARD_RESET_DETECT); -#ifdef IT83XX_INTC_PLUG_IN_SUPPORT - /* - * when tcpc detect type-c plug in (cc lines voltage change), it will - * interrupt fw to wake pd task, so task can react immediately. - * - * w/c status and unmask TCDCR (detect type-c plug in interrupt default - * is enable). - */ - IT83XX_USBPD_TCDCR(port) = USBPD_REG_PLUG_IN_OUT_DETECT_STAT; -#endif //IT83XX_INTC_PLUG_IN_SUPPORT - IT83XX_USBPD_CCPSR(port) = 0xff; - /* cc connect */ - IT83XX_USBPD_CCCSR(port) = 0; - /* disable vconn */ - it83xx_enable_vconn(port, 0); - /* TX start from high */ - IT83XX_USBPD_CCADCR(port) |= BIT(6); - /* enable cc1/cc2 */ - *usbpd_ctrl_regs[port].cc1 = IT83XX_USBPD_CC_PIN_CONFIG; - *usbpd_ctrl_regs[port].cc2 = IT83XX_USBPD_CC_PIN_CONFIG; - task_clear_pending_irq(usbpd_ctrl_regs[port].irq); - task_enable_irq(usbpd_ctrl_regs[port].irq); - USBPD_START(port); -} - -static void it83xx_select_polarity(enum usbpd_port port, - enum usbpd_cc_pin cc_pin) -{ - /* cc1/cc2 selection */ - if (cc_pin == USBPD_CC_PIN_1) - SET_MASK(IT83XX_USBPD_CCGCR(port), BIT(0)); - else - CLEAR_MASK(IT83XX_USBPD_CCGCR(port), BIT(0)); -} - -static int it83xx_set_cc(enum usbpd_port port, int pull) -{ - int enable_cc = 1; - - switch (pull) { - case TYPEC_CC_RD: - it83xx_set_power_role(port, PD_ROLE_SINK); - break; - case TYPEC_CC_RP: - it83xx_set_power_role(port, PD_ROLE_SOURCE); - break; - case TYPEC_CC_OPEN: - /* Power-down CC1 & CC2 to remove Rp/Rd */ - enable_cc = 0; - break; - default: - return EC_ERROR_UNIMPLEMENTED; - } - - it83xx_enable_cc(port, enable_cc); - return EC_SUCCESS; -} - -static int it83xx_tcpm_init(int port) -{ - /* Initialize physical layer */ - it83xx_init(port, PD_ROLE_DEFAULT(port)); - - return EC_SUCCESS; -} - -static int it83xx_tcpm_release(int port) -{ - return EC_ERROR_UNIMPLEMENTED; -} - -static int it83xx_tcpm_get_cc(int port, enum tcpc_cc_voltage_status *cc1, - enum tcpc_cc_voltage_status *cc2) -{ - *cc2 = it83xx_get_cc(port, USBPD_CC_PIN_2); - *cc1 = it83xx_get_cc(port, USBPD_CC_PIN_1); - - return EC_SUCCESS; -} - -static int it83xx_tcpm_select_rp_value(int port, int rp_sel) -{ - uint8_t rp; - /* - * bit[3-2]: CC output current (when Rp selected) - * 00: reserved - * 01: 330uA outpt (3.0A) - * 10: 180uA outpt (1.5A) - * 11: 80uA outpt (USB default) - */ - switch (rp_sel) { - case TYPEC_RP_1A5: - rp = 2 << 2; - break; - case TYPEC_RP_3A0: - rp = BIT(2); - break; - case TYPEC_RP_USB: - default: - rp = 3 << 2; - break; - } - IT83XX_USBPD_CCGCR(port) = (IT83XX_USBPD_CCGCR(port) & ~(3 << 2)) | rp; - - return EC_SUCCESS; -} - -static int it83xx_tcpm_set_cc(int port, int pull) -{ - return it83xx_set_cc(port, pull); -} - -static int it83xx_tcpm_set_polarity(int port, int polarity) -{ - it83xx_select_polarity(port, polarity); - - return EC_SUCCESS; -} - -static int it83xx_tcpm_set_vconn(int port, int enable) -{ - /* - * IT83XX doesn't have integrated circuit to source CC lines for VCONN. - * An external device like PPC or Power Switch has to source the VCONN. - */ - if (IS_ENABLED(CONFIG_USBC_VCONN)) { - if (enable) { - /* - * Unused cc will become Vconn SRC, disable cc analog - * module (ex.UP/RD/DET/Tx/Rx) and enable 5v tolerant. - */ - it83xx_enable_vconn(port, enable); - if (IS_ENABLED(CONFIG_USB_PD_DECODE_SOP)) - /* Enable tcpc receive SOP' packet */ - IT83XX_USBPD_PDMSR(port) |= - USBPD_REG_MASK_SOPP_ENABLE; - } - - /* Turn on/off vconn power switch. */ - board_pd_vconn_ctrl(port, - USBPD_GET_PULL_CC_SELECTION(port) ? - USBPD_CC_PIN_2 : USBPD_CC_PIN_1, enable); - - if (!enable) { - /* Disable tcpc receive SOP' packet */ - if (IS_ENABLED(CONFIG_USB_PD_DECODE_SOP)) - IT83XX_USBPD_PDMSR(port) &= - ~USBPD_REG_MASK_SOPP_ENABLE; - /* - * We need to make sure cc voltage detector is enabled - * after vconn is turned off to avoid the potential risk - * of voltage fed back into Vcore. - */ - usleep(PD_IT83XX_VCONN_TURN_OFF_DELAY_US); - /* - * Since our cc are not Vconn SRC, enable cc analog - * module (ex.UP/RD/DET/Tx/Rx) and disable 5v tolerant. - */ - it83xx_enable_vconn(port, enable); - } - } - - return EC_SUCCESS; -} - -static int it83xx_tcpm_set_msg_header(int port, int power_role, int data_role) -{ - /* PD_ROLE_SINK 0, PD_ROLE_SOURCE 1 */ - if (power_role == PD_ROLE_SOURCE) - /* bit0: source */ - SET_MASK(IT83XX_USBPD_PDMSR(port), BIT(0)); - else - /* bit0: sink */ - CLEAR_MASK(IT83XX_USBPD_PDMSR(port), BIT(0)); - - it83xx_set_data_role(port, data_role); - - return EC_SUCCESS; -} - -static int it83xx_tcpm_set_rx_enable(int port, int enable) -{ - int i; - - if (enable) { - IT83XX_USBPD_IMR(port) &= ~USBPD_REG_MASK_MSG_RX_DONE; - USBPD_ENABLE_BMC_PHY(port); - } else { - IT83XX_USBPD_IMR(port) |= USBPD_REG_MASK_MSG_RX_DONE; - USBPD_DISABLE_BMC_PHY(port); - } - - /* If any PD port is connected, then disable deep sleep */ - for (i = 0; i < board_get_usb_pd_port_count(); ++i) - if (IT83XX_USBPD_GCR(i) | USBPD_REG_MASK_BMC_PHY) - break; - - if (i == board_get_usb_pd_port_count()) - enable_sleep(SLEEP_MASK_USB_PD); - else - disable_sleep(SLEEP_MASK_USB_PD); - - return EC_SUCCESS; -} - -static int it83xx_tcpm_transmit(int port, - enum tcpm_transmit_type type, - uint16_t header, - const uint32_t *data) -{ - int status = TCPC_TX_COMPLETE_FAILED; - - switch (type) { - case TCPC_TX_SOP: - case TCPC_TX_SOP_PRIME: - case TCPC_TX_SOP_PRIME_PRIME: - case TCPC_TX_SOP_DEBUG_PRIME: - case TCPC_TX_SOP_DEBUG_PRIME_PRIME: - status = it83xx_tx_data(port, - type, - header, - data); - break; - case TCPC_TX_BIST_MODE_2: - it83xx_send_bist_mode2_pattern(port); - status = TCPC_TX_COMPLETE_SUCCESS; - break; - case TCPC_TX_HARD_RESET: - case TCPC_TX_CABLE_RESET: - status = it83xx_send_hw_reset(port, type); - break; - default: - status = TCPC_TX_COMPLETE_FAILED; - break; - } - pd_transmit_complete(port, status); - - return EC_SUCCESS; -} - -static int it83xx_tcpm_get_chip_info(int port, int live, - struct ec_response_pd_chip_info_v1 **chip_info) -{ - static struct ec_response_pd_chip_info_v1 i; - - *chip_info = &i; - i.vendor_id = USB_VID_ITE; - i.product_id = (IT83XX_GCTRL_CHIPID1 << 8) | IT83XX_GCTRL_CHIPID2; - i.device_id = IT83XX_GCTRL_CHIPVER & 0xf; - i.fw_version_number = 0xEC; - - return EC_SUCCESS; -} - -static void it83xx_tcpm_sw_reset(void) -{ - int port = TASK_ID_TO_PD_PORT(task_get_current()); -#ifdef IT83XX_INTC_PLUG_IN_SUPPORT - /* - * Enable detect type-c plug in interrupt, since the pd task has - * detected a type-c physical disconnected. - */ - IT83XX_USBPD_TCDCR(port) &= ~USBPD_REG_PLUG_IN_OUT_DETECT_DISABLE; -#endif //IT83XX_INTC_PLUG_IN_SUPPORT - /* exit BIST test data mode */ - USBPD_SW_RESET(port); -} - -DECLARE_HOOK(HOOK_USB_PD_DISCONNECT, it83xx_tcpm_sw_reset, HOOK_PRIO_DEFAULT); - -const struct tcpm_drv it83xx_tcpm_drv = { - .init = &it83xx_tcpm_init, - .release = &it83xx_tcpm_release, - .get_cc = &it83xx_tcpm_get_cc, - .select_rp_value = &it83xx_tcpm_select_rp_value, - .set_cc = &it83xx_tcpm_set_cc, - .set_polarity = &it83xx_tcpm_set_polarity, - .set_vconn = &it83xx_tcpm_set_vconn, - .set_msg_header = &it83xx_tcpm_set_msg_header, - .set_rx_enable = &it83xx_tcpm_set_rx_enable, - .get_message_raw = &it83xx_tcpm_get_message_raw, - .transmit = &it83xx_tcpm_transmit, - .get_chip_info = &it83xx_tcpm_get_chip_info, -}; diff --git a/driver/tcpm/it83xx_pd.h b/driver/tcpm/it83xx_pd.h deleted file mode 100644 index 9e8bff1342..0000000000 --- a/driver/tcpm/it83xx_pd.h +++ /dev/null @@ -1,109 +0,0 @@ -/* Copyright 2016 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. - */ - -/* USB Power delivery port management */ -#ifndef __CROS_EC_DRIVER_TCPM_IT83XX_H -#define __CROS_EC_DRIVER_TCPM_IT83XX_H - -/* - * Dedicated setting for CC pin. - * This setting will connect CC pin to internal PD module directly without - * applying any GPIO/ALT configuration. - */ -#define IT83XX_USBPD_CC_PIN_CONFIG 0x86 - -#define TASK_EVENT_PHY_TX_DONE TASK_EVENT_CUSTOM_BIT(PD_EVENT_FIRST_FREE_BIT) - -#define SET_MASK(reg, bit_mask) ((reg) |= (bit_mask)) -#define CLEAR_MASK(reg, bit_mask) ((reg) &= (~(bit_mask))) -#define IS_MASK_SET(reg, bit_mask) (((reg) & (bit_mask)) != 0) -#define IS_MASK_CLEAR(reg, bit_mask) (((reg) & (bit_mask)) == 0) - -/* macros for set */ -#define USBPD_KICK_TX_START(port) \ - SET_MASK(IT83XX_USBPD_MTCR(port), \ - USBPD_REG_MASK_TX_START) -#define USBPD_SEND_HARD_RESET(port) \ - SET_MASK(IT83XX_USBPD_MTSR0(port), \ - USBPD_REG_MASK_SEND_HW_RESET) -#define USBPD_SW_RESET(port) \ - SET_MASK(IT83XX_USBPD_GCR(port), \ - USBPD_REG_MASK_SW_RESET_BIT) -#define USBPD_ENABLE_BMC_PHY(port) \ - SET_MASK(IT83XX_USBPD_GCR(port), \ - USBPD_REG_MASK_BMC_PHY) -#define USBPD_DISABLE_BMC_PHY(port) \ - CLEAR_MASK(IT83XX_USBPD_GCR(port), \ - USBPD_REG_MASK_BMC_PHY) -#define USBPD_START(port) \ - CLEAR_MASK(IT83XX_USBPD_CCGCR(port), \ - USBPD_REG_MASK_DISABLE_CC) -#define USBPD_ENABLE_SEND_BIST_MODE_2(port) \ - SET_MASK(IT83XX_USBPD_MTSR0(port), \ - USBPD_REG_MASK_SEND_BIST_MODE_2) -#define USBPD_DISABLE_SEND_BIST_MODE_2(port) \ - CLEAR_MASK(IT83XX_USBPD_MTSR0(port), \ - USBPD_REG_MASK_SEND_BIST_MODE_2) - -/* macros for get */ -#define USBPD_GET_POWER_ROLE(port) \ - (IT83XX_USBPD_PDMSR(port) & 1) -#define USBPD_GET_CC1_PULL_REGISTER_SELECTION(port) \ - (IT83XX_USBPD_CCGCR(port) & BIT(1)) -#define USBPD_GET_CC2_PULL_REGISTER_SELECTION(port) \ - (IT83XX_USBPD_BMCSR(port) & BIT(3)) -#define USBPD_GET_PULL_CC_SELECTION(port) \ - (IT83XX_USBPD_CCGCR(port) & 1) - -/* macros for check */ -#define USBPD_IS_TX_ERR(port) \ - IS_MASK_SET(IT83XX_USBPD_MTCR(port), USBPD_REG_MASK_TX_ERR_STAT) -#define USBPD_IS_TX_DISCARD(port) \ - IS_MASK_SET(IT83XX_USBPD_MTCR(port), USBPD_REG_MASK_TX_DISCARD_STAT) - -/* macros for PD ISR */ -#define USBPD_IS_HARD_RESET_DETECT(port) \ - IS_MASK_SET(IT83XX_USBPD_ISR(port), USBPD_REG_MASK_HARD_RESET_DETECT) -#define USBPD_IS_TX_DONE(port) \ - IS_MASK_SET(IT83XX_USBPD_ISR(port), USBPD_REG_MASK_MSG_TX_DONE) -#define USBPD_IS_RX_DONE(port) \ - IS_MASK_SET(IT83XX_USBPD_ISR(port), USBPD_REG_MASK_MSG_RX_DONE) -#ifdef IT83XX_INTC_PLUG_IN_SUPPORT -#define USBPD_IS_PLUG_IN_OUT_DETECT(port)\ - IS_MASK_SET(IT83XX_USBPD_TCDCR(port), USBPD_REG_PLUG_IN_OUT_DETECT_STAT) -#endif //IT83XX_INTC_PLUG_IN_SUPPORT - -enum usbpd_ufp_volt_status { - USBPD_UFP_STATE_SNK_OPEN = 0, - USBPD_UFP_STATE_SNK_DEF = 1, - USBPD_UFP_STATE_SNK_1_5 = 3, - USBPD_UFP_STATE_SNK_3_0 = 7, -}; - -enum usbpd_dfp_volt_status { - USBPD_DFP_STATE_SRC_RA = 0, - USBPD_DFP_STATE_SRC_RD = 1, - USBPD_DFP_STATE_SRC_OPEN = 3, -}; - -enum usbpd_power_role { - USBPD_POWER_ROLE_CONSUMER, - USBPD_POWER_ROLE_PROVIDER, - USBPD_POWER_ROLE_CONSUMER_PROVIDER, - USBPD_POWER_ROLE_PROVIDER_CONSUMER, -}; - -struct usbpd_ctrl_t { - volatile uint8_t *cc1; - volatile uint8_t *cc2; - uint8_t irq; -}; - -extern const struct usbpd_ctrl_t usbpd_ctrl_regs[]; -extern const struct tcpm_drv it83xx_tcpm_drv; -/* Disable integrated pd module */ -void it83xx_disable_pd_module(int port); - -#endif /* __CROS_EC_DRIVER_TCPM_IT83XX_H */ diff --git a/driver/tcpm/mt6370.c b/driver/tcpm/mt6370.c deleted file mode 100644 index 8a50b480f6..0000000000 --- a/driver/tcpm/mt6370.c +++ /dev/null @@ -1,218 +0,0 @@ -/* Copyright 2018 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. - * - * MT6370 TCPC Driver - */ - -#include "console.h" -#include "hooks.h" -#include "mt6370.h" -#include "task.h" -#include "tcpci.h" -#include "tcpm.h" -#include "timer.h" -#include "usb_mux.h" -#include "usb_pd.h" -#include "util.h" - -#define CPRINTS(format, args...) cprints(CC_USBCHARGE, format, ## args) -#define CPRINTF(format, args...) cprintf(CC_USBCHARGE, format, ## args) - -static int mt6370_polarity; - -/* i2c_write function which won't wake TCPC from low power mode. */ -static int mt6370_i2c_write8(int port, int reg, int val) -{ - return i2c_write8(tcpc_config[port].i2c_info.port, - tcpc_config[port].i2c_info.addr_flags, reg, val); -} - -static int mt6370_init(int port) -{ - int rv, val; - - rv = tcpc_read(port, MT6370_REG_IDLE_CTRL, &val); - - /* Only do soft-reset in shipping mode. (b:122017882) */ - if (!(val & MT6370_REG_SHIPPING_OFF)) { - - /* Software reset. */ - rv = tcpc_write(port, MT6370_REG_SWRESET, 1); - if (rv) - return rv; - - /* Need 1 ms for software reset. */ - msleep(1); - } - - /* The earliest point that we can do generic init. */ - rv = tcpci_tcpm_init(port); - - if (rv) - return rv; - - /* - * AUTO IDLE off, shipping off, select CK_300K from BICIO_320K, - * PD3.0 ext-msg on. - */ - rv = tcpc_write(port, MT6370_REG_IDLE_CTRL, - MT6370_REG_IDLE_SET(0, 1, 0, 0)); - /* CC Detect Debounce 5 */ - rv |= tcpc_write(port, MT6370_REG_TTCPC_FILTER, 5); - /* DRP Duty */ - rv |= tcpc_write(port, MT6370_REG_DRP_TOGGLE_CYCLE, 4); - rv |= tcpc_write16(port, MT6370_REG_DRP_DUTY_CTRL, 400); - /* Vconn OC on */ - rv |= tcpc_write(port, MT6370_REG_VCONN_CLIMITEN, 1); - /* PHY control */ - rv |= tcpc_write(port, MT6370_REG_PHY_CTRL1, - MT6370_REG_PHY_CTRL1_SET(0, 7, 0, 1)); - rv |= tcpc_write(port, MT6370_REG_PHY_CTRL3, 0x82); - - return rv; -} - -static inline int mt6370_init_cc_params(int port, int cc_res) -{ - int rv, en, sel; - - if (cc_res == TYPEC_CC_VOLT_RP_DEF) { /* RXCC threshold : 0.55V */ - en = 1; - sel = MT6370_OCCTRL_600MA | MT6370_MASK_BMCIO_RXDZSEL; - } else { /* RD threshold : 0.4V & RP threshold : 0.7V */ - en = 0; - sel = MT6370_OCCTRL_600MA; - } - rv = tcpc_write(port, MT6370_REG_BMCIO_RXDZEN, en); - if (!rv) - rv = tcpc_write(port, MT6370_REG_BMCIO_RXDZSEL, sel); - return rv; -} - -static int mt6370_get_cc(int port, enum tcpc_cc_voltage_status *cc1, - enum tcpc_cc_voltage_status *cc2) -{ - int status; - int rv; - int role, is_snk; - - rv = tcpc_read(port, TCPC_REG_CC_STATUS, &status); - - /* If tcpc read fails, return error and CC as open */ - if (rv) { - *cc1 = TYPEC_CC_VOLT_OPEN; - *cc2 = TYPEC_CC_VOLT_OPEN; - return rv; - } - - *cc1 = TCPC_REG_CC_STATUS_CC1(status); - *cc2 = TCPC_REG_CC_STATUS_CC2(status); - - /* - * If status is not open, then OR in termination to convert to - * enum tcpc_cc_voltage_status. - * - * MT6370 TCPC follows USB PD 1.0 protocol. When DRP not auto-toggling, - * it will not update the DRP_RESULT bits in TCPC_REG_CC_STATUS, - * instead, we should check CC1/CC2 bits in TCPC_REG_ROLE_CTRL. - */ - rv = tcpc_read(port, TCPC_REG_ROLE_CTRL, &role); - - if (TCPC_REG_ROLE_CTRL_DRP(role)) - is_snk = TCPC_REG_CC_STATUS_TERM(status); - else - /* CC1/CC2 states are the same, checking one-side is enough. */ - is_snk = TCPC_REG_CC_STATUS_CC1(role) == TYPEC_CC_RD; - - if (is_snk) { - if (*cc1 != TYPEC_CC_VOLT_OPEN) - *cc1 |= 0x04; - if (*cc2 != TYPEC_CC_VOLT_OPEN) - *cc2 |= 0x04; - } - - rv = mt6370_init_cc_params(port, (int)mt6370_polarity ? *cc1 : *cc2); - return rv; -} - -static int mt6370_set_cc(int port, int pull) -{ - if (pull == TYPEC_CC_RD) - mt6370_init_cc_params(port, TYPEC_CC_VOLT_RP_DEF); - return tcpci_tcpm_set_cc(port, pull); -} - -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER -static int mt6370_enter_low_power_mode(int port) -{ - int rv; - - /* VBUS_DET_EN for detecting charger plug. */ - rv = tcpc_write(port, MT6370_REG_BMC_CTRL, - MT6370_REG_BMCIO_LPEN | MT6370_REG_VBUS_DET_EN); - - if (rv) - return rv; - - return tcpci_enter_low_power_mode(port); -} -#endif - -static int mt6370_set_polarity(int port, int polarity) -{ - enum tcpc_cc_voltage_status cc1, cc2; - - mt6370_polarity = polarity; - mt6370_get_cc(port, &cc1, &cc2); - return tcpci_tcpm_set_polarity(port, polarity); -} - -int mt6370_vconn_discharge(int port) -{ - /* - * Write to mt6370 in low-power mode may return fail, but it is - * actually written. So we just ignore its return value. - */ - mt6370_i2c_write8(port, MT6370_REG_OVP_FLAG_SEL, - MT6370_REG_DISCHARGE_LVL); - /* Set MT6370_REG_DISCHARGE_EN bit and also the rest default value. */ - mt6370_i2c_write8(port, MT6370_REG_BMC_CTRL, - MT6370_REG_DISCHARGE_EN | - MT6370_REG_BMC_CTRL_DEFAULT); - - return EC_SUCCESS; -} - -/* MT6370 is a TCPCI compatible port controller */ -const struct tcpm_drv mt6370_tcpm_drv = { - .init = &mt6370_init, - .release = &tcpci_tcpm_release, - .get_cc = &mt6370_get_cc, -#ifdef CONFIG_USB_PD_VBUS_DETECT_TCPC - .get_vbus_level = &tcpci_tcpm_get_vbus_level, -#endif - .select_rp_value = &tcpci_tcpm_select_rp_value, - .set_cc = &mt6370_set_cc, - .set_polarity = &mt6370_set_polarity, - .set_vconn = &tcpci_tcpm_set_vconn, - .set_msg_header = &tcpci_tcpm_set_msg_header, - .set_rx_enable = &tcpci_tcpm_set_rx_enable, - .get_message_raw = &tcpci_tcpm_get_message_raw, - .transmit = &tcpci_tcpm_transmit, - .tcpc_alert = &tcpci_tcpc_alert, -#ifdef CONFIG_USB_PD_DISCHARGE_TCPC - .tcpc_discharge_vbus = &tcpci_tcpc_discharge_vbus, -#endif -#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE - .drp_toggle = &tcpci_tcpc_drp_toggle, -#endif - .get_chip_info = &tcpci_get_chip_info, -#ifdef CONFIG_USBC_PPC - .set_snk_ctrl = &tcpci_tcpm_set_snk_ctrl, - .set_src_ctrl = &tcpci_tcpm_set_src_ctrl, -#endif -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER - .enter_low_power_mode = &mt6370_enter_low_power_mode, -#endif -}; diff --git a/driver/tcpm/mt6370.h b/driver/tcpm/mt6370.h deleted file mode 100644 index cdc3112a3e..0000000000 --- a/driver/tcpm/mt6370.h +++ /dev/null @@ -1,200 +0,0 @@ -/* Copyright 2018 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. - * - * MT6370 TCPC Driver - */ - -#ifndef __CROS_EC_USB_PD_TCPM_MT6370_H -#define __CROS_EC_USB_PD_TCPM_MT6370_H - -/* MT6370 Private RegMap */ - -#define MT6370_REG_PHY_CTRL1 0x80 -#define MT6370_REG_PHY_CTRL2 0x81 -#define MT6370_REG_PHY_CTRL3 0x82 -#define MT6370_REG_PHY_CTRL6 0x85 - -#define MT6370_REG_CLK_CTRL2 0x87 -#define MT6370_REG_CLK_CTRL3 0x88 - -#define MT6370_REG_RUST_STATUS 0x8A -#define MT6370_REG_RUST_INT_EVENT 0x8B -#define MT6370_REG_RUST_MASK 0x8C -#define MT6370_REG_BMC_CTRL 0x90 -#define MT6370_REG_BMCIO_RXDZSEL 0x93 -#define MT6370_REG_VCONN_CLIMITEN 0x95 - -#define MT6370_REG_OVP_FLAG_SEL 0x96 - -#define MT6370_REG_RT_STATUS 0x97 -#define MT6370_REG_RT_INT 0x98 -#define MT6370_REG_RT_MASK 0x99 -#define RT5081_REG_BMCIO_RXDZEN 0x9A -#define MT6370_REG_IDLE_CTRL 0x9B -#define MT6370_REG_INTRST_CTRL 0x9C -#define MT6370_REG_WATCHDOG_CTRL 0x9D -#define MT6370_REG_I2CRST_CTRL 0X9E - -#define MT6370_REG_SWRESET 0xA0 -#define MT6370_REG_TTCPC_FILTER 0xA1 -#define MT6370_REG_DRP_TOGGLE_CYCLE 0xA2 -#define MT6370_REG_DRP_DUTY_CTRL 0xA3 -#define MT6370_REG_RUST_DETECTION 0xAD -#define MT6370_REG_RUST_CONTROL 0xAE -#define MT6370_REG_BMCIO_RXDZEN 0xAF -#define MT6370_REG_DRP_RUST 0xB9 - -#define MT6370_REG_UNLOCK_PW2 0xF0 -#define MT6370_REG_UNLOCK_PW1 0xF1 - -#define MT6370_TCPC_I2C_ADDR_FLAGS 0x4E - -/* - * MT6370_REG_PHY_CTRL1 0x80 - */ - -#define MT6370_REG_PHY_CTRL1_SET(retry_discard, toggle_cnt, bus_idle_cnt, \ - rx_filter) \ - ((retry_discard << 7) | (toggle_cnt << 4) | (bus_idle_cnt << 2) | \ - (rx_filter & 0x03)) - -/* - * MT6370_REG_CLK_CTRL2 0x87 - */ - -#define MT6370_REG_CLK_DIV_600K_EN BIT(7) -#define MT6370_REG_CLK_BCLK2_EN BIT(6) -#define MT6370_REG_CLK_BCLK2_TG_EN BIT(5) -#define MT6370_REG_CLK_DIV_300K_EN BIT(3) -#define MT6370_REG_CLK_CK_300K_EN BIT(2) -#define MT6370_REG_CLK_BCLK_EN BIT(1) -#define MT6370_REG_CLK_BCLK_TH_EN BIT(0) - -/* - * MT6370_REG_CLK_CTRL3 0x88 - */ - -#define MT6370_REG_CLK_OSCMUX_RG_EN BIT(7) -#define MT6370_REG_CLK_CK_24M_EN BIT(6) -#define MT6370_REG_CLK_OSC_RG_EN BIT(5) -#define MT6370_REG_CLK_DIV_2P4M_EN BIT(4) -#define MT6370_REG_CLK_CK_2P4M_EN BIT(3) -#define MT6370_REG_CLK_PCLK_EN BIT(2) -#define MT6370_REG_CLK_PCLK_RG_EN BIT(1) -#define MT6370_REG_CLK_PCLK_TG_EN BIT(0) - -/* - * MT6370_REG_RX_TX_DBG 0x8b - */ - -#define MT6370_REG_RX_TX_DBG_RX_BUSY BIT(7) -#define MT6370_REG_RX_TX_DBG_TX_BUSY BIT(6) - -/* - * MT6370_REG_BMC_CTRL 0x90 - */ - -#define MT6370_REG_IDLE_EN BIT(6) -#define MT6370_REG_DISCHARGE_EN BIT(5) -#define MT6370_REG_BMCIO_LPRPRD BIT(4) -#define MT6370_REG_BMCIO_LPEN BIT(3) -#define MT6370_REG_BMCIO_BG_EN BIT(2) -#define MT6370_REG_VBUS_DET_EN BIT(1) -#define MT6370_REG_BMCIO_OSC_EN BIT(0) -#define MT6370_REG_BMC_CTRL_DEFAULT \ - (MT6370_REG_BMCIO_BG_EN | MT6370_REG_VBUS_DET_EN | \ - MT6370_REG_BMCIO_OSC_EN) - -/* - * MT6370_REG_BMCIO_RXDZSEL 0x93 - */ - -#define MT6370_MASK_OCCTRL_SEL 0xE0 -#define MT6370_OCCTRL_600MA 0x80 -#define MT6370_MASK_BMCIO_RXDZSEL BIT(0) - -/* - * MT6370_REG_OVP_FLAG_SEL 0x96 - */ - -#define MT6370_MASK_DISCHARGE_LVL 0x03 -#define MT6370_REG_DISCHARGE_LVL BIT(0) - -/* - * MT6370_REG_RT_STATUS 0x97 - */ - -#define MT6370_REG_RA_DETACH BIT(5) -#define MT6370_REG_VBUS_80 BIT(1) - -/* - * MT6370_REG_RT_INT 0x98 - */ - -#define MT6370_REG_INT_RA_DETACH BIT(5) -#define MT6370_REG_INT_WATCHDOG BIT(2) -#define MT6370_REG_INT_VBUS_80 BIT(1) -#define MT6370_REG_INT_WAKEUP BIT(0) - -/* - * MT6370_REG_RT_MASK 0x99 - */ - -#define MT6370_REG_M_RA_DETACH BIT(5) -#define MT6370_REG_M_WATCHDOG BIT(2) -#define MT6370_REG_M_VBUS_80 BIT(1) -#define MT6370_REG_M_WAKEUP BIT(0) - -/* - * MT6370_REG_IDLE_CTRL 0x9B - */ - -#define MT6370_REG_CK_300K_SEL BIT(7) -#define MT6370_REG_SHIPPING_OFF BIT(5) -#define MT6370_REG_ENEXTMSG BIT(4) -#define MT6370_REG_AUTOIDLE_EN BIT(3) - -/* timeout = (tout*2+1) * 6.4ms */ -#ifdef CONFIG_USB_PD_REV30 -#define MT6370_REG_IDLE_SET(ck300, ship_dis, auto_idle, tout) \ - ((ck300 << 7) | (ship_dis << 5) | (auto_idle << 3) | (tout & 0x07) | \ - MT6370_REG_ENEXTMSG) -#else -#define MT6370_REG_IDLE_SET(ck300, ship_dis, auto_idle, tout) \ - ((ck300 << 7) | (ship_dis << 5) | (auto_idle << 3) | (tout & 0x07)) -#endif - -/* - * MT6370_REG_INTRST_CTRL 0x9C - */ - -#define MT6370_REG_INTRST_EN BIT(7) - -/* timeout = (tout+1) * 0.2sec */ -#define MT6370_REG_INTRST_SET(en, tout) ((en << 7) | (tout & 0x03)) - -/* - * MT6370_REG_WATCHDOG_CTRL 0x9D - */ - -#define MT6370_REG_WATCHDOG_EN BIT(7) - -/* timeout = (tout+1) * 0.4sec */ -#define MT6370_REG_WATCHDOG_CTRL_SET(en, tout) ((en << 7) | (tout & 0x07)) - -/* - * MT6370_REG_I2CRST_CTRL 0x9E - */ - -#define MT6370_REG_I2CRST_EN BIT(7) - -/* timeout = (tout+1) * 12.5ms */ -#define MT6370_REG_I2CRST_SET(en, tout) ((en << 7) | (tout & 0x0f)) - -extern const struct tcpm_drv mt6370_tcpm_drv; - -/* Enable VCONN discharge. */ -int mt6370_vconn_discharge(int port); - -#endif /* __CROS_EC_USB_PD_TCPM_MT6370_H */ diff --git a/driver/tcpm/nct38xx.c b/driver/tcpm/nct38xx.c deleted file mode 100644 index 9d91c21b30..0000000000 --- a/driver/tcpm/nct38xx.c +++ /dev/null @@ -1,384 +0,0 @@ -/* - * Copyright 2019 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. - */ - -/* Type-C port manager for Nuvoton NCT38XX. */ - -#include "common.h" -#include "console.h" -#include "ioexpander_nct38xx.h" -#include "nct38xx.h" -#include "tcpci.h" - -#if !defined(CONFIG_USB_PD_TCPM_TCPCI) -#error "NCT38XX is using part of standard TCPCI control" -#error "Please upgrade your board configuration" -#endif - -#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) -#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) - -#define POLARITY_NORMAL 0 -#define POLARITY_FLIPPED 1 -#define POLARITY_NONE 3 - -static int cable_polarity[CONFIG_USB_PD_PORT_MAX_COUNT]; -static unsigned char txBuf[33]; -static unsigned char rxBuf[33]; -/* Save the selected rp value */ -static int selected_rp[CONFIG_USB_PD_PORT_MAX_COUNT]; - -static int nct38xx_tcpm_init(int port) -{ - int rv = 0; - int reg; - - cable_polarity[port] = POLARITY_NONE; - - rv = tcpci_tcpm_init(port); - if (rv) - return rv; - - /* - * Write to the CONTROL_OUT_EN register to enable: - * [6] - CONNDIREN : Connector direction indication output enable - * [2] - SNKEN : VBUS sink enable output enable - * [0] - SRCEN : VBUS source voltage enable output enable - */ - reg = NCT38XX_REG_CTRL_OUT_EN_SRCEN | - NCT38XX_REG_CTRL_OUT_EN_SNKEN | - NCT38XX_REG_CTRL_OUT_EN_CONNDIREN; - - rv = tcpc_write(port, NCT38XX_REG_CTRL_OUT_EN, reg); - if (rv) - return rv; - - /* Disable OVP */ - rv = tcpc_read(port, TCPC_REG_FAULT_CTRL, ®); - if (rv) - return rv; - reg = reg | TCPC_REG_FAULT_CTRL_VBUS_OVP_FAULT_DIS; - rv = tcpc_write(port, TCPC_REG_FAULT_CTRL, reg); - if (rv) - return rv; - - /* Enable VBus monitor and Disable FRS */ - rv = tcpc_read(port, TCPC_REG_POWER_CTRL, ®); - if (rv) - return rv; - reg = reg & ~(TCPC_REG_POWER_CTRL_VBUS_VOL_MONITOR_DIS | - TCPC_REG_POWER_CTRL_FRS_ENABLE); - rv = tcpc_write(port, TCPC_REG_POWER_CTRL, reg); - if (rv) - return rv; - - /* Set FRS direction for SNK detect, if FRS is enabled */ - if (IS_ENABLED(CONFIG_USB_TYPEC_PD_FAST_ROLE_SWAP)) { - reg = TCPC_REG_DEV_CAP_2_SNK_FR_SWAP; - rv = tcpc_write(port, TCPC_REG_DEV_CAP_2, reg); - if (rv) - return rv; - - reg = TCPC_REG_CONFIG_EXT_1_FR_SWAP_SNK_DIR; - rv = tcpc_write(port, TCPC_REG_CONFIG_EXT_1, reg); - if (rv) - return rv; - } - - /* Start VBus monitor */ - rv = tcpc_write(port, TCPC_REG_COMMAND, - TCPC_REG_COMMAND_ENABLE_VBUS_DETECT); - - /* - * Enable the Vendor Define alert event only when the IO expander - * feature is defined - */ - if (IS_ENABLED(CONFIG_IO_EXPANDER_NCT38XX)) { - int mask; - - rv |= tcpc_read16(port, TCPC_REG_ALERT_MASK, &mask); - mask |= TCPC_REG_ALERT_VENDOR_DEF; - rv |= tcpc_write16(port, TCPC_REG_ALERT_MASK, mask); - } - return rv; -} - -static int tcpci_nct38xx_select_rp_value(int port, int rp) -{ - selected_rp[port] = rp; - return EC_SUCCESS; -} - -static int auto_discharge_disconnect(int port, int enable) -{ - int reg, rv; - - rv = tcpc_read(port, TCPC_REG_POWER_CTRL, ®); - if (rv) - return rv; - - if (enable) - reg = reg | TCPC_REG_POWER_CTRL_AUTO_DISCHARGE_DISCONNECT; - else - reg = reg & ~TCPC_REG_POWER_CTRL_AUTO_DISCHARGE_DISCONNECT; - rv = tcpc_write(port, TCPC_REG_POWER_CTRL, reg); - return rv; - -} - -static int tcpci_nct38xx_check_cable_polarity(int port) -{ - int cc, rv; - - /* Try to check the polarity */ - rv = tcpc_read(port, TCPC_REG_CC_STATUS, &cc); - if (rv) - return rv; - - if (TCPC_REG_CC_STATUS_TERM(cc)) { - /* TCPC is presenting RD (Sink mode) */ - if ((TCPC_REG_CC_STATUS_CC1(cc) != TYPEC_CC_VOLT_OPEN) && - (TCPC_REG_CC_STATUS_CC2(cc) == TYPEC_CC_VOLT_OPEN)) { - /* CC1 active && CC2 open */ - cable_polarity[port] = POLARITY_NORMAL; - } - if ((TCPC_REG_CC_STATUS_CC1(cc) == TYPEC_CC_VOLT_OPEN) && - (TCPC_REG_CC_STATUS_CC2(cc) != TYPEC_CC_VOLT_OPEN)) { - /* CC1 open && CC2 active */ - cable_polarity[port] = POLARITY_FLIPPED; - } - } else { - /* TCPC is presenting RP (Source mode) */ - if ((TCPC_REG_CC_STATUS_CC1(cc) == TYPEC_CC_VOLT_RD) && - (TCPC_REG_CC_STATUS_CC2(cc) != TYPEC_CC_VOLT_RD)) { - /* CC1 active && CC2 open */ - cable_polarity[port] = POLARITY_NORMAL; - } - if ((TCPC_REG_CC_STATUS_CC1(cc) != TYPEC_CC_VOLT_RD) && - (TCPC_REG_CC_STATUS_CC2(cc) == TYPEC_CC_VOLT_RD)) { - /* CC1 open && CC2 active */ - cable_polarity[port] = POLARITY_FLIPPED; - } - } - return rv; -} - - /* - * TODO(crbug.com/951681): This code can be simplified once that bug is fixed. - */ -static int tcpci_nct38xx_set_cc(int port, int pull) -{ - - int rv; - - if (cable_polarity[port] == POLARITY_NONE) { - rv = tcpci_nct38xx_check_cable_polarity(port); - if (rv) - return rv; - } - - if (cable_polarity[port] == POLARITY_NORMAL) { - rv = tcpc_write(port, TCPC_REG_ROLE_CTRL, - TCPC_REG_ROLE_CTRL_SET(0, selected_rp[port], - pull, TYPEC_CC_OPEN)); - } else if (cable_polarity[port] == POLARITY_FLIPPED) { - rv = tcpc_write(port, TCPC_REG_ROLE_CTRL, - TCPC_REG_ROLE_CTRL_SET(0, selected_rp[port], - TYPEC_CC_OPEN, pull)); - } else { - return -1; - } - - return rv; -} - -static int tcpci_nct38xx_get_cc(int port, enum tcpc_cc_voltage_status *cc1, - enum tcpc_cc_voltage_status *cc2) -{ - int rv; - int rc; - - rv = tcpc_read(port, TCPC_REG_ROLE_CTRL, &rc); - if (rv) - return rv; - - rv = tcpci_tcpm_get_cc(port, cc1, cc2); - if (rv) - return rv; - - if (!TCPC_REG_ROLE_CTRL_DRP(rc)) { - if ((*cc1 != TYPEC_CC_VOLT_OPEN) && - (TCPC_REG_ROLE_CTRL_CC1(rc) == TYPEC_CC_RD)) - *cc1 |= 0x4; - if ((*cc2 != TYPEC_CC_VOLT_OPEN) && - (TCPC_REG_ROLE_CTRL_CC2(rc) == TYPEC_CC_RD)) - *cc2 |= 0x04; - } - - return rv; -} - -int tcpci_nct38xx_drp_toggle(int port) -{ - int rv; - - cable_polarity[port] = POLARITY_NONE; - - /* - * The port was disconnected so it is probably a good place to set - * auto-discharge-disconnect to '0' - * - * TODO(crbug.com/951683: this should be removed when common code adds - * auto discharge. - */ - rv = auto_discharge_disconnect(port, 0); - if (rv) - return rv; - - return tcpci_tcpc_drp_toggle(port); - -} - -int tcpci_nct38xx_set_polarity(int port, int polarity) -{ - int rv, reg; - - rv = tcpc_read(port, TCPC_REG_TCPC_CTRL, ®); - if (rv) - return rv; - - reg = polarity ? (reg | TCPC_REG_TCPC_CTRL_SET(1)) : - (reg & ~TCPC_REG_TCPC_CTRL_SET(1)); - - rv = tcpc_write(port, TCPC_REG_TCPC_CTRL, reg); - if (rv) - return rv; - - /* - * Polarity is set after connection so it is probably a good time to set - * auto-discharge-disconnect to '1' - */ - rv = auto_discharge_disconnect(port, 1); - return rv; -} - -int tcpci_nct38xx_transmit(int port, enum tcpm_transmit_type type, - uint16_t header, const uint32_t *data) -{ - int rv, num_obj_byte; - - num_obj_byte = PD_HEADER_CNT(header) * 4; - - txBuf[0] = num_obj_byte + 2; - - txBuf[1] = (unsigned char)(header & 0xFF); - txBuf[2] = (unsigned char)((header >> 8) & 0xFF); - - if (num_obj_byte) { - uint32_t *buf_ptr; - - buf_ptr = (uint32_t *)&txBuf[3]; - memcpy(buf_ptr, data, num_obj_byte); - } - - /* total write size = size header (1 byte) + message size */ - rv = tcpc_write_block(port, TCPC_REG_TX_BYTE_CNT, - (const uint8_t *)txBuf, txBuf[0] + 1); - - rv = tcpc_write(port, TCPC_REG_TRANSMIT, - TCPC_REG_TRANSMIT_SET_WITH_RETRY(type)); - return rv; -} - -static int tcpci_nct38xx_get_message_raw(int port, uint32_t *payload, int *head) -{ - int rv, cnt, num_obj_byte; - - rv = tcpc_read(port, TCPC_REG_RX_BYTE_CNT, &cnt); - - if (rv != EC_SUCCESS || cnt < 3) { - rv = EC_ERROR_UNKNOWN; - goto clear; - } - - rv = tcpc_read_block(port, TCPC_REG_RX_BYTE_CNT, rxBuf, cnt + 1); - if (rv != EC_SUCCESS) - goto clear; - - *head = *(int *)&rxBuf[2]; - num_obj_byte = PD_HEADER_CNT(*head) * 4; - - if (num_obj_byte) { - uint32_t *buf_ptr; - - buf_ptr = (uint32_t *)&rxBuf[4]; - memcpy(payload, buf_ptr, num_obj_byte); - } - -clear: - /* Read complete, clear RX status alert bit */ - tcpc_write16(port, TCPC_REG_ALERT, TCPC_REG_ALERT_RX_STATUS); - - return rv; -} - -static void nct38xx_tcpc_alert(int port) -{ - int alert, rv; - - /* - * If IO expander feature is defined, read the ALERT register first to - * keep the status of Vendor Define bit. Otherwise, the status of ALERT - * register will be cleared after tcpci_tcpc_alert() is executed. - */ - if (IS_ENABLED(CONFIG_IO_EXPANDER_NCT38XX)) - rv = tcpc_read16(port, TCPC_REG_ALERT, &alert); - - /* Process normal TCPC ALERT event and clear status */ - tcpci_tcpc_alert(port); - - /* - * If IO expander feature is defined, check the Vendor Define bit to - * handle the IOEX IO's interrupt event - */ - if (IS_ENABLED(CONFIG_IO_EXPANDER_NCT38XX)) - if (!rv && (alert & TCPC_REG_ALERT_VENDOR_DEF)) - nct38xx_ioex_event_handler(port); - -} -const struct tcpm_drv nct38xx_tcpm_drv = { - .init = &nct38xx_tcpm_init, - .release = &tcpci_tcpm_release, - .get_cc = &tcpci_nct38xx_get_cc, -#ifdef CONFIG_USB_PD_VBUS_DETECT_TCPC - .get_vbus_level = &tcpci_tcpm_get_vbus_level, -#endif - .select_rp_value = &tcpci_nct38xx_select_rp_value, - .set_cc = &tcpci_nct38xx_set_cc, - .set_polarity = &tcpci_nct38xx_set_polarity, - .set_vconn = &tcpci_tcpm_set_vconn, - .set_msg_header = &tcpci_tcpm_set_msg_header, - .set_rx_enable = &tcpci_tcpm_set_rx_enable, - .get_message_raw = &tcpci_nct38xx_get_message_raw, - .transmit = &tcpci_nct38xx_transmit, - .tcpc_alert = &nct38xx_tcpc_alert, -#ifdef CONFIG_USB_PD_DISCHARGE_TCPC - .tcpc_discharge_vbus = &tcpci_tcpc_discharge_vbus, -#endif -#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE - .drp_toggle = &tcpci_nct38xx_drp_toggle, -#endif -#ifdef CONFIG_USBC_PPC - .set_snk_ctrl = &tcpci_tcpm_set_snk_ctrl, - .set_src_ctrl = &tcpci_tcpm_set_src_ctrl, -#endif - .get_chip_info = &tcpci_get_chip_info, -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER - .enter_low_power_mode = &tcpci_enter_low_power_mode, -#endif -#ifdef CONFIG_USB_TYPEC_PD_FAST_ROLE_SWAP - .set_frs_enable = &tcpci_tcpc_fast_role_swap_enable, -#endif -}; diff --git a/driver/tcpm/nct38xx.h b/driver/tcpm/nct38xx.h deleted file mode 100644 index 970c1c8c85..0000000000 --- a/driver/tcpm/nct38xx.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2019 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. - */ - -/* Nuvoton Type-C port controller */ - -#ifndef __CROS_EC_USB_PD_TCPM_NCT38XX_H -#define __CROS_EC_USB_PD_TCPM_NCT38XX_H - -/* Chip variant ID (Part number) */ -#define NCT38XX_VARIANT_MASK 0x1C -#define NCT38XX_VARIANT_3807 0x0 -#define NCT38XX_VARIANT_3808 0x2 - -/* There are two IO ports in NCT3807 */ -#define NCT38XX_NCT3807_MAX_IO_PORT 2 -/* There is only one IO port in NCT3808 */ -#define NCT38XX_NCT3808_MAX_IO_PORT 1 - -#define NCT38XX_SUPPORT_GPIO_FLAGS (GPIO_OPEN_DRAIN | GPIO_INPUT | \ - GPIO_OUTPUT | GPIO_LOW | GPIO_HIGH | GPIO_INT_F_RISING | \ - GPIO_INT_F_FALLING | GPIO_INT_F_HIGH | GPIO_INT_F_LOW) - -/* I2C interface */ -#define NCT38XX_I2C_ADDR1_1_FLAGS 0x70 -#define NCT38XX_I2C_ADDR1_2_FLAGS 0x71 -#define NCT38XX_I2C_ADDR1_3_FLAGS 0x72 -#define NCT38XX_I2C_ADDR1_4_FLAGS 0x73 - -#define NCT38XX_I2C_ADDR2_1_FLAGS 0x74 -#define NCT38XX_I2C_ADDR2_2_FLAGS 0x75 -#define NCT38XX_I2C_ADDR2_3_FLAGS 0x76 -#define NCT38XX_I2C_ADDR2_4_FLAGS 0x77 - -#define NCT38XX_REG_VENDOR_ID_L 0x00 -#define NCT38XX_REG_VENDOR_ID_H 0x01 -#define NCT38XX_VENDOR_ID 0x0416 - -#define NCT38XX_PRODUCT_ID 0xC301 - -#define NCT38XX_REG_GPIO_DATA_IN(n) (0xC0 + ((n) * 8)) -#define NCT38XX_REG_GPIO_DATA_OUT(n) (0xC1 + ((n) * 8)) -#define NCT38XX_REG_GPIO_DIR(n) (0xC2 + ((n) * 8)) -#define NCT38XX_REG_GPIO_OD_SEL(n) (0xC3 + ((n) * 8)) -#define NCT38XX_REG_GPIO_ALERT_RISE(n) (0xC4 + ((n) * 8)) -#define NCT38XX_REG_GPIO_ALERT_FALL(n) (0xC5 + ((n) * 8)) -#define NCT38XX_REG_GPIO_ALERT_LEVEL(n) (0xC6 + ((n) * 8)) -#define NCT38XX_REG_GPIO_ALERT_MASK(n) (0xC7 + ((n) * 8)) -#define NCT38XX_REG_MUX_CONTROL 0xD0 -#define NCT38XX_REG_GPIO_ALERT_STAT(n) (0xD4 + (n)) - -/* NCT3808 only supports GPIO 2/3/4/6/7 */ -#define NCT38XXX_3808_VALID_GPIO_MASK 0xDC - -#define NCT38XX_REG_CTRL_OUT_EN 0xD2 -#define NCT38XX_REG_CTRL_OUT_EN_SRCEN (1 << 0) -#define NCT38XX_REG_CTRL_OUT_EN_FASTEN (1 << 1) -#define NCT38XX_REG_CTRL_OUT_EN_SNKEN (1 << 2) -#define NCT38XX_REG_CTRL_OUT_EN_CONNDIREN (1 << 6) - -#define NCT38XX_RESET_HOLD_DELAY_MS 1 -#define NCT38XX_RESET_POST_DELAY_MS 0 - -extern const struct tcpm_drv nct38xx_tcpm_drv; - -#endif /* defined(__CROS_EC_USB_PD_TCPM_NCT38XX_H) */ diff --git a/driver/tcpm/ps8xxx.c b/driver/tcpm/ps8xxx.c deleted file mode 100644 index 81117a5af1..0000000000 --- a/driver/tcpm/ps8xxx.c +++ /dev/null @@ -1,321 +0,0 @@ -/* Copyright 2017 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. - */ - -/* - * Type-C port manager for Parade PS8XXX with integrated superspeed muxes. - * - * Supported TCPCs: - * - PS8751 - * - PS8805 - */ - -#include "common.h" -#include "ps8xxx.h" -#include "tcpci.h" -#include "tcpm.h" -#include "timer.h" -#include "usb_pd.h" - -#if !defined(CONFIG_USB_PD_TCPM_PS8751) && \ - !defined(CONFIG_USB_PD_TCPM_PS8805) -#error "Unsupported PS8xxx TCPC." -#endif - -#if !defined(CONFIG_USB_PD_TCPM_TCPCI) || \ - !defined(CONFIG_USB_PD_TCPM_MUX) || \ - !defined(CONFIG_USBC_SS_MUX) - -#error "PS8XXX is using a standard TCPCI interface with integrated mux control" -#error "Please upgrade your board configuration" - -#endif - -/* - * timestamp of the next possible toggle to ensure the 2-ms spacing - * between IRQ_HPD. - */ -static uint64_t hpd_deadline[CONFIG_USB_PD_PORT_MAX_COUNT]; - -static int dp_set_hpd(int port, int enable) -{ - int reg; - int rv; - - rv = mux_read(port, MUX_IN_HPD_ASSERTION_REG, ®); - if (rv) - return rv; - if (enable) - reg |= IN_HPD; - else - reg &= ~IN_HPD; - return mux_write(port, MUX_IN_HPD_ASSERTION_REG, reg); -} - -static int dp_set_irq(int port, int enable) -{ - - int reg; - int rv; - - rv = mux_read(port, MUX_IN_HPD_ASSERTION_REG, ®); - if (rv) - return rv; - if (enable) - reg |= HPD_IRQ; - else - reg &= ~HPD_IRQ; - return mux_write(port, MUX_IN_HPD_ASSERTION_REG, reg); -} - -void ps8xxx_tcpc_update_hpd_status(int port, int hpd_lvl, int hpd_irq) -{ - dp_set_hpd(port, hpd_lvl); - - if (hpd_irq) { - uint64_t now = get_time().val; - /* wait for the minimum spacing between IRQ_HPD if needed */ - if (now < hpd_deadline[port]) - usleep(hpd_deadline[port] - now); - - dp_set_irq(port, 0); - usleep(HPD_DSTREAM_DEBOUNCE_IRQ); - dp_set_irq(port, hpd_irq); - } - /* enforce 2-ms delay between HPD pulses */ - hpd_deadline[port] = get_time().val + HPD_USTREAM_DEBOUNCE_LVL; -} - -static int ps8xxx_tcpc_bist_mode_2(int port) -{ - int rv; - - /* Generate BIST for 50ms. */ - rv = tcpc_write(port, - PS8XXX_REG_BIST_CONT_MODE_BYTE0, PS8751_BIST_COUNTER_BYTE0); - rv |= tcpc_write(port, - PS8XXX_REG_BIST_CONT_MODE_BYTE1, PS8751_BIST_COUNTER_BYTE1); - rv |= tcpc_write(port, - PS8XXX_REG_BIST_CONT_MODE_BYTE2, PS8751_BIST_COUNTER_BYTE2); - - /* Auto stop */ - rv |= tcpc_write(port, PS8XXX_REG_BIST_CONT_MODE_CTR, 0); - - /* Start BIST MODE 2 */ - rv |= tcpc_write(port, TCPC_REG_TRANSMIT, TCPC_TX_BIST_MODE_2); - - return rv; -} - -static int ps8xxx_tcpm_transmit(int port, enum tcpm_transmit_type type, - uint16_t header, const uint32_t *data) -{ - if (type == TCPC_TX_BIST_MODE_2) - return ps8xxx_tcpc_bist_mode_2(port); - else - return tcpci_tcpm_transmit(port, type, header, data); -} - -static int ps8xxx_tcpm_release(int port) -{ - int version; - int status; - - status = tcpc_read(port, FW_VER_REG, &version); - if (status != 0) { - /* wait for chip to wake up */ - msleep(10); - } - - return tcpci_tcpm_release(port); -} - -static int ps8xxx_get_chip_info(int port, int live, - struct ec_response_pd_chip_info_v1 **chip_info) -{ - int val; - int rv = tcpci_get_chip_info(port, live, chip_info); - - if (rv) - return rv; - - if (!live) { - (*chip_info)->vendor_id = PS8XXX_VENDOR_ID; - (*chip_info)->product_id = PS8XXX_PRODUCT_ID; - } - - if ((*chip_info)->fw_version_number == 0 || - (*chip_info)->fw_version_number == -1 || live) { - rv = tcpc_read(port, FW_VER_REG, &val); - - if (rv) - return rv; - - (*chip_info)->fw_version_number = val; - } - -#if defined(CONFIG_USB_PD_TCPM_PS8751) && \ - defined(CONFIG_USB_PD_VBUS_DETECT_TCPC) - /* - * Min firmware version of PS8751 to ensure that it can detect Vbus - * properly. See b/109769787#comment7 - */ - (*chip_info)->min_req_fw_version_number = 0x39; -#endif - - return rv; -} - -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER -static int ps8xxx_enter_low_power_mode(int port) -{ - return EC_SUCCESS; -} -#endif - -/* - * DCI is enabled by default and burns about 40 mW when the port is in - * USB2 mode or when a C-to-A dongle is attached, so force it off. - */ - -static int ps8xxx_addr_dci_disable(int port, int i2c_addr, int i2c_reg) -{ - int status; - int dci; - - status = tcpc_addr_read(port, i2c_addr, i2c_reg, &dci); - if (status != EC_SUCCESS) - return status; - if ((dci & PS8XXX_REG_MUX_USB_DCI_CFG_MODE_MASK) != - PS8XXX_REG_MUX_USB_DCI_CFG_MODE_OFF) { - dci &= ~PS8XXX_REG_MUX_USB_DCI_CFG_MODE_MASK; - dci |= PS8XXX_REG_MUX_USB_DCI_CFG_MODE_OFF; - if (tcpc_addr_write(port, i2c_addr, i2c_reg, dci) != EC_SUCCESS) - return status; - } - return EC_SUCCESS; -} - -#ifdef CONFIG_USB_PD_TCPM_PS8805 -static int ps8xxx_dci_disable(int port) -{ - int status, e; - int p1_addr; - - status = tcpc_write(port, PS8XXX_REG_I2C_DEBUGGING_ENABLE, - PS8XXX_REG_I2C_DEBUGGING_ENABLE_ON); - if (status != EC_SUCCESS) - return status; - - p1_addr = tcpc_config[port].i2c_info.addr_flags - - (PS8751_I2C_ADDR1_FLAGS - PS8751_I2C_ADDR1_P1_FLAGS); - status = ps8xxx_addr_dci_disable(port, p1_addr, - PS8805_P1_REG_MUX_USB_DCI_CFG); - - e = tcpc_write(port, PS8XXX_REG_I2C_DEBUGGING_ENABLE, - PS8XXX_REG_I2C_DEBUGGING_ENABLE_OFF); - if (e != EC_SUCCESS) { - if (status == EC_SUCCESS) - status = e; - } - - return status; -} -#endif /* CONFIG_USB_PD_TCPM_PS8805 */ - -#ifdef CONFIG_USB_PD_TCPM_PS8751 -static int ps8xxx_dci_disable(int port) -{ - int p3_addr; - - p3_addr = tcpc_config[port].i2c_info.addr_flags; - return ps8xxx_addr_dci_disable(port, p3_addr, - PS8751_REG_MUX_USB_DCI_CFG); -} -#endif /* CONFIG_USB_PD_TCPM_PS8751 */ - -static int ps8xxx_tcpm_init(int port) -{ - int status; - - status = tcpci_tcpm_init(port); - if (status != EC_SUCCESS) - return status; - - return ps8xxx_dci_disable(port); -} - -const struct tcpm_drv ps8xxx_tcpm_drv = { - .init = &ps8xxx_tcpm_init, - .release = &ps8xxx_tcpm_release, - .get_cc = &tcpci_tcpm_get_cc, -#ifdef CONFIG_USB_PD_VBUS_DETECT_TCPC - .get_vbus_level = &tcpci_tcpm_get_vbus_level, -#endif - .select_rp_value = &tcpci_tcpm_select_rp_value, - .set_cc = &tcpci_tcpm_set_cc, - .set_polarity = &tcpci_tcpm_set_polarity, - .set_vconn = &tcpci_tcpm_set_vconn, - .set_msg_header = &tcpci_tcpm_set_msg_header, - .set_rx_enable = &tcpci_tcpm_set_rx_enable, - .get_message_raw = &tcpci_tcpm_get_message_raw, - .transmit = &ps8xxx_tcpm_transmit, - .tcpc_alert = &tcpci_tcpc_alert, -#ifdef CONFIG_USB_PD_DISCHARGE_TCPC - .tcpc_discharge_vbus = &tcpci_tcpc_discharge_vbus, -#endif -#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE - .drp_toggle = &tcpci_tcpc_drp_toggle, -#endif -#ifdef CONFIG_USBC_PPC - .set_snk_ctrl = &tcpci_tcpm_set_snk_ctrl, - .set_src_ctrl = &tcpci_tcpm_set_src_ctrl, -#endif - .get_chip_info = &ps8xxx_get_chip_info, -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER - .enter_low_power_mode = &ps8xxx_enter_low_power_mode, -#endif -}; - -#ifdef CONFIG_CMD_I2C_STRESS_TEST_TCPC -struct i2c_stress_test_dev ps8xxx_i2c_stress_test_dev = { - .reg_info = { - .read_reg = PS8XXX_REG_VENDOR_ID_L, - .read_val = PS8XXX_VENDOR_ID & 0xFF, - .write_reg = MUX_IN_HPD_ASSERTION_REG, - }, - .i2c_read = &tcpc_i2c_read, - .i2c_write = &tcpc_i2c_write, -}; -#endif /* CONFIG_CMD_I2C_STRESS_TEST_TCPC */ - -static int ps8xxx_mux_init(int port) -{ - tcpci_tcpm_mux_init(port); - - /* If this MUX is also the TCPC, then skip init */ - if (!(usb_muxes[port].flags & USB_MUX_FLAG_NOT_TCPC)) - return EC_SUCCESS; - - /* We always want to be a sink when this device is only being used as a mux - * to support external peripherals better. - */ - return mux_write(port, TCPC_REG_ROLE_CTRL, - TCPC_REG_ROLE_CTRL_SET(0, 1, TYPEC_CC_RD, TYPEC_CC_RD)); -} - -static int ps8xxx_mux_enter_low_power_mode(int port) -{ - mux_write(port, TCPC_REG_ROLE_CTRL, - TCPC_REG_ROLE_CTRL_SET(0, 0, TYPEC_CC_RP, TYPEC_CC_RP)); - return tcpci_tcpm_mux_enter_low_power(port); -} - -/* This is meant for mux-only applications */ -const struct usb_mux_driver ps8xxx_usb_mux_driver = { - .init = &ps8xxx_mux_init, - .set = &tcpci_tcpm_mux_set, - .get = &tcpci_tcpm_mux_get, - .enter_low_power_mode = &ps8xxx_mux_enter_low_power_mode, -}; diff --git a/driver/tcpm/ps8xxx.h b/driver/tcpm/ps8xxx.h deleted file mode 100644 index e2927c8531..0000000000 --- a/driver/tcpm/ps8xxx.h +++ /dev/null @@ -1,81 +0,0 @@ -/* Copyright 2017 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. - */ - -/* Parade Tech Type-C port controller */ - -#ifndef __CROS_EC_USB_PD_TCPM_PS8XXX_H -#define __CROS_EC_USB_PD_TCPM_PS8XXX_H - -/* I2C interface */ -#define PS8751_I2C_ADDR1_P1_FLAGS 0x09 -#define PS8751_I2C_ADDR1_FLAGS 0x0B -#define PS8751_I2C_ADDR2_FLAGS 0x1B -#define PS8751_I2C_ADDR3_FLAGS 0x2B -#define PS8751_I2C_ADDR4_FLAGS 0x4B - -/* Minimum Delay for reset assertion */ -#define PS8XXX_RESET_DELAY_MS 1 - -#define PS8751_BIST_TIMER_FREQ 15000000 -#define PS8751_BIST_DELAY_MS 50 - -#define PS8751_BIST_COUNTER (PS8751_BIST_TIMER_FREQ / MSEC \ - * PS8751_BIST_DELAY_MS) - -#define PS8751_BIST_COUNTER_BYTE0 (PS8751_BIST_COUNTER & 0xff) -#define PS8751_BIST_COUNTER_BYTE1 ((PS8751_BIST_COUNTER >> 8) & 0xff) -#define PS8751_BIST_COUNTER_BYTE2 ((PS8751_BIST_COUNTER >> 16) & 0xff) - -#define PS8XXX_VENDOR_ID 0x1DA0 -#define PS8XXX_REG_I2C_DEBUGGING_ENABLE 0xA0 -#define PS8XXX_REG_I2C_DEBUGGING_ENABLE_ON 0x30 -#define PS8XXX_REG_I2C_DEBUGGING_ENABLE_OFF 0x31 /* default */ -#define PS8XXX_REG_BIST_CONT_MODE_BYTE0 0xBC -#define PS8XXX_REG_BIST_CONT_MODE_BYTE1 0xBD -#define PS8XXX_REG_BIST_CONT_MODE_BYTE2 0xBE -#define PS8XXX_REG_BIST_CONT_MODE_CTR 0xBF -#define PS8XXX_REG_DET_CTRL0 0x08 - -#define PS8XXX_REG_MUX_USB_DCI_CFG_MODE_MASK 0xC0 -#define PS8XXX_REG_MUX_USB_DCI_CFG_MODE_OFF 0x80 - -#if defined(CONFIG_USB_PD_TCPM_PS8751) -/* Vendor defined registers */ -#define PS8XXX_PRODUCT_ID 0x8751 - -#define FW_VER_REG 0x90 -#define PS8XXX_REG_VENDOR_ID_L 0x00 -#define PS8XXX_REG_VENDOR_ID_H 0x01 -#define MUX_IN_HPD_ASSERTION_REG 0xD0 -#define IN_HPD BIT(0) -#define HPD_IRQ BIT(1) -#define PS8XXX_REG_MUX_DP_EQ_CONFIGURATION 0xD3 -#define PS8XXX_REG_MUX_DP_OUTPUT_CONFIGURATION 0xD4 -#define PS8XXX_REG_MUX_USB_C2SS_EQ 0xE7 -#define PS8XXX_REG_MUX_USB_C2SS_HS_THRESHOLD 0xE8 -#define PS8751_REG_MUX_USB_DCI_CFG 0xED - -#elif defined(CONFIG_USB_PD_TCPM_PS8805) -/* Vendor defined registers */ -#define PS8XXX_PRODUCT_ID 0x8805 - -#define PS8805_P1_REG_MUX_USB_DCI_CFG 0x4B -#define FW_VER_REG 0x82 -#define MUX_IN_HPD_ASSERTION_REG 0xD0 -#define IN_HPD BIT(0) -#define HPD_IRQ BIT(1) - -#endif - -extern const struct tcpm_drv ps8xxx_tcpm_drv; -void ps8xxx_tcpc_update_hpd_status(int port, int hpd_lvl, int hpd_irq); - -#ifdef CONFIG_CMD_I2C_STRESS_TEST_TCPC -extern struct i2c_stress_test_dev ps8xxx_i2c_stress_test_dev; -#endif /* defined(CONFIG_CMD_I2C_STRESS_TEST_TCPC) */ - -extern const struct usb_mux_driver ps8xxx_usb_mux_driver; - -#endif /* defined(__CROS_EC_USB_PD_TCPM_PS8XXX_H) */ diff --git a/driver/tcpm/stub.c b/driver/tcpm/stub.c deleted file mode 100644 index 0ebe8af999..0000000000 --- a/driver/tcpm/stub.c +++ /dev/null @@ -1,155 +0,0 @@ -/* 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. - */ - -/* TCPM for MCU also running TCPC */ - -#include "task.h" -#include "tcpci.h" -#include "tcpm.h" -#include "usb_pd.h" -#include "usb_pd_tcpc.h" -#include "usb_pd_tcpm.h" - -static int init_alert_mask(int port) -{ - uint16_t mask; - int rv; - - /* - * Create mask of alert events that will cause the TCPC to - * signal the TCPM via the Alert# gpio line. - */ - mask = TCPC_REG_ALERT_TX_SUCCESS | TCPC_REG_ALERT_TX_FAILED | - TCPC_REG_ALERT_TX_DISCARDED | TCPC_REG_ALERT_RX_STATUS | - TCPC_REG_ALERT_RX_HARD_RST | TCPC_REG_ALERT_CC_STATUS; - /* Set the alert mask in TCPC */ - rv = tcpc_alert_mask_set(port, mask); - - return rv; -} - -static int init_power_status_mask(int port) -{ - return tcpc_set_power_status_mask(port, 0); -} - -int tcpm_init(int port) -{ - int rv; - - tcpc_init(port); - rv = init_alert_mask(port); - if (rv) - return rv; - - return init_power_status_mask(port); -} - -int tcpm_get_cc(int port, enum tcpc_cc_voltage_status *cc1, - enum tcpc_cc_voltage_status *cc2) -{ - return tcpc_get_cc(port, cc1, cc2); -} - -int tcpm_select_rp_value(int port, int rp) -{ - return tcpc_select_rp_value(port, rp); -} - -int tcpm_set_cc(int port, int pull) -{ - return tcpc_set_cc(port, pull); -} - -int tcpm_set_polarity(int port, int polarity) -{ - return tcpc_set_polarity(port, polarity); -} - -int tcpm_set_vconn(int port, int enable) -{ - return tcpc_set_vconn(port, enable); -} - -int tcpm_set_msg_header(int port, int power_role, int data_role) -{ - return tcpc_set_msg_header(port, power_role, data_role); -} - -static int tcpm_alert_status(int port, int *alert) -{ - /* Read TCPC Alert register */ - return tcpc_alert_status(port, alert); -} - -int tcpm_set_rx_enable(int port, int enable) -{ - return tcpc_set_rx_enable(port, enable); -} - -int tcpm_has_pending_message(int port) -{ - return !rx_buf_is_empty(port); -} - -int tcpm_dequeue_message(int port, uint32_t *payload, int *head) -{ - int ret = tcpc_get_message(port, payload, head); - - /* Read complete, clear RX status alert bit */ - tcpc_alert_status_clear(port, TCPC_REG_ALERT_RX_STATUS); - - return ret; -} - -void tcpm_clear_pending_messages(int port) -{ - rx_buf_clear(port); -} - -int tcpm_transmit(int port, enum tcpm_transmit_type type, uint16_t header, - const uint32_t *data) -{ - return tcpc_transmit(port, type, header, data); -} - -void tcpc_alert(int port) -{ - int status; - - /* Read the Alert register from the TCPC */ - tcpm_alert_status(port, &status); - - /* - * Clear alert status for everything except RX_STATUS, which shouldn't - * be cleared until we have successfully retrieved message. - */ - if (status & ~TCPC_REG_ALERT_RX_STATUS) - tcpc_alert_status_clear(port, - status & ~TCPC_REG_ALERT_RX_STATUS); - - if (status & TCPC_REG_ALERT_CC_STATUS) { - /* CC status changed, wake task */ - task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_CC, 0); - } - if (status & TCPC_REG_ALERT_RX_STATUS) { - /* - * message received. since TCPC is compiled in, we - * already woke the PD task up from the phy layer via - * pd_rx_event(), so we don't need to wake it again. - */ - } - if (status & TCPC_REG_ALERT_RX_HARD_RST) { - /* hard reset received */ - pd_execute_hard_reset(port); - task_wake(PD_PORT_TO_TASK_ID(port)); - } - if (status & TCPC_REG_ALERT_TX_COMPLETE) { - /* transmit complete */ - pd_transmit_complete(port, status & TCPC_REG_ALERT_TX_SUCCESS ? - TCPC_TX_COMPLETE_SUCCESS : - TCPC_TX_COMPLETE_FAILED); - } -} diff --git a/driver/tcpm/tcpci.c b/driver/tcpm/tcpci.c deleted file mode 100644 index 001a7ee579..0000000000 --- a/driver/tcpm/tcpci.c +++ /dev/null @@ -1,1014 +0,0 @@ -/* 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. - */ - -/* Type-C port manager */ - -#include "atomic.h" -#include "anx74xx.h" -#include "compile_time_macros.h" -#include "console.h" -#include "ec_commands.h" -#include "ps8xxx.h" -#include "task.h" -#include "tcpci.h" -#include "tcpm.h" -#include "timer.h" -#include "usb_charge.h" -#include "usb_mux.h" -#include "usb_pd.h" -#include "usb_pd_tcpc.h" -#include "util.h" - -#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) -#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) - -#ifdef CONFIG_USB_PD_DECODE_SOP -static int vconn_en[CONFIG_USB_PD_PORT_MAX_COUNT]; -static int rx_en[CONFIG_USB_PD_PORT_MAX_COUNT]; -#endif -static int tcpc_vbus[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/* Save the selected rp value */ -static int selected_rp[CONFIG_USB_PD_PORT_MAX_COUNT]; - - -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER -int tcpc_addr_write(int port, int i2c_addr, int reg, int val) -{ - int rv; - - pd_wait_exit_low_power(port); - - rv = i2c_write8(tcpc_config[port].i2c_info.port, - i2c_addr, reg, val); - - pd_device_accessed(port); - return rv; -} - -int tcpc_write16(int port, int reg, int val) -{ - int rv; - - pd_wait_exit_low_power(port); - - rv = i2c_write16(tcpc_config[port].i2c_info.port, - tcpc_config[port].i2c_info.addr_flags, - reg, val); - - pd_device_accessed(port); - return rv; -} - -int tcpc_addr_read(int port, int i2c_addr, int reg, int *val) -{ - int rv; - - pd_wait_exit_low_power(port); - - rv = i2c_read8(tcpc_config[port].i2c_info.port, - i2c_addr, reg, val); - - pd_device_accessed(port); - return rv; -} - -int tcpc_read16(int port, int reg, int *val) -{ - int rv; - - pd_wait_exit_low_power(port); - - rv = i2c_read16(tcpc_config[port].i2c_info.port, - tcpc_config[port].i2c_info.addr_flags, - reg, val); - - pd_device_accessed(port); - return rv; -} - -int tcpc_read_block(int port, int reg, uint8_t *in, int size) -{ - int rv; - - pd_wait_exit_low_power(port); - - rv = i2c_read_block(tcpc_config[port].i2c_info.port, - tcpc_config[port].i2c_info.addr_flags, - reg, in, size); - - pd_device_accessed(port); - return rv; -} - -int tcpc_write_block(int port, int reg, const uint8_t *out, int size) -{ - int rv; - - pd_wait_exit_low_power(port); - - rv = i2c_write_block(tcpc_config[port].i2c_info.port, - tcpc_config[port].i2c_info.addr_flags, - reg, out, size); - - pd_device_accessed(port); - return rv; -} - -int tcpc_xfer(int port, const uint8_t *out, int out_size, - uint8_t *in, int in_size) -{ - int rv; - /* Dispatching to tcpc_xfer_unlocked reduces code size growth. */ - tcpc_lock(port, 1); - rv = tcpc_xfer_unlocked(port, out, out_size, in, in_size, - I2C_XFER_SINGLE); - tcpc_lock(port, 0); - return rv; -} - -int tcpc_xfer_unlocked(int port, const uint8_t *out, int out_size, - uint8_t *in, int in_size, int flags) -{ - int rv; - - pd_wait_exit_low_power(port); - - rv = i2c_xfer_unlocked(tcpc_config[port].i2c_info.port, - tcpc_config[port].i2c_info.addr_flags, - out, out_size, in, in_size, flags); - - pd_device_accessed(port); - return rv; -} -#endif /* CONFIG_USB_PD_TCPC_LOW_POWER */ - -static int init_alert_mask(int port) -{ - int rv; - uint16_t mask; - - /* - * Create mask of alert events that will cause the TCPC to - * signal the TCPM via the Alert# gpio line. - */ - mask = TCPC_REG_ALERT_TX_SUCCESS | TCPC_REG_ALERT_TX_FAILED | - TCPC_REG_ALERT_TX_DISCARDED | TCPC_REG_ALERT_RX_STATUS | - TCPC_REG_ALERT_RX_HARD_RST | TCPC_REG_ALERT_CC_STATUS -#ifdef CONFIG_USB_PD_VBUS_DETECT_TCPC - | TCPC_REG_ALERT_POWER_STATUS -#endif - ; - /* Set the alert mask in TCPC */ - rv = tcpc_write16(port, TCPC_REG_ALERT_MASK, mask); - - if (IS_ENABLED(CONFIG_USB_TYPEC_PD_FAST_ROLE_SWAP)) { - if (rv) - return rv; - - /* Sink FRS allowed */ - mask = TCPC_REG_ALERT_EXT_SNK_FRS; - rv = tcpc_write(port, TCPC_REG_ALERT_EXT, mask); - } - return rv; -} - -static int clear_alert_mask(int port) -{ - return tcpc_write16(port, TCPC_REG_ALERT_MASK, 0); -} - -static int init_power_status_mask(int port) -{ - uint8_t mask; - int rv; - -#ifdef CONFIG_USB_PD_VBUS_DETECT_TCPC - mask = TCPC_REG_POWER_STATUS_VBUS_PRES; -#else - mask = 0; -#endif - rv = tcpc_write(port, TCPC_REG_POWER_STATUS_MASK , mask); - - return rv; -} - -static int clear_power_status_mask(int port) -{ - return tcpc_write(port, TCPC_REG_POWER_STATUS_MASK, 0); -} - -int tcpci_tcpm_get_cc(int port, enum tcpc_cc_voltage_status *cc1, - enum tcpc_cc_voltage_status *cc2) -{ - int status; - int rv; - - rv = tcpc_read(port, TCPC_REG_CC_STATUS, &status); - - /* If tcpc read fails, return error and CC as open */ - if (rv) { - *cc1 = TYPEC_CC_VOLT_OPEN; - *cc2 = TYPEC_CC_VOLT_OPEN; - return rv; - } - - *cc1 = TCPC_REG_CC_STATUS_CC1(status); - *cc2 = TCPC_REG_CC_STATUS_CC2(status); - - /* - * If status is not open, then OR in termination to convert to - * enum tcpc_cc_voltage_status. - */ - if (*cc1 != TYPEC_CC_VOLT_OPEN) - *cc1 |= TCPC_REG_CC_STATUS_TERM(status) << 2; - if (*cc2 != TYPEC_CC_VOLT_OPEN) - *cc2 |= TCPC_REG_CC_STATUS_TERM(status) << 2; - - return rv; -} - -static int tcpci_tcpm_get_power_status(int port, int *status) -{ - return tcpc_read(port, TCPC_REG_POWER_STATUS, status); -} - -int tcpci_tcpm_select_rp_value(int port, int rp) -{ - selected_rp[port] = rp; - return EC_SUCCESS; -} - -#ifdef CONFIG_USB_PD_DISCHARGE_TCPC -void tcpci_tcpc_discharge_vbus(int port, int enable) -{ - int reg; - - if (tcpc_read(port, TCPC_REG_POWER_CTRL, ®)) - return; - - if (enable) - reg |= TCPC_REG_POWER_CTRL_FORCE_DISCHARGE; - else - reg &= ~TCPC_REG_POWER_CTRL_FORCE_DISCHARGE; - - tcpc_write(port, TCPC_REG_POWER_CTRL, reg); -} -#endif - -static int set_role_ctrl(int port, int toggle, int rp, int pull) -{ - return tcpc_write(port, TCPC_REG_ROLE_CTRL, - TCPC_REG_ROLE_CTRL_SET(toggle, rp, pull, pull)); -} - -int tcpci_tcpm_set_cc(int port, int pull) -{ - /* Set manual control, and set both CC lines to the same pull */ - return set_role_ctrl(port, 0, selected_rp[port], pull); -} - -#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE -int tcpci_tcpc_drp_toggle(int port) -{ - int rv; - - /* Set auto drp toggle */ - rv = set_role_ctrl(port, 1, TYPEC_RP_USB, TYPEC_CC_RD); - - /* Set Look4Connection command */ - rv |= tcpc_write(port, TCPC_REG_COMMAND, - TCPC_REG_COMMAND_LOOK4CONNECTION); - - return rv; -} -#endif - -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER -int tcpci_enter_low_power_mode(int port) -{ - return tcpc_write(port, TCPC_REG_COMMAND, TCPC_REG_COMMAND_I2CIDLE); -} -#endif - -int tcpci_tcpm_set_polarity(int port, int polarity) -{ - return tcpc_write(port, TCPC_REG_TCPC_CTRL, - TCPC_REG_TCPC_CTRL_SET(polarity)); -} - -#ifdef CONFIG_USBC_PPC -int tcpci_tcpm_set_snk_ctrl(int port, int enable) -{ - int cmd = enable ? TCPC_REG_COMMAND_SNK_CTRL_HIGH : - TCPC_REG_COMMAND_SNK_CTRL_LOW; - - return tcpc_write(port, TCPC_REG_COMMAND, cmd); -} - -int tcpci_tcpm_set_src_ctrl(int port, int enable) -{ - int cmd = enable ? TCPC_REG_COMMAND_SRC_CTRL_HIGH : - TCPC_REG_COMMAND_SRC_CTRL_LOW; - - return tcpc_write(port, TCPC_REG_COMMAND, cmd); -} -#endif - -int tcpci_tcpm_set_vconn(int port, int enable) -{ - int reg, rv; - - rv = tcpc_read(port, TCPC_REG_POWER_CTRL, ®); - if (rv) - return rv; - -#ifdef CONFIG_USB_PD_DECODE_SOP - /* save vconn */ - vconn_en[port] = enable; - - if (rx_en[port]) { - int detect_sop_en = TCPC_REG_RX_DETECT_SOP_HRST_MASK; - - if (enable) { - detect_sop_en = - TCPC_REG_RX_DETECT_SOP_SOPP_SOPPP_HRST_MASK; - } - - tcpc_write(port, TCPC_REG_RX_DETECT, detect_sop_en); - } -#endif - reg &= ~TCPC_REG_POWER_CTRL_VCONN(1); - reg |= TCPC_REG_POWER_CTRL_VCONN(enable); - return tcpc_write(port, TCPC_REG_POWER_CTRL, reg); -} - -int tcpci_tcpm_set_msg_header(int port, int power_role, int data_role) -{ - return tcpc_write(port, TCPC_REG_MSG_HDR_INFO, - TCPC_REG_MSG_HDR_INFO_SET(data_role, power_role)); -} - -static int tcpm_alert_status(int port, int *alert) -{ - /* Read TCPC Alert register */ - return tcpc_read16(port, TCPC_REG_ALERT, alert); -} - -static int tcpm_alert_ext_status(int port, int *alert_ext) -{ - /* Read TCPC Extended Alert register */ - return tcpc_read(port, TCPC_REG_ALERT_EXT, alert_ext); -} - -int tcpci_tcpm_set_rx_enable(int port, int enable) -{ - int detect_sop_en = 0; - - if (enable) { - detect_sop_en = TCPC_REG_RX_DETECT_SOP_HRST_MASK; - -#ifdef CONFIG_USB_PD_DECODE_SOP - /* save rx_on */ - rx_en[port] = enable; - - /* - * Only the VCONN Source is allowed to communicate - * with the Cable Plugs. - */ - - if (vconn_en[port]) - detect_sop_en = - TCPC_REG_RX_DETECT_SOP_SOPP_SOPPP_HRST_MASK; -#endif - } - - /* If enable, then set RX detect for SOP and HRST */ - return tcpc_write(port, TCPC_REG_RX_DETECT, detect_sop_en); -} - -#ifdef CONFIG_USB_TYPEC_PD_FAST_ROLE_SWAP -void tcpci_tcpc_fast_role_swap_enable(int port, int enable) -{ - int reg; - - if (tcpc_read(port, TCPC_REG_POWER_CTRL, ®)) - return; - - if (enable) - reg |= TCPC_REG_POWER_CTRL_FRS_ENABLE; - else - reg &= ~TCPC_REG_POWER_CTRL_FRS_ENABLE; - - tcpc_write(port, TCPC_REG_POWER_CTRL, reg); - - board_tcpc_fast_role_swap_enable(port, enable); -} -#endif - -#ifdef CONFIG_USB_PD_VBUS_DETECT_TCPC -int tcpci_tcpm_get_vbus_level(int port) -{ - return tcpc_vbus[port]; -} -#endif - -struct cached_tcpm_message { - uint32_t header; - uint32_t payload[7]; -}; - -int tcpci_tcpm_get_message_raw(int port, uint32_t *payload, int *head) -{ - int rv, cnt, reg = TCPC_REG_RX_DATA; -#ifdef CONFIG_USB_PD_DECODE_SOP - int frm; -#endif - - rv = tcpc_read(port, TCPC_REG_RX_BYTE_CNT, &cnt); - - /* RX_BYTE_CNT includes 3 bytes for frame type and header */ - if (rv != EC_SUCCESS || cnt < 3) { - rv = EC_ERROR_UNKNOWN; - goto clear; - } - cnt -= 3; - if (cnt > member_size(struct cached_tcpm_message, payload)) { - rv = EC_ERROR_UNKNOWN; - goto clear; - } - -#ifdef CONFIG_USB_PD_DECODE_SOP - rv = tcpc_read(port, TCPC_REG_RX_BUF_FRAME_TYPE, &frm); - if (rv != EC_SUCCESS) { - rv = EC_ERROR_UNKNOWN; - goto clear; - } -#endif - - rv = tcpc_read16(port, TCPC_REG_RX_HDR, (int *)head); - -#ifdef CONFIG_USB_PD_DECODE_SOP - /* Encode message address in bits 31 to 28 */ - *head &= 0x0000ffff; - *head |= PD_HEADER_SOP(frm & 7); -#endif - if (rv == EC_SUCCESS && cnt > 0) { - tcpc_read_block(port, reg, (uint8_t *)payload, cnt); - } - -clear: - /* Read complete, clear RX status alert bit */ - tcpc_write16(port, TCPC_REG_ALERT, TCPC_REG_ALERT_RX_STATUS); - - return rv; -} - -/* Cache depth needs to be power of 2 */ -#define CACHE_DEPTH BIT(2) -#define CACHE_DEPTH_MASK (CACHE_DEPTH - 1) - -struct queue { - /* - * Head points to the index of the first empty slot to put a new RX - * message. Must be masked before used in lookup. - */ - uint32_t head; - /* - * Tail points to the index of the first message for the PD task to - * consume. Must be masked before used in lookup. - */ - uint32_t tail; - struct cached_tcpm_message buffer[CACHE_DEPTH]; -}; -static struct queue cached_messages[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/* Note this method can be called from an interrupt context. */ -int tcpm_enqueue_message(const int port) -{ - int rv; - struct queue *const q = &cached_messages[port]; - struct cached_tcpm_message *const head = - &q->buffer[q->head & CACHE_DEPTH_MASK]; - - if (q->head - q->tail == CACHE_DEPTH) { - CPRINTS("C%d RX EC Buffer full!", port); - return EC_ERROR_OVERFLOW; - } - - /* Blank any old message, just in case. */ - memset(head, 0, sizeof(*head)); - /* Call the raw driver without caching */ - rv = tcpc_config[port].drv->get_message_raw(port, head->payload, - &head->header); - if (rv) { - CPRINTS("C%d: Could not retrieve RX message (%d)", port, rv); - return rv; - } - - /* Increment atomically to ensure get_message_raw happens-before */ - atomic_add(&q->head, 1); - - /* Wake PD task up so it can process incoming RX messages */ - task_set_event(PD_PORT_TO_TASK_ID(port), TASK_EVENT_WAKE, 0); - - return EC_SUCCESS; -} - -int tcpm_has_pending_message(const int port) -{ - const struct queue *const q = &cached_messages[port]; - - return q->head != q->tail; -} - -int tcpm_dequeue_message(const int port, uint32_t *const payload, - int *const header) -{ - struct queue *const q = &cached_messages[port]; - struct cached_tcpm_message *const tail = - &q->buffer[q->tail & CACHE_DEPTH_MASK]; - - if (!tcpm_has_pending_message(port)) { - CPRINTS("C%d No message in RX buffer!", port); - return EC_ERROR_BUSY; - } - - /* Copy cache data in to parameters */ - *header = tail->header; - memcpy(payload, tail->payload, sizeof(tail->payload)); - - /* Increment atomically to ensure memcpy happens-before */ - atomic_add(&q->tail, 1); - - return EC_SUCCESS; -} - -void tcpm_clear_pending_messages(int port) -{ - struct queue *const q = &cached_messages[port]; - - q->tail = q->head; -} - -int tcpci_tcpm_transmit(int port, enum tcpm_transmit_type type, - uint16_t header, const uint32_t *data) -{ - int reg = TCPC_REG_TX_DATA; - int rv, cnt = 4*PD_HEADER_CNT(header); - - /* If not SOP* transmission, just write to the transmit register */ - if (type >= NUM_SOP_STAR_TYPES) { - /* - * Per TCPCI spec, do not specify retry (although the TCPC - * should ignore retry field for these 3 types). - */ - return tcpc_write(port, TCPC_REG_TRANSMIT, - TCPC_REG_TRANSMIT_SET_WITHOUT_RETRY(type)); - } - - /* TX_BYTE_CNT includes extra bytes for message header */ - rv = tcpc_write(port, TCPC_REG_TX_BYTE_CNT, cnt + sizeof(header)); - - rv |= tcpc_write16(port, TCPC_REG_TX_HDR, header); - - /* If tcpc read fails, return error */ - if (rv) - return rv; - - if (cnt > 0) { - rv = tcpc_write_block(port, reg, (const uint8_t *)data, cnt); - - /* If tcpc read fails, return error */ - if (rv) - return rv; - } - - /* - * On receiving a received message on SOP, protocol layer - * discards the pending SOP messages queued for transmission. - * But it doesn't do the same for SOP' message. So retry is - * assigned to 0 to avoid multiple transmission. - */ - return tcpc_write(port, TCPC_REG_TRANSMIT, - (type == TCPC_TX_SOP_PRIME) ? - TCPC_REG_TRANSMIT_SET_WITHOUT_RETRY(type) : - TCPC_REG_TRANSMIT_SET_WITH_RETRY(type)); -} - -#ifndef CONFIG_USB_PD_TCPC_LOW_POWER -/* - * Returns true if TCPC has reset based on reading mask registers. Only need to - * check this if the TCPC low power mode (LPM) code isn't compiled in because - * LPM will automatically reset the device when the TCPC exits LPM. - */ -static int register_mask_reset(int port) -{ - int mask; - - mask = 0; - tcpc_read16(port, TCPC_REG_ALERT_MASK, &mask); - if (mask == TCPC_REG_ALERT_MASK_ALL) - return 1; - - mask = 0; - tcpc_read(port, TCPC_REG_POWER_STATUS_MASK, &mask); - if (mask == TCPC_REG_POWER_STATUS_MASK_ALL) - return 1; - - return 0; -} -#endif - -/* - * Don't let the TCPC try to pull from the RX buffer forever. We typical only - * have 1 or 2 messages waiting. - */ -#define MAX_ALLOW_FAILED_RX_READS 10 - -void tcpci_tcpc_alert(int port) -{ - int status = 0; - int alert_ext = 0; - int failed_attempts; - uint32_t pd_event = 0; - - /* Read the Alert register from the TCPC */ - tcpm_alert_status(port, &status); - - /* Get Extended Alert register if needed */ - if (status & TCPC_REG_ALERT_ALERT_EXT) - tcpm_alert_ext_status(port, &alert_ext); - - /* Clear any pending faults */ - if (status & TCPC_REG_ALERT_FAULT) { - int fault; - int fault_rv; - - fault_rv = tcpc_read(port, TCPC_REG_FAULT_STATUS, &fault); - if (!fault_rv) { - CPRINTS("C%d FAULT=0x%02X", port, fault); - - /* Clear any faults that are set */ - fault_rv = tcpc_write(port, - TCPC_REG_FAULT_STATUS, - fault); - if (fault_rv) - CPRINTS("C%d Writing FAULT failed, rv=%d", - port, fault_rv); - } else { - CPRINTS("C%d Reading FAULT failed, rv=%d", - port, fault_rv); - } - } - - /* - * Check for TX complete first b/c PD state machine waits on TX - * completion events. This will send an event to the PD tasks - * immediately - */ - if (status & TCPC_REG_ALERT_TX_COMPLETE) - pd_transmit_complete(port, status & TCPC_REG_ALERT_TX_SUCCESS ? - TCPC_TX_COMPLETE_SUCCESS : - TCPC_TX_COMPLETE_FAILED); - - /* Pull all RX messages from TCPC into EC memory */ - failed_attempts = 0; - while (status & TCPC_REG_ALERT_RX_STATUS) { - if (tcpm_enqueue_message(port)) - ++failed_attempts; - if (tcpm_alert_status(port, &status)) - ++failed_attempts; - - /* Ensure we don't loop endlessly */ - if (failed_attempts >= MAX_ALLOW_FAILED_RX_READS) { - CPRINTS("C%d Cannot consume RX buffer after %d failed attempts!", - port, failed_attempts); - /* - * The port is in a bad state, we don't want to consume - * all EC resources so suspend the port for a little - * while. - */ - pd_set_suspend(port, 1); - pd_deferred_resume(port); - return; - } - } - - /* Clear all pending alert bits */ - if (status) - tcpc_write16(port, TCPC_REG_ALERT, status); - - if (status & TCPC_REG_ALERT_CC_STATUS) { - /* CC status changed, wake task */ - pd_event |= PD_EVENT_CC; - } - if (status & TCPC_REG_ALERT_POWER_STATUS) { - int reg = 0; - /* Read Power Status register */ - tcpci_tcpm_get_power_status(port, ®); - /* Update VBUS status */ - tcpc_vbus[port] = reg & - TCPC_REG_POWER_STATUS_VBUS_PRES ? 1 : 0; -#if defined(CONFIG_USB_PD_VBUS_DETECT_TCPC) && defined(CONFIG_USB_CHARGER) - /* Update charge manager with new VBUS state */ - usb_charger_vbus_change(port, tcpc_vbus[port]); - pd_event |= TASK_EVENT_WAKE; -#endif /* CONFIG_USB_PD_VBUS_DETECT_TCPC && CONFIG_USB_CHARGER */ - } - if (status & TCPC_REG_ALERT_RX_HARD_RST) { - /* hard reset received */ - pd_execute_hard_reset(port); - pd_event |= TASK_EVENT_WAKE; - } - - if (IS_ENABLED(CONFIG_USB_TYPEC_PD_FAST_ROLE_SWAP) - && (alert_ext & TCPC_REG_ALERT_EXT_SNK_FRS)) - pd_got_frs_signal(port); - -#ifndef CONFIG_USB_PD_TCPC_LOW_POWER - /* - * Check registers to see if we can tell that the TCPC has reset. If - * so, perform a tcpc_init. This only needs to happen for devices that - * don't support low power mode as the transition from low power mode - * will automatically reset the device. - */ - if (register_mask_reset(port)) - pd_event |= PD_EVENT_TCPC_RESET; -#endif - - /* - * Wait until all possible TCPC accesses in this function are complete - * prior to setting events and/or waking the pd task. When the PD - * task is woken and runs (which will happen during I2C transactions in - * this function), the pd task may put the TCPC into low power mode and - * the next I2C transaction to the TCPC will cause it to wake again. - */ - if (pd_event) - task_set_event(PD_PORT_TO_TASK_ID(port), pd_event, 0); -} - -/* - * This call will wake up the TCPC if it is in low power mode upon accessing the - * i2c bus (but the pd state machine should put it back into low power mode). - * - * Once it's called, the chip info will be stored in cache, which can be - * accessed by tcpm_get_chip_info without worrying about chip states. - */ -int tcpci_get_chip_info(int port, int live, - struct ec_response_pd_chip_info_v1 **chip_info) -{ - static struct ec_response_pd_chip_info_v1 - info[CONFIG_USB_PD_PORT_MAX_COUNT]; - struct ec_response_pd_chip_info_v1 *i; - int error; - int val; - - if (port >= board_get_usb_pd_port_count()) - return EC_ERROR_INVAL; - - i = &info[port]; - - /* If chip_info is NULL, chip info will be stored in cache and can be - * read later by another call. */ - if (chip_info) - *chip_info = i; - - /* If already cached && live data is not asked, return cached value */ - if (i->vendor_id && !live) - return EC_SUCCESS; - - error = tcpc_read16(port, TCPC_REG_VENDOR_ID, &val); - if (error) - return error; - i->vendor_id = val; - - error = tcpc_read16(port, TCPC_REG_PRODUCT_ID, &val); - if (error) - return error; - i->product_id = val; - - error = tcpc_read16(port, TCPC_REG_BCD_DEV, &val); - if (error) - return error; - i->device_id = val; - - /* - * This varies chip to chip; more specific driver code is expected to - * override this value if it can. - */ - i->fw_version_number = -1; - - return EC_SUCCESS; -} - -/* - * Dissociate from the TCPC. - */ - -int tcpci_tcpm_release(int port) -{ - int error; - - error = clear_alert_mask(port); - if (error) - return error; - error = clear_power_status_mask(port); - if (error) - return error; - /* Clear pending interrupts */ - error = tcpc_write16(port, TCPC_REG_ALERT, 0xffff); - if (error) - return error; - - return EC_SUCCESS; -} - -/* - * On TCPC i2c failure, make 30 tries (at least 300ms) before giving up - * in order to allow the TCPC time to boot / reset. - */ -#define TCPM_INIT_TRIES 30 - -int tcpci_tcpm_init(int port) -{ - int error; - int power_status; - int tries = TCPM_INIT_TRIES; - - if (port >= board_get_usb_pd_port_count()) - return EC_ERROR_INVAL; - - while (1) { - error = tcpc_read(port, TCPC_REG_POWER_STATUS, &power_status); - /* - * If read succeeds and the uninitialized bit is clear, then - * initialization is complete, clear all alert bits and write - * the initial alert mask. - */ - if (!error && !(power_status & TCPC_REG_POWER_STATUS_UNINIT)) - break; - if (--tries <= 0) - return error ? error : EC_ERROR_TIMEOUT; - msleep(10); - } - - tcpc_write16(port, TCPC_REG_ALERT, 0xffff); - /* Initialize power_status_mask */ - init_power_status_mask(port); - /* Update VBUS status */ - tcpc_vbus[port] = power_status & - TCPC_REG_POWER_STATUS_VBUS_PRES ? 1 : 0; -#if defined(CONFIG_USB_PD_VBUS_DETECT_TCPC) && defined(CONFIG_USB_CHARGER) - /* - * Set Vbus change now in case the TCPC doesn't send a power status - * changed interrupt for it later. - */ - usb_charger_vbus_change(port, tcpc_vbus[port]); -#endif - error = init_alert_mask(port); - if (error) - return error; - - /* Read chip info here when we know the chip is awake. */ - tcpm_get_chip_info(port, 1, NULL); - - return EC_SUCCESS; -} - -#ifdef CONFIG_USB_PD_TCPM_MUX - -/* - * When the TCPC/MUX device is only used for the MUX, we need to initialize it - * via mux init because tcpc_init won't run for the device. This is borrowed - * from tcpc_init. - */ -int tcpci_tcpm_mux_init(int port) -{ - int error; - int power_status; - int tries = TCPM_INIT_TRIES; - - /* If this MUX is also the TCPC, then skip init */ - if (!(usb_muxes[port].flags & USB_MUX_FLAG_NOT_TCPC)) - return EC_SUCCESS; - - /* Wait for the device to exit low power state */ - while (1) { - error = mux_read(port, TCPC_REG_POWER_STATUS, &power_status); - /* - * If read succeeds and the uninitialized bit is clear, then - * initialization is complete. - */ - if (!error && !(power_status & TCPC_REG_POWER_STATUS_UNINIT)) - break; - if (--tries <= 0) - return error ? error : EC_ERROR_TIMEOUT; - msleep(10); - } - - /* Turn off all alerts and acknowledge any pending IRQ */ - error = mux_write16(port, TCPC_REG_ALERT_MASK, 0); - error |= mux_write16(port, TCPC_REG_ALERT, 0xffff); - - return error ? EC_ERROR_UNKNOWN : EC_SUCCESS; -} - -int tcpci_tcpm_mux_enter_low_power(int port) -{ - /* If this MUX is also the TCPC, then skip low power */ - if (!(usb_muxes[port].flags & USB_MUX_FLAG_NOT_TCPC)) - return EC_SUCCESS; - - return mux_write(port, TCPC_REG_COMMAND, TCPC_REG_COMMAND_I2CIDLE); -} - -int tcpci_tcpm_mux_set(int port, mux_state_t mux_state) -{ - int reg = 0; - int rv; - - /* Parameter is port only */ - rv = mux_read(port, TCPC_REG_CONFIG_STD_OUTPUT, ®); - if (rv != EC_SUCCESS) - return rv; - - reg &= ~(TCPC_REG_CONFIG_STD_OUTPUT_MUX_MASK | - TCPC_REG_CONFIG_STD_OUTPUT_CONNECTOR_FLIPPED); - if (mux_state & MUX_USB_ENABLED) - reg |= TCPC_REG_CONFIG_STD_OUTPUT_MUX_USB; - if (mux_state & MUX_DP_ENABLED) - reg |= TCPC_REG_CONFIG_STD_OUTPUT_MUX_DP; - if (mux_state & MUX_POLARITY_INVERTED) - reg |= TCPC_REG_CONFIG_STD_OUTPUT_CONNECTOR_FLIPPED; - - /* Parameter is port only */ - return mux_write(port, TCPC_REG_CONFIG_STD_OUTPUT, reg); -} - -/* Reads control register and updates mux_state accordingly */ -int tcpci_tcpm_mux_get(int port, mux_state_t *mux_state) -{ - int reg = 0; - int rv; - - *mux_state = 0; - - /* Parameter is port only */ - rv = mux_read(port, TCPC_REG_CONFIG_STD_OUTPUT, ®); - - if (rv != EC_SUCCESS) - return rv; - - if (reg & TCPC_REG_CONFIG_STD_OUTPUT_MUX_USB) - *mux_state |= MUX_USB_ENABLED; - if (reg & TCPC_REG_CONFIG_STD_OUTPUT_MUX_DP) - *mux_state |= MUX_DP_ENABLED; - if (reg & TCPC_REG_CONFIG_STD_OUTPUT_CONNECTOR_FLIPPED) - *mux_state |= MUX_POLARITY_INVERTED; - - return EC_SUCCESS; -} - -const struct usb_mux_driver tcpci_tcpm_usb_mux_driver = { - .init = &tcpci_tcpm_mux_init, - .set = &tcpci_tcpm_mux_set, - .get = &tcpci_tcpm_mux_get, - .enter_low_power_mode = &tcpci_tcpm_mux_enter_low_power, -}; - -#endif /* CONFIG_USB_PD_TCPM_MUX */ - -const struct tcpm_drv tcpci_tcpm_drv = { - .init = &tcpci_tcpm_init, - .release = &tcpci_tcpm_release, - .get_cc = &tcpci_tcpm_get_cc, -#ifdef CONFIG_USB_PD_VBUS_DETECT_TCPC - .get_vbus_level = &tcpci_tcpm_get_vbus_level, -#endif - .select_rp_value = &tcpci_tcpm_select_rp_value, - .set_cc = &tcpci_tcpm_set_cc, - .set_polarity = &tcpci_tcpm_set_polarity, - .set_vconn = &tcpci_tcpm_set_vconn, - .set_msg_header = &tcpci_tcpm_set_msg_header, - .set_rx_enable = &tcpci_tcpm_set_rx_enable, - .get_message_raw = &tcpci_tcpm_get_message_raw, - .transmit = &tcpci_tcpm_transmit, - .tcpc_alert = &tcpci_tcpc_alert, -#ifdef CONFIG_USB_PD_DISCHARGE_TCPC - .tcpc_discharge_vbus = &tcpci_tcpc_discharge_vbus, -#endif -#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE - .drp_toggle = &tcpci_tcpc_drp_toggle, -#endif - .get_chip_info = &tcpci_get_chip_info, -#ifdef CONFIG_USBC_PPC - .set_snk_ctrl = &tcpci_tcpm_set_snk_ctrl, - .set_src_ctrl = &tcpci_tcpm_set_src_ctrl, -#endif -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER - .enter_low_power_mode = &tcpci_enter_low_power_mode, -#endif -}; diff --git a/driver/tcpm/tcpci.h b/driver/tcpm/tcpci.h deleted file mode 100644 index 930bfd6d70..0000000000 --- a/driver/tcpm/tcpci.h +++ /dev/null @@ -1,191 +0,0 @@ -/* 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. - */ - -/* USB Power delivery port management */ - -#ifndef __CROS_EC_USB_PD_TCPM_TCPCI_H -#define __CROS_EC_USB_PD_TCPM_TCPCI_H - -#include "tcpm.h" -#include "usb_mux.h" -#include "usb_pd_tcpm.h" - -#define TCPC_REG_VENDOR_ID 0x0 -#define TCPC_REG_PRODUCT_ID 0x2 -#define TCPC_REG_BCD_DEV 0x4 -#define TCPC_REG_TC_REV 0x6 -#define TCPC_REG_PD_REV 0x8 -#define TCPC_REG_PD_INT_REV 0xa - -#define TCPC_REG_ALERT 0x10 -#define TCPC_REG_ALERT_MASK_ALL 0xffff -#define TCPC_REG_ALERT_VENDOR_DEF BIT(15) -#define TCPC_REG_ALERT_ALERT_EXT BIT(14) -#define TCPC_REG_ALERT_VBUS_DISCNCT BIT(11) -#define TCPC_REG_ALERT_RX_BUF_OVF BIT(10) -#define TCPC_REG_ALERT_FAULT BIT(9) -#define TCPC_REG_ALERT_V_ALARM_LO BIT(8) -#define TCPC_REG_ALERT_V_ALARM_HI BIT(7) -#define TCPC_REG_ALERT_TX_SUCCESS BIT(6) -#define TCPC_REG_ALERT_TX_DISCARDED BIT(5) -#define TCPC_REG_ALERT_TX_FAILED BIT(4) -#define TCPC_REG_ALERT_RX_HARD_RST BIT(3) -#define TCPC_REG_ALERT_RX_STATUS BIT(2) -#define TCPC_REG_ALERT_POWER_STATUS BIT(1) -#define TCPC_REG_ALERT_CC_STATUS BIT(0) -#define TCPC_REG_ALERT_TX_COMPLETE (TCPC_REG_ALERT_TX_SUCCESS | \ - TCPC_REG_ALERT_TX_DISCARDED | \ - TCPC_REG_ALERT_TX_FAILED) - -#define TCPC_REG_ALERT_MASK 0x12 -#define TCPC_REG_POWER_STATUS_MASK 0x14 -#define TCPC_REG_FAULT_STATUS_MASK 0x15 - -#define TCPC_REG_CONFIG_STD_OUTPUT 0x18 -#define TCPC_REG_CONFIG_STD_OUTPUT_MUX_MASK (3 << 2) -#define TCPC_REG_CONFIG_STD_OUTPUT_MUX_NONE (0 << 2) -#define TCPC_REG_CONFIG_STD_OUTPUT_MUX_USB BIT(2) -#define TCPC_REG_CONFIG_STD_OUTPUT_MUX_DP (2 << 2) -#define TCPC_REG_CONFIG_STD_OUTPUT_CONNECTOR_FLIPPED BIT(0) - -#define TCPC_REG_TCPC_CTRL 0x19 -#define TCPC_REG_TCPC_CTRL_SET(polarity) (polarity) -#define TCPC_REG_TCPC_CTRL_POLARITY(reg) ((reg) & 0x1) - -#define TCPC_REG_ROLE_CTRL 0x1a -#define TCPC_REG_ROLE_CTRL_SET(drp, rp, cc1, cc2) \ - ((drp) << 6 | (rp) << 4 | (cc2) << 2 | (cc1)) -#define TCPC_REG_ROLE_CTRL_DRP(reg) (((reg) & 0x40) >> 6) -#define TCPC_REG_ROLE_CTRL_RP_MASK 0x30 -#define TCPC_REG_ROLE_CTRL_RP(reg) (((reg) & TCPC_REG_ROLE_CTRL_RP_MASK) >> 4) -#define TCPC_REG_ROLE_CTRL_CC2(reg) (((reg) & 0xc) >> 2) -#define TCPC_REG_ROLE_CTRL_CC1(reg) ((reg) & 0x3) - -#define TCPC_REG_FAULT_CTRL 0x1b -#define TCPC_REG_FAULT_CTRL_VBUS_OVP_FAULT_DIS BIT(1) - -#define TCPC_REG_POWER_CTRL 0x1c -#define TCPC_REG_POWER_CTRL_FRS_ENABLE BIT(7) -#define TCPC_REG_POWER_CTRL_VBUS_VOL_MONITOR_DIS BIT(6) -#define TCPC_REG_POWER_CTRL_AUTO_DISCHARGE_DISCONNECT BIT(4) -#define TCPC_REG_POWER_CTRL_FORCE_DISCHARGE BIT(2) -#define TCPC_REG_POWER_CTRL_SET(vconn) (vconn) -#define TCPC_REG_POWER_CTRL_VCONN(reg) ((reg) & 0x1) - -#define TCPC_REG_CC_STATUS 0x1d -#define TCPC_REG_CC_STATUS_LOOK4CONNECTION(reg) ((reg & 0x20) >> 5) -#define TCPC_REG_CC_STATUS_SET(term, cc1, cc2) \ - ((term) << 4 | ((cc2) & 0x3) << 2 | ((cc1) & 0x3)) -#define TCPC_REG_CC_STATUS_TERM(reg) (((reg) & 0x10) >> 4) -#define TCPC_REG_CC_STATUS_CC2(reg) (((reg) & 0xc) >> 2) -#define TCPC_REG_CC_STATUS_CC1(reg) ((reg) & 0x3) - -#define TCPC_REG_POWER_STATUS 0x1e -#define TCPC_REG_POWER_STATUS_MASK_ALL 0xff -#define TCPC_REG_POWER_STATUS_UNINIT BIT(6) -#define TCPC_REG_POWER_STATUS_VBUS_DET BIT(3) -#define TCPC_REG_POWER_STATUS_VBUS_PRES BIT(2) - -#define TCPC_REG_FAULT_STATUS 0x1f - -#define TCPC_REG_ALERT_EXT 0x21 -#define TCPC_REG_ALERT_EXT_TIMER_EXPIRED BIT(2) -#define TCPC_REG_ALERT_EXT_SRC_FRS BIT(1) -#define TCPC_REG_ALERT_EXT_SNK_FRS BIT(0) - -#define TCPC_REG_COMMAND 0x23 -#define TCPC_REG_COMMAND_ENABLE_VBUS_DETECT 0x33 -#define TCPC_REG_COMMAND_SNK_CTRL_LOW 0x44 -#define TCPC_REG_COMMAND_SNK_CTRL_HIGH 0x55 -#define TCPC_REG_COMMAND_SRC_CTRL_LOW 0x66 -#define TCPC_REG_COMMAND_SRC_CTRL_HIGH 0x77 -#define TCPC_REG_COMMAND_LOOK4CONNECTION 0x99 -#define TCPC_REG_COMMAND_I2CIDLE 0xFF - -#define TCPC_REG_DEV_CAP_1 0x24 - -#define TCPC_REG_DEV_CAP_2 0x26 -#define TCPC_REG_DEV_CAP_2_SNK_FR_SWAP BIT(9) - -#define TCPC_REG_STD_INPUT_CAP 0x28 -#define TCPC_REG_STD_OUTPUT_CAP 0x29 - -#define TCPC_REG_CONFIG_EXT_1 0x2A -#define TCPC_REG_CONFIG_EXT_1_FR_SWAP_SNK_DIR BIT(1) - -#define TCPC_REG_MSG_HDR_INFO 0x2e -#define TCPC_REG_MSG_HDR_INFO_SET(drole, prole) \ - ((drole) << 3 | (PD_REV20 << 1) | (prole)) -#define TCPC_REG_MSG_HDR_INFO_DROLE(reg) (((reg) & 0x8) >> 3) -#define TCPC_REG_MSG_HDR_INFO_PROLE(reg) ((reg) & 0x1) - -#define TCPC_REG_RX_DETECT 0x2f -#define TCPC_REG_RX_DETECT_SOP_HRST_MASK 0x21 -#define TCPC_REG_RX_DETECT_SOP_SOPP_SOPPP_HRST_MASK 0x27 -#define TCPC_REG_RX_BYTE_CNT 0x30 -#define TCPC_REG_RX_BUF_FRAME_TYPE 0x31 - -#define TCPC_REG_RX_HDR 0x32 -#define TCPC_REG_RX_DATA 0x34 /* through 0x4f */ - -#define TCPC_REG_TRANSMIT 0x50 -#define TCPC_REG_TRANSMIT_SET_WITH_RETRY(type) \ - (PD_RETRY_COUNT << 4 | (type)) -#define TCPC_REG_TRANSMIT_SET_WITHOUT_RETRY(type) (type) -#define TCPC_REG_TRANSMIT_RETRY(reg) (((reg) & 0x30) >> 4) -#define TCPC_REG_TRANSMIT_TYPE(reg) ((reg) & 0x7) - -#define TCPC_REG_TX_BYTE_CNT 0x51 -#define TCPC_REG_TX_HDR 0x52 -#define TCPC_REG_TX_DATA 0x54 /* through 0x6f */ - -#define TCPC_REG_VBUS_VOLTAGE 0x70 -#define TCPC_REG_VBUS_SINK_DISCONNECT_THRESH 0x72 -#define TCPC_REG_VBUS_STOP_DISCHARGE_THRESH 0x74 -#define TCPC_REG_VBUS_VOLTAGE_ALARM_HI_CFG 0x76 -#define TCPC_REG_VBUS_VOLTAGE_ALARM_LO_CFG 0x78 - -extern const struct tcpm_drv tcpci_tcpm_drv; -extern const struct usb_mux_driver tcpci_tcpm_usb_mux_driver; - -void tcpci_tcpc_alert(int port); -int tcpci_tcpm_init(int port); -int tcpci_tcpm_get_cc(int port, enum tcpc_cc_voltage_status *cc1, - enum tcpc_cc_voltage_status *cc2); -int tcpci_tcpm_get_vbus_level(int port); -int tcpci_tcpm_select_rp_value(int port, int rp); -int tcpci_tcpm_set_cc(int port, int pull); -int tcpci_tcpm_set_polarity(int port, int polarity); -int tcpci_tcpm_set_vconn(int port, int enable); -int tcpci_tcpm_set_msg_header(int port, int power_role, int data_role); -int tcpci_tcpm_set_rx_enable(int port, int enable); -int tcpci_tcpm_get_message_raw(int port, uint32_t *payload, int *head); -int tcpci_tcpm_transmit(int port, enum tcpm_transmit_type type, - uint16_t header, const uint32_t *data); -int tcpci_tcpm_release(int port); -#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE -int tcpci_tcpc_drp_toggle(int port); -#endif -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER -int tcpci_enter_low_power_mode(int port); -#endif -#ifdef CONFIG_USB_PD_DISCHARGE_TCPC -void tcpci_tcpc_discharge_vbus(int port, int enable); -#endif - -int tcpci_tcpm_mux_init(int i2c_addr); -int tcpci_tcpm_mux_set(int i2c_addr, mux_state_t mux_state); -int tcpci_tcpm_mux_get(int i2c_addr, mux_state_t *mux_state); -int tcpci_tcpm_mux_enter_low_power(int port); -int tcpci_get_chip_info(int port, int live, - struct ec_response_pd_chip_info_v1 **chip_info); -#ifdef CONFIG_USBC_PPC -int tcpci_tcpm_set_snk_ctrl(int port, int enable); -int tcpci_tcpm_set_src_ctrl(int port, int enable); -#endif - -void tcpci_tcpc_fast_role_swap_enable(int port, int enable); - -#endif /* __CROS_EC_USB_PD_TCPM_TCPCI_H */ diff --git a/driver/tcpm/tcpm.h b/driver/tcpm/tcpm.h deleted file mode 100644 index 51b315fb77..0000000000 --- a/driver/tcpm/tcpm.h +++ /dev/null @@ -1,418 +0,0 @@ -/* 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. - */ - -/* USB Power delivery port management - common header for TCPM drivers */ - -#ifndef __CROS_EC_USB_PD_TCPM_TCPM_H -#define __CROS_EC_USB_PD_TCPM_TCPM_H - -#include "common.h" -#include "ec_commands.h" -#include "gpio.h" -#include "i2c.h" -#include "usb_pd_tcpm.h" -#include "util.h" - -#if defined(CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE) && \ - !defined(CONFIG_USB_PD_DUAL_ROLE) -#error "DRP auto toggle requires board to have DRP support" -#error "Please upgrade your board configuration" -#endif - -#ifndef CONFIG_USB_PD_TCPC - -/* I2C wrapper functions - get I2C port / slave addr from config struct. */ -#ifndef CONFIG_USB_PD_TCPC_LOW_POWER -static inline int tcpc_addr_write(int port, int i2c_addr, int reg, int val) -{ - return i2c_write8(tcpc_config[port].i2c_info.port, - i2c_addr, reg, val); -} - -static inline int tcpc_write16(int port, int reg, int val) -{ - return i2c_write16(tcpc_config[port].i2c_info.port, - tcpc_config[port].i2c_info.addr_flags, - reg, val); -} - -static inline int tcpc_addr_read(int port, int i2c_addr, int reg, int *val) -{ - return i2c_read8(tcpc_config[port].i2c_info.port, - i2c_addr, reg, val); -} - -static inline int tcpc_read16(int port, int reg, int *val) -{ - return i2c_read16(tcpc_config[port].i2c_info.port, - tcpc_config[port].i2c_info.addr_flags, - reg, val); -} - -static inline int tcpc_xfer(int port, const uint8_t *out, int out_size, - uint8_t *in, int in_size) -{ - return i2c_xfer(tcpc_config[port].i2c_info.port, - tcpc_config[port].i2c_info.addr_flags, - out, out_size, in, in_size); -} - -static inline int tcpc_xfer_unlocked(int port, const uint8_t *out, int out_size, - uint8_t *in, int in_size, int flags) -{ - return i2c_xfer_unlocked(tcpc_config[port].i2c_info.port, - tcpc_config[port].i2c_info.addr_flags, - out, out_size, in, in_size, flags); -} - -static inline int tcpc_read_block(int port, int reg, uint8_t *in, int size) -{ - return i2c_read_block(tcpc_config[port].i2c_info.port, - tcpc_config[port].i2c_info.addr_flags, - reg, in, size); -} - -static inline int tcpc_write_block(int port, int reg, - const uint8_t *out, int size) -{ - return i2c_write_block(tcpc_config[port].i2c_info.port, - tcpc_config[port].i2c_info.addr_flags, - reg, out, size); -} - -#else /* !CONFIG_USB_PD_TCPC_LOW_POWER */ -int tcpc_addr_write(int port, int i2c_addr, int reg, int val); -int tcpc_write16(int port, int reg, int val); -int tcpc_addr_read(int port, int i2c_addr, int reg, int *val); -int tcpc_read16(int port, int reg, int *val); -int tcpc_read_block(int port, int reg, uint8_t *in, int size); -int tcpc_write_block(int port, int reg, const uint8_t *out, int size); -int tcpc_xfer(int port, const uint8_t *out, int out_size, - uint8_t *in, int in_size); -int tcpc_xfer_unlocked(int port, const uint8_t *out, int out_size, - uint8_t *in, int in_size, int flags); - -#endif /* CONFIG_USB_PD_TCPC_LOW_POWER */ - -static inline int tcpc_write(int port, int reg, int val) -{ - return tcpc_addr_write(port, - tcpc_config[port].i2c_info.addr_flags, reg, val); -} - -static inline int tcpc_read(int port, int reg, int *val) -{ - return tcpc_addr_read(port, - tcpc_config[port].i2c_info.addr_flags, reg, val); -} - -static inline void tcpc_lock(int port, int lock) -{ - i2c_lock(tcpc_config[port].i2c_info.port, lock); -} - -/* TCPM driver wrapper function */ -static inline int tcpm_init(int port) -{ - int rv; - - rv = tcpc_config[port].drv->init(port); - if (rv) - return rv; - - /* Board specific post TCPC init */ - if (board_tcpc_post_init) - rv = board_tcpc_post_init(port); - - return rv; -} - -static inline int tcpm_release(int port) -{ - return tcpc_config[port].drv->release(port); -} - -static inline int tcpm_get_cc(int port, enum tcpc_cc_voltage_status *cc1, - enum tcpc_cc_voltage_status *cc2) -{ - return tcpc_config[port].drv->get_cc(port, cc1, cc2); -} - -static inline int tcpm_get_vbus_level(int port) -{ - return tcpc_config[port].drv->get_vbus_level(port); -} - -static inline int tcpm_select_rp_value(int port, int rp) -{ - return tcpc_config[port].drv->select_rp_value(port, rp); -} - -static inline int tcpm_set_cc(int port, int pull) -{ - return tcpc_config[port].drv->set_cc(port, pull); -} - -static inline int tcpm_set_polarity(int port, int polarity) -{ - return tcpc_config[port].drv->set_polarity(port, polarity); -} - -static inline int tcpm_set_vconn(int port, int enable) -{ - return tcpc_config[port].drv->set_vconn(port, enable); -} - -static inline int tcpm_set_msg_header(int port, int power_role, int data_role) -{ - return tcpc_config[port].drv->set_msg_header(port, power_role, - data_role); -} - -static inline int tcpm_set_rx_enable(int port, int enable) -{ - return tcpc_config[port].drv->set_rx_enable(port, enable); -} - -/** - * Reads a message using get_message_raw driver method and puts it into EC's - * cache. - */ -int tcpm_enqueue_message(int port); - -static inline int tcpm_transmit(int port, enum tcpm_transmit_type type, - uint16_t header, const uint32_t *data) -{ - return tcpc_config[port].drv->transmit(port, type, header, data); -} - -#ifdef CONFIG_USBC_PPC -static inline int tcpm_set_snk_ctrl(int port, int enable) -{ - if (tcpc_config[port].drv->set_snk_ctrl != NULL) - return tcpc_config[port].drv->set_snk_ctrl(port, enable); - else - return EC_ERROR_UNIMPLEMENTED; -} - -static inline int tcpm_set_src_ctrl(int port, int enable) -{ - if (tcpc_config[port].drv->set_src_ctrl != NULL) - return tcpc_config[port].drv->set_src_ctrl(port, enable); - else - return EC_ERROR_UNIMPLEMENTED; -} -#endif - -static inline void tcpc_alert(int port) -{ - tcpc_config[port].drv->tcpc_alert(port); -} - -static inline void tcpc_discharge_vbus(int port, int enable) -{ - tcpc_config[port].drv->tcpc_discharge_vbus(port, enable); -} - -#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE -static inline int tcpm_auto_toggle_supported(int port) -{ - return !!tcpc_config[port].drv->drp_toggle; -} - -static inline int tcpm_enable_drp_toggle(int port) -{ - return tcpc_config[port].drv->drp_toggle(port); -} -#endif - -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER -static inline int tcpm_enter_low_power_mode(int port) -{ - return tcpc_config[port].drv->enter_low_power_mode(port); -} -#endif - -#ifdef CONFIG_CMD_I2C_STRESS_TEST_TCPC -static inline int tcpc_i2c_read(const int port, const uint16_t addr_flags, - const int reg, int *data) -{ - return tcpc_read(port, reg, data); -} - -static inline int tcpc_i2c_write(const int port, const uint16_t addr_flags, - const int reg, int data) -{ - return tcpc_write(port, reg, data); -} -#endif - -static inline int tcpm_get_chip_info(int port, int live, - struct ec_response_pd_chip_info_v1 **info) -{ - if (tcpc_config[port].drv->get_chip_info) - return tcpc_config[port].drv->get_chip_info(port, live, info); - return EC_ERROR_UNIMPLEMENTED; -} - -#else - -/** - * Initialize TCPM driver and wait for TCPC readiness. - * - * @param port Type-C port number - * - * @return EC_SUCCESS or error - */ -int tcpm_init(int port); - -/** - * Read the CC line status. - * - * @param port Type-C port number - * @param cc1 pointer to CC status for CC1 - * @param cc2 pointer to CC status for CC2 - * - * @return EC_SUCCESS or error - */ -int tcpm_get_cc(int port, enum tcpc_cc_voltage_status *cc1, - enum tcpc_cc_voltage_status *cc2); - -/** - * Read VBUS - * - * @param port Type-C port number - * - * @return 0 => VBUS not detected, 1 => VBUS detected - */ -int tcpm_get_vbus_level(int port); - -/** - * Set the value of the CC pull-up used when we are a source. - * - * @param port Type-C port number - * @param rp One of enum tcpc_rp_value - * - * @return EC_SUCCESS or error - */ -int tcpm_select_rp_value(int port, int rp); - -/** - * Set the CC pull resistor. This sets our role as either source or sink. - * - * @param port Type-C port number - * @param pull One of enum tcpc_cc_pull - * - * @return EC_SUCCESS or error - */ -int tcpm_set_cc(int port, int pull); - -/** - * Set polarity - * - * @param port Type-C port number - * @param polarity 0=> transmit on CC1, 1=> transmit on CC2 - * - * @return EC_SUCCESS or error - */ -int tcpm_set_polarity(int port, int polarity); - -/** - * Set Vconn. - * - * @param port Type-C port number - * @param polarity Polarity of the CC line to read - * - * @return EC_SUCCESS or error - */ -int tcpm_set_vconn(int port, int enable); - -/** - * Set PD message header to use for goodCRC - * - * @param port Type-C port number - * @param power_role Power role to use in header - * @param data_role Data role to use in header - * - * @return EC_SUCCESS or error - */ -int tcpm_set_msg_header(int port, int power_role, int data_role); - -/** - * Set RX enable flag - * - * @param port Type-C port number - * @enable true for enable, false for disable - * - * @return EC_SUCCESS or error - */ -int tcpm_set_rx_enable(int port, int enable); - -/** - * Transmit PD message - * - * @param port Type-C port number - * @param type Transmit type - * @param header Packet header - * @param cnt Number of bytes in payload - * @param data Payload - * - * @return EC_SUCCESS or error - */ -int tcpm_transmit(int port, enum tcpm_transmit_type type, uint16_t header, - const uint32_t *data); - -/** - * TCPC is asserting alert - * - * @param port Type-C port number - */ -void tcpc_alert(int port); - -#endif - -/** - * Gets the next waiting RX message. - * - * @param port Type-C port number - * @param payload Pointer to location to copy payload of PD message - * @param header The header of PD message - * - * @return EC_SUCCESS or error - */ -int tcpm_dequeue_message(int port, uint32_t *payload, int *header); - -/** - * Returns true if the tcpm has RX messages waiting to be consumed. - */ -int tcpm_has_pending_message(int port); - -/** - * Clear any pending messages in the RX queue. This function must be - * called from the same context as the caller of tcpm_dequeue_message to avoid - * race conditions. - */ -void tcpm_clear_pending_messages(int port); - -/** - * Enable/Disable TCPC Fast Role Swap detection - * - * @param port Type-C port number - * @param enable FRS enable (true) disable (false) - */ -static inline void tcpm_set_frs_enable(int port, int enable) -{ - const struct tcpm_drv *tcpc; - - /* - * set_frs_enable will be set to tcpci_tcp_fast_role_swap_enable - * if it is handled by the tcpci for the tcpc chipset - */ - tcpc = tcpc_config[port].drv; - if (tcpc->set_frs_enable) - tcpc->set_frs_enable(port, enable); -} - -#endif diff --git a/driver/tcpm/tusb422.c b/driver/tcpm/tusb422.c deleted file mode 100644 index 0e6a6c51d5..0000000000 --- a/driver/tcpm/tusb422.c +++ /dev/null @@ -1,66 +0,0 @@ -/* Copyright 2018 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. - */ - -/* Type-C port manager for TI TUSB422 Port Controller */ - -#include "common.h" -#include "tusb422.h" -#include "tcpci.h" -#include "tcpm.h" -#include "timer.h" -#include "usb_pd.h" - -#ifndef CONFIG_USB_PD_TCPM_TCPCI -#error "TUSB422 is using a standard TCPCI interface" -#error "Please upgrade your board configuration" - -#endif - -int tusb422_tcpci_tcpn_init(int port) -{ - int rv = tcpci_tcpm_init(port); - - if (rv) - return rv; - - /* - * VBUS detection is supposed to be enabled by default, however the - * TUSB422 has this disabled following reset. - */ - /* Enable VBUS detection */ - return tcpc_write16(port, TCPC_REG_COMMAND, 0x33); -} - -const struct tcpm_drv tusb422_tcpm_drv = { - .init = &tusb422_tcpci_tcpn_init, - .release = &tcpci_tcpm_release, - .get_cc = &tcpci_tcpm_get_cc, -#ifdef CONFIG_USB_PD_VBUS_DETECT_TCPC - .get_vbus_level = &tcpci_tcpm_get_vbus_level, -#endif - .select_rp_value = &tcpci_tcpm_select_rp_value, - .set_cc = &tcpci_tcpm_set_cc, - .set_polarity = &tcpci_tcpm_set_polarity, - .set_vconn = &tcpci_tcpm_set_vconn, - .set_msg_header = &tcpci_tcpm_set_msg_header, - .set_rx_enable = &tcpci_tcpm_set_rx_enable, - .get_message_raw = &tcpci_tcpm_get_message_raw, - .transmit = &tcpci_tcpm_transmit, - .tcpc_alert = &tcpci_tcpc_alert, -#ifdef CONFIG_USB_PD_DISCHARGE_TCPC - .tcpc_discharge_vbus = &tcpci_tcpc_discharge_vbus, -#endif -#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE - .drp_toggle = &tcpci_tcpc_drp_toggle, -#endif -#ifdef CONFIG_USBC_PPC - .set_snk_ctrl = &tcpci_tcpm_set_snk_ctrl, - .set_src_ctrl = &tcpci_tcpm_set_src_ctrl, -#endif - .get_chip_info = &tcpci_get_chip_info, -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER - .enter_low_power_mode = &tcpci_enter_low_power_mode, -#endif -}; diff --git a/driver/tcpm/tusb422.h b/driver/tcpm/tusb422.h deleted file mode 100644 index 52d0a079d6..0000000000 --- a/driver/tcpm/tusb422.h +++ /dev/null @@ -1,16 +0,0 @@ -/* Copyright 2018 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. - */ - -/* TI TUSB422 Type-C port controller */ - -#ifndef __CROS_EC_USB_PD_TCPM_TUSB422_H -#define __CROS_EC_USB_PD_TCPM_TUSB422_H - -/* I2C interface */ -#define TUSB422_I2C_ADDR_FLAGS 0x20 - -extern const struct tcpm_drv tusb422_tcpm_drv; - -#endif /* defined(__CROS_EC_USB_PD_TCPM_TUSB422_H) */ diff --git a/include/charge_manager.h b/include/charge_manager.h deleted file mode 100644 index 2a3ca27d74..0000000000 --- a/include/charge_manager.h +++ /dev/null @@ -1,301 +0,0 @@ -/* 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. - */ - -#ifndef __CROS_EC_CHARGE_MANAGER_H -#define __CROS_EC_CHARGE_MANAGER_H - -#include "common.h" -#include "ec_commands.h" - -/* Charge port that indicates no active port */ -#define CHARGE_PORT_NONE -1 -#define CHARGE_CEIL_NONE -1 - -/* Initial charge state */ -#define CHARGE_CURRENT_UNINITIALIZED -1 -#define CHARGE_VOLTAGE_UNINITIALIZED -1 - -/* Only track BC1.2 charge current if we support BC1.2 charging */ -#if defined(HAS_TASK_USB_CHG) || defined(HAS_TASK_USB_CHG_P0) || \ -defined(TEST_BUILD) -#define CHARGE_MANAGER_BC12 -#endif - -/** - * Time to delay for detecting the charger type (must be long enough for BC1.2 - * driver to get supplier information and notify charge manager). - */ -#define CHARGE_DETECT_DELAY (2*SECOND) - -/* Commonly-used charge suppliers listed in no particular order */ -enum charge_supplier { - CHARGE_SUPPLIER_NONE = -1, - CHARGE_SUPPLIER_PD, - CHARGE_SUPPLIER_TYPEC, - CHARGE_SUPPLIER_TYPEC_DTS, -#ifdef CHARGE_MANAGER_BC12 - CHARGE_SUPPLIER_BC12_DCP, - CHARGE_SUPPLIER_BC12_CDP, - CHARGE_SUPPLIER_BC12_SDP, - CHARGE_SUPPLIER_PROPRIETARY, - CHARGE_SUPPLIER_TYPEC_UNDER_1_5A, - CHARGE_SUPPLIER_OTHER, - CHARGE_SUPPLIER_VBUS, -#endif /* CHARGE_MANAGER_BC12 */ -#if CONFIG_DEDICATED_CHARGE_PORT_COUNT > 0 - CHARGE_SUPPLIER_DEDICATED, -#endif -#ifdef CONFIG_WIRELESS_CHARGER_P9221_R7 - CHARGE_SUPPLIER_WPC_BPP, - CHARGE_SUPPLIER_WPC_EPP, - CHARGE_SUPPLIER_WPC_GPP, -#endif - CHARGE_SUPPLIER_COUNT -}; - -/* - * Charge supplier priority: lower number indicates higher priority. - * Default priority is in charge_manager.c. It can be overridden by boards. - */ -extern const int supplier_priority[]; - -/* Charge tasks report available current and voltage */ -struct charge_port_info { - int current; - int voltage; -}; - -/** - * Called by charging tasks to update their available charge. - * - * @param supplier Charge supplier to update. - * @param port Charge port to update. - * @param charge Charge port current / voltage. If NULL, current = 0 - * voltage = 0 will be used. - */ -void charge_manager_update_charge(int supplier, - int port, - const struct charge_port_info *charge); - -/* Partner port dualrole capabilities */ -enum dualrole_capabilities { - CAP_UNKNOWN, - CAP_DUALROLE, - CAP_DEDICATED, -}; - -/** - * Notify charge_manager of a partner dualrole capability change. - * - * @param port Charge port which changed. - * @param cap New port capability. - */ -void charge_manager_update_dualrole(int port, enum dualrole_capabilities cap); - -/** - * Tell charge_manager to leave safe mode and switch to standard port / ILIM - * selection logic. - */ -void charge_manager_leave_safe_mode(void); - -/** - * Charge ceiling can be set independently by different tasks / functions, - * for different purposes. - */ -enum ceil_requestor { - /* Set by PD task, during negotiation */ - CEIL_REQUESTOR_PD, - /* Set by host commands */ - CEIL_REQUESTOR_HOST, - /* Number of ceiling groups */ - CEIL_REQUESTOR_COUNT, -}; - -#define CHARGE_PORT_COUNT (CONFIG_USB_PD_PORT_MAX_COUNT + \ - CONFIG_DEDICATED_CHARGE_PORT_COUNT) -#if (CONFIG_DEDICATED_CHARGE_PORT_COUNT > 0) - -/** - * By default, dedicated port has following properties: - * - * - dedicated port is sink only. - * - dedicated port is always connected. - * - dedicated port is given highest priority (supplier type is always - * CHARGE_SUPPLIER_DEDICATED). - * - dualrole capability of dedicated port is always CAP_DEDICATED. - * - there's only one dedicated port, its number is larger than PD port number. - * - * Sink property can be customized by implementing board_charge_port_is_sink() - * and board_fill_source_power_info(). - * Connected can be customized by implementing board_charge_port_is_connected(). - */ -#if !defined(DEDICATED_CHARGE_PORT) -#error "DEDICATED_CHARGE_PORT must be defined" -#elif DEDICATED_CHARGE_PORT < CONFIG_USB_PD_PORT_MAX_COUNT -#error "DEDICATED_CHARGE_PORT must larger than pd port numbers" -#endif /* !defined(DEDICATED_CHARGE_PORT) */ - -#endif /* CONFIG_DEDICATED_CHARGE_PORT_COUNT > 0 */ - -/** - * Update charge ceiling for a given port. The ceiling can be set independently - * for several requestors, and the min. ceil will be enforced. - * - * @param port Charge port to update. - * @param requestor Charge ceiling requestor. - * @param ceil Charge ceiling (mA). - */ -void charge_manager_set_ceil(int port, enum ceil_requestor requestor, int ceil); - -/* - * Update PD charge ceiling for a given port. In the event that our ceiling - * is currently above ceil, change the current limit before returning, without - * waiting for a charge manager refresh. This function should only be used in - * time-critical situations where we absolutely cannot proceed without limiting - * our input current, and it should only be called from the PD tasks. - * If you ever call this function then you are a terrible person. - */ -void charge_manager_force_ceil(int port, int ceil); - -/** - * Select an 'override port', a port which is always the preferred charge port. - * - * @param port Charge port to select as override, or - * OVERRIDE_OFF to select no override port, - * or OVERRIDE_DONT_CHARGE to specific that no - * charge port should be selected. - * @return EC_SUCCESS on success, - * the other ec_error_list status on failure. - */ -int charge_manager_set_override(int port); - -/** - * Get the override port. - * - * @return Port number or OVERRIDE_OFF or OVERRIDE_DONT_CHARGE. - */ -int charge_manager_get_override(void); - -/** - * Get the current active charge port, as determined by charge manager. - * - * @return Current active charge port. - */ -int charge_manager_get_active_charge_port(void); - -/** - * Get the power limit set by charge manager. - * - * @return Power limit (uW). - */ -int charge_manager_get_power_limit_uw(void); - -/** - * Get the charger current (mA) value. - * - * @return Charger current (mA) or CHARGE_CURRENT_UNINITIALIZED. - */ -int charge_manager_get_charger_current(void); - -/** - * Get the charger voltage (mV) value. - * - * @return Charger voltage (mV) or CHARGE_VOLTAGE_UNINITIALIZED. - */ -int charge_manager_get_charger_voltage(void); - -#ifdef CONFIG_USB_PD_LOGGING -/* Save power state log entry for the given port */ -void charge_manager_save_log(int port); -#endif - -/** - * Update whether a given port is sourcing current. - * - * @param port Port number to be updated. - * @param enable 0 if the source port is disabled; - * Otherwise the source port is enabled. - */ -void charge_manager_source_port(int port, int enable); - -/** - * Get PD source power data objects. - * - * @param src_pdo Pointer to the data to return. - * @param port Current port to evaluate against. - * @return number of PDOs returned. - */ -int charge_manager_get_source_pdo(const uint32_t **src_pdo, const int port); - -/* Board-level callback functions */ - -/** - * Set the passed charge port as active.` - * - * @param charge_port Charge port to be enabled. - * @return EC_SUCCESS if the charge port is accepted, - * other ec_error_list status otherwise. - */ -int board_set_active_charge_port(int charge_port); - -/** - * Set the charge current limit. - * - * @param port PD port. - * @param supplier Identified CHARGE_SUPPLIER_*. - * @param charge_ma Desired charge current limit, <= max_ma. - * @param max_ma Maximum charge current limit, >= charge_ma. - * @param charge_mv Negotiated charge voltage (mV). - */ -void board_set_charge_limit(int port, int supplier, int charge_ma, - int max_ma, int charge_mv); - -/** - * Get whether the port is sourcing power on VBUS. - * - * @param port PD port. - * @return VBUS power state. - */ -int board_vbus_source_enabled(int port); - -#ifdef CONFIG_USB_PD_VBUS_MEASURE_ADC_EACH_PORT -/** - * Gets the adc_channel for the specified port. - * - * @param port PD port. - * @return adc_channel that measures the Vbus voltage. - */ -enum adc_channel board_get_vbus_adc(int port); -#endif /* CONFIG_USB_PD_VBUS_MEASURE_ADC_EACH_PORT */ - -/** - * Board specific callback to check if the given port is sink. - * - * @param port Dedicated charge port. - * @return 1 if the port is sink. - */ -__override_proto int board_charge_port_is_sink(int port); - -/** - * Board specific callback to check if the given port is connected. - * - * @param port Dedicated charge port. - * @return 1 if the port is connected. - */ -__override_proto int board_charge_port_is_connected(int port); - -/** - * Board specific callback to fill passed power_info structure with current info - * about the passed dedicate port. - * This function is responsible for filling r->meas.* and r->max_power. - * - * @param port Dedicated charge port. - * @param r USB PD power info to be updated. - */ -__override_proto -void board_fill_source_power_info(int port, - struct ec_response_usb_pd_power_info *r); - -#endif /* __CROS_EC_CHARGE_MANAGER_H */ diff --git a/include/config.h b/include/config.h index 3c57bb1487..e82af70e28 100644 --- a/include/config.h +++ b/include/config.h @@ -897,41 +897,6 @@ #undef CONFIG_CHARGER_BQ25710_IDCHG_LIMIT_MA /* - * Define to use Power Delivery State Machine Framework. Along with - * CONFIG_USB_SM_FRAMEWORK, you must ensure the follow options are defined to - * use the new statemachine for USB-C: - * - * CONFIG_USB_TYPEC_SM (defined by default) - * CONFIG_USB_PRL_SM (defined by default) - * One of CONFIG_USB_PE_* policy engine options. - */ -#undef CONFIG_USB_SM_FRAMEWORK - -/* - * Define to enable Type-C State Machine. Must be enabled - * with CONFIG_USB_SM_FRAMEWORK - */ -#define CONFIG_USB_TYPEC_SM - -/* - * Define to enable Protocol Layer State Machine. Must be enabled - * with CONFIG_USB_SM_FRAMEWORK and CONFIG_USB_TYPEC_SM - */ -#define CONFIG_USB_PRL_SM - -/* - * Define to enable Policy Engine State Machine. Must be enabled - * with CONFIG_USB_SM_FRAMEWORK and CONFIG_USB_TYPEC_SM - */ -#define CONFIG_USB_PE_SM - -/* - * Define to enable Policy Engine State Machine. This is an override - * that is used to just pull in PE for unit testing. - */ -#undef CONFIG_TEST_USB_PE_SM - -/* * Board specific maximum input current limit, in mA. */ #undef CONFIG_CHARGER_MAX_INPUT_CURRENT diff --git a/include/usb_charge.h b/include/usb_charge.h deleted file mode 100644 index a14c696daa..0000000000 --- a/include/usb_charge.h +++ /dev/null @@ -1,109 +0,0 @@ -/* Copyright 2012 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. - */ - -/* USB charging control module for Chrome EC */ - -#ifndef __CROS_EC_USB_CHARGE_H -#define __CROS_EC_USB_CHARGE_H - -#include "common.h" -#include "ec_commands.h" - -/* USB charger voltage */ -#define USB_CHARGER_VOLTAGE_MV 5000 -/* USB charger minimum current */ -#define USB_CHARGER_MIN_CURR_MA 500 - -/** - * Set USB charge mode for the port. - * - * @param usb_port_id Port to set. - * @param mode New mode for port. - * @param inhibit_charge Inhibit charging during system suspend. - * @return EC_SUCCESS, or non-zero if error. - */ -int usb_charge_set_mode(int usb_port_id, enum usb_charge_mode mode, - enum usb_suspend_charge inhibit_charge); - -#ifdef HAS_TASK_USB_CHG_P0 -#define USB_CHG_EVENT_BC12 TASK_EVENT_CUSTOM_BIT(0) -#define USB_CHG_EVENT_VBUS TASK_EVENT_CUSTOM_BIT(1) -#define USB_CHG_EVENT_INTR TASK_EVENT_CUSTOM_BIT(2) -#define USB_CHG_EVENT_DR_UFP TASK_EVENT_CUSTOM_BIT(3) -#define USB_CHG_EVENT_DR_DFP TASK_EVENT_CUSTOM_BIT(4) -#define USB_CHG_EVENT_CC_OPEN TASK_EVENT_CUSTOM_BIT(5) -#define USB_CHG_EVENT_MUX TASK_EVENT_CUSTOM_BIT(6) -#endif - -/* - * Define USB_CHG_PORT_TO_TASK_ID() and TASK_ID_TO_USB_CHG__PORT() macros to - * go between USB_CHG port number and task ID. Assume that TASK_ID_USB_CHG_P0, - * is the lowest task ID and IDs are on a continuous range. - */ -#ifdef HAS_TASK_USB_CHG_P0 -#define USB_CHG_PORT_TO_TASK_ID(port) (TASK_ID_USB_CHG_P0 + (port)) -#define TASK_ID_TO_USB_CHG_PORT(id) ((id) - TASK_ID_USB_CHG_P0) -#else -#define USB_CHG_PORT_TO_TASK_ID(port) -1 /* dummy task ID */ -#define TASK_ID_TO_USB_CHG_PORT(id) 0 -#endif /* HAS_TASK_USB_CHG_P0 */ - -/** - * Returns true if the passed port is a power source. - * - * @param port Port number. - * @return True if port is sourcing vbus. - */ -int usb_charger_port_is_sourcing_vbus(int port); - -enum usb_switch { - USB_SWITCH_CONNECT, - USB_SWITCH_DISCONNECT, - USB_SWITCH_RESTORE, -}; - -/** - * Configure USB data switches on type-C port. - * - * @param port port number. - * @param setting new switch setting to configure. - */ -void usb_charger_set_switches(int port, enum usb_switch setting); - -/** - * Notify USB_CHG task that VBUS level has changed. - * - * @param port port number. - * @param vbus_level new VBUS level - */ -void usb_charger_vbus_change(int port, int vbus_level); - -/** - * Check if ramping is allowed for given supplier - * - * @supplier Supplier to check - * - * @return Ramping is allowed for given supplier - */ -int usb_charger_ramp_allowed(int supplier); - -/** - * Get the maximum current limit that we are allowed to ramp to - * - * @supplier Active supplier type - * @sup_curr Input current limit based on supplier - * - * @return Maximum current in mA - */ -int usb_charger_ramp_max(int supplier, int sup_curr); - - -/** - * Reset available BC 1.2 chargers on all ports - * @param port - */ -void usb_charger_reset_charge(int port); - -#endif /* __CROS_EC_USB_CHARGE_H */ diff --git a/include/usb_common.h b/include/usb_common.h deleted file mode 100644 index b35e5f27f9..0000000000 --- a/include/usb_common.h +++ /dev/null @@ -1,106 +0,0 @@ -/* Copyright 2019 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. - */ -#ifndef __CROS_EC_USB_COMMON_H -#define __CROS_EC_USB_COMMON_H - -/* Functions that are shared between old and new PD stacks */ -#include "usb_pd.h" -#include "usb_pd_tcpm.h" -#include "task_id.h" - -enum pd_drp_next_states { - DRP_TC_DEFAULT, - DRP_TC_UNATTACHED_SNK, - DRP_TC_UNATTACHED_SRC, - DRP_TC_DRP_AUTO_TOGGLE -}; - -/** - * Returns the next state to transition to while in the drp auto toggle state. - * - * @param drp_sink_time timer for handling TOGGLE_OFF/FORCE_SINK mode when - * auto-toggle enabled. This is an in/out variable. - * @param power_role current power role - * @param drp_state dual role states - * @param cc1 value of CC1 set by tcpm_get_cc - * @param cc2 value of CC2 set by tcpm_get_cc - * - */ -enum pd_drp_next_states drp_auto_toggle_next_state(uint64_t *drp_sink_time, - enum pd_power_role power_role, enum pd_dual_role_states drp_state, - enum tcpc_cc_voltage_status cc1, enum tcpc_cc_voltage_status cc2); - -/* Returns the battery percentage [0-100] of the system. */ -int usb_get_battery_soc(void); - -/* - * Returns type C current limit (mA), potentially with the DTS flag, based upon - * states of the CC lines on the partner side. - * - * @param polarity 0 if cc1 is primary, otherwise 1 - * @param cc1 value of CC1 set by tcpm_get_cc - * @param cc2 value of CC2 set by tcpm_get_cc - * @return current limit (mA) with DTS flag set if appropriate - */ -typec_current_t usb_get_typec_current_limit(enum pd_cc_polarity_type polarity, - enum tcpc_cc_voltage_status cc1, enum tcpc_cc_voltage_status cc2); - -/** - * Returns the polarity of a Sink. - * - * @param cc1 value of CC1 set by tcpm_get_cc - * @param cc2 value of CC2 set by tcpm_get_cc - * @return 0 if cc1 is primary, else 1 for cc2 being primary - */ -enum pd_cc_polarity_type get_snk_polarity(enum tcpc_cc_voltage_status cc1, - enum tcpc_cc_voltage_status cc2); - -/** - * Find PDO index that offers the most amount of power and stays within - * max_mv voltage. - * - * @param src_cap_cnt - * @param src_caps - * @param max_mv maximum voltage (or -1 if no limit) - * @param pdo raw pdo corresponding to index, or index 0 on error (output) - * @return index of PDO within source cap packet - */ -int pd_find_pdo_index(uint32_t src_cap_cnt, const uint32_t * const src_caps, - int max_mv, uint32_t *selected_pdo); - -/** - * Extract power information out of a Power Data Object (PDO) - * - * @param pdo raw pdo to extract - * @param ma current of the PDO (output) - * @param mv voltage of the PDO (output) - */ -void pd_extract_pdo_power(uint32_t pdo, uint32_t *ma, uint32_t *mv); - -/** - * Decide which PDO to choose from the source capabilities. - * - * @param src_cap_cnt - * @param src_caps - * @param rdo requested Request Data Object. - * @param ma selected current limit (stored on success) - * @param mv selected supply voltage (stored on success) - * @param req_type request type - * @param max_request_mv max voltage a sink can request before getting - * source caps - */ -void pd_build_request(uint32_t src_cap_cnt, const uint32_t * const src_caps, - int32_t vpd_vdo, uint32_t *rdo, uint32_t *ma, uint32_t *mv, - enum pd_request_type req_type, uint32_t max_request_mv); - -/** - * Notifies a task that is waiting on a system jump, that it's complete. - * - * @param sysjump_task_waiting indicates if the task is waiting on the - * system jump. - */ -void notify_sysjump_ready(volatile const task_id_t * const - sysjump_task_waiting); -#endif /* __CROS_EC_USB_COMMON_H */ diff --git a/include/usb_pd.h b/include/usb_pd.h deleted file mode 100644 index dcd1586486..0000000000 --- a/include/usb_pd.h +++ /dev/null @@ -1,2362 +0,0 @@ -/* 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. - */ - -/* USB Power delivery module */ - -#ifndef __CROS_EC_USB_PD_H -#define __CROS_EC_USB_PD_H - -#include "common.h" -#include "usb_pd_tcpm.h" - -/* PD Host command timeout */ -#define PD_HOST_COMMAND_TIMEOUT_US SECOND - -#ifdef CONFIG_USB_PD_PORT_MAX_COUNT -/* - * Define PD_PORT_TO_TASK_ID() and TASK_ID_TO_PD_PORT() macros to - * go between PD port number and task ID. Assume that TASK_ID_PD_C0 is the - * lowest task ID and IDs are on a continuous range. - */ -#ifdef HAS_TASK_PD_C0 -#define PD_PORT_TO_TASK_ID(port) (TASK_ID_PD_C0 + (port)) -#define TASK_ID_TO_PD_PORT(id) ((id) - TASK_ID_PD_C0) -#else -#define PD_PORT_TO_TASK_ID(port) -1 /* dummy task ID */ -#define TASK_ID_TO_PD_PORT(id) 0 -#endif /* CONFIG_COMMON_RUNTIME */ -#endif /* CONFIG_USB_PD_PORT_MAX_COUNT */ - -enum pd_rx_errors { - PD_RX_ERR_INVAL = -1, /* Invalid packet */ - PD_RX_ERR_HARD_RESET = -2, /* Got a Hard-Reset packet */ - PD_RX_ERR_CRC = -3, /* CRC mismatch */ - PD_RX_ERR_ID = -4, /* Invalid ID number */ - PD_RX_ERR_UNSUPPORTED_SOP = -5, /* Unsupported SOP */ - PD_RX_ERR_CABLE_RESET = -6 /* Got a Cable-Reset packet */ -}; - -/* Events for USB PD task */ - -/* Outgoing packet event */ -#define PD_EVENT_TX TASK_EVENT_CUSTOM_BIT(3) -/* CC line change event */ -#define PD_EVENT_CC TASK_EVENT_CUSTOM_BIT(4) -/* TCPC has reset */ -#define PD_EVENT_TCPC_RESET TASK_EVENT_CUSTOM_BIT(5) -/* DRP state has changed */ -#define PD_EVENT_UPDATE_DUAL_ROLE TASK_EVENT_CUSTOM_BIT(6) -/* - * A task, other than the task owning the PD port, accessed the TCPC. The task - * that owns the port does not send itself this event. - */ -#define PD_EVENT_DEVICE_ACCESSED TASK_EVENT_CUSTOM_BIT(7) -/* Chipset power state changed */ -#define PD_EVENT_POWER_STATE_CHANGE TASK_EVENT_CUSTOM_BIT(8) -/* Issue a Hard Reset. */ -#define PD_EVENT_SEND_HARD_RESET TASK_EVENT_CUSTOM_BIT(9) -/* PD State machine event */ -#define PD_EVENT_SM TASK_EVENT_CUSTOM_BIT(10) -/* Prepare for sysjump */ -#define PD_EVENT_SYSJUMP TASK_EVENT_CUSTOM_BIT(11) -/* First free event on PD task */ -#define PD_EVENT_FIRST_FREE_BIT 12 - -/* Ensure TCPC is out of low power mode before handling these events. */ -#define PD_EXIT_LOW_POWER_EVENT_MASK \ - (PD_EVENT_CC | \ - PD_EVENT_UPDATE_DUAL_ROLE | \ - PD_EVENT_POWER_STATE_CHANGE | \ - TASK_EVENT_WAKE) - -/* --- PD data message helpers --- */ -#define PDO_MAX_OBJECTS 7 -#define PDO_MODES (PDO_MAX_OBJECTS - 1) - -/* PDO : Power Data Object */ -/* - * 1. The vSafe5V Fixed Supply Object shall always be the first object. - * 2. The remaining Fixed Supply Objects, - * if present, shall be sent in voltage order; lowest to highest. - * 3. The Battery Supply Objects, - * if present shall be sent in Minimum Voltage order; lowest to highest. - * 4. The Variable Supply (non battery) Objects, - * if present, shall be sent in Minimum Voltage order; lowest to highest. - * 5. (PD3.0) The Augmented PDO is defined to allow extension beyond the 4 PDOs - * above by examining bits <29:28> to determine the additional PDO function. - */ -#define PDO_TYPE_FIXED (0 << 30) -#define PDO_TYPE_BATTERY BIT(30) -#define PDO_TYPE_VARIABLE (2 << 30) -#define PDO_TYPE_AUGMENTED (3 << 30) -#define PDO_TYPE_MASK (3 << 30) - -#define PDO_FIXED_DUAL_ROLE BIT(29) /* Dual role device */ -#define PDO_FIXED_SUSPEND BIT(28) /* USB Suspend supported */ -#define PDO_FIXED_EXTERNAL BIT(27) /* Externally powered */ -#define PDO_FIXED_COMM_CAP BIT(26) /* USB Communications Capable */ -#define PDO_FIXED_DATA_SWAP BIT(25) /* Data role swap command supported */ -#define PDO_FIXED_FRS_CURR_MASK (3 << 23) /* [23..24] FRS current */ -#define PDO_FIXED_FRS_CURR_NOT_SUPPORTED (0 << 23) -#define PDO_FIXED_FRS_CURR_DFLT_USB_POWER (1 << 23) -#define PDO_FIXED_FRS_CURR_1A5_AT_5V (2 << 23) -#define PDO_FIXED_FRS_CURR_3A0_AT_5V (3 << 23) -#define PDO_FIXED_PEAK_CURR () /* [21..20] Peak current */ -#define PDO_FIXED_VOLT(mv) (((mv)/50) << 10) /* Voltage in 50mV units */ -#define PDO_FIXED_CURR(ma) (((ma)/10) << 0) /* Max current in 10mA units */ - -#define PDO_FIXED(mv, ma, flags) (PDO_FIXED_VOLT(mv) |\ - PDO_FIXED_CURR(ma) | (flags)) - -#define PDO_VAR_MAX_VOLT(mv) ((((mv) / 50) & 0x3FF) << 20) -#define PDO_VAR_MIN_VOLT(mv) ((((mv) / 50) & 0x3FF) << 10) -#define PDO_VAR_OP_CURR(ma) ((((ma) / 10) & 0x3FF) << 0) - -#define PDO_VAR(min_mv, max_mv, op_ma) \ - (PDO_VAR_MIN_VOLT(min_mv) | \ - PDO_VAR_MAX_VOLT(max_mv) | \ - PDO_VAR_OP_CURR(op_ma) | \ - PDO_TYPE_VARIABLE) - -#define PDO_BATT_MAX_VOLT(mv) ((((mv) / 50) & 0x3FF) << 20) -#define PDO_BATT_MIN_VOLT(mv) ((((mv) / 50) & 0x3FF) << 10) -#define PDO_BATT_OP_POWER(mw) ((((mw) / 250) & 0x3FF) << 0) - -#define PDO_BATT(min_mv, max_mv, op_mw) \ - (PDO_BATT_MIN_VOLT(min_mv) | \ - PDO_BATT_MAX_VOLT(max_mv) | \ - PDO_BATT_OP_POWER(op_mw) | \ - PDO_TYPE_BATTERY) - -/* RDO : Request Data Object */ -#define RDO_OBJ_POS(n) (((n) & 0x7) << 28) -#define RDO_POS(rdo) (((rdo) >> 28) & 0x7) -#define RDO_GIVE_BACK BIT(27) -#define RDO_CAP_MISMATCH BIT(26) -#define RDO_COMM_CAP BIT(25) -#define RDO_NO_SUSPEND BIT(24) -#define RDO_FIXED_VAR_OP_CURR(ma) ((((ma) / 10) & 0x3FF) << 10) -#define RDO_FIXED_VAR_MAX_CURR(ma) ((((ma) / 10) & 0x3FF) << 0) - -#define RDO_BATT_OP_POWER(mw) ((((mw) / 250) & 0x3FF) << 10) -#define RDO_BATT_MAX_POWER(mw) ((((mw) / 250) & 0x3FF) << 10) - -#define RDO_FIXED(n, op_ma, max_ma, flags) \ - (RDO_OBJ_POS(n) | (flags) | \ - RDO_FIXED_VAR_OP_CURR(op_ma) | \ - RDO_FIXED_VAR_MAX_CURR(max_ma)) - - -#define RDO_BATT(n, op_mw, max_mw, flags) \ - (RDO_OBJ_POS(n) | (flags) | \ - RDO_BATT_OP_POWER(op_mw) | \ - RDO_BATT_MAX_POWER(max_mw)) - -/* BDO : BIST Data Object */ -#define BDO_MODE_RECV (0 << 28) -#define BDO_MODE_TRANSMIT BIT(28) -#define BDO_MODE_COUNTERS (2 << 28) -#define BDO_MODE_CARRIER0 (3 << 28) -#define BDO_MODE_CARRIER1 (4 << 28) -#define BDO_MODE_CARRIER2 (5 << 28) -#define BDO_MODE_CARRIER3 (6 << 28) -#define BDO_MODE_EYE (7 << 28) - -#define BDO(mode, cnt) ((mode) | ((cnt) & 0xFFFF)) - -#define BIST_MODE(n) ((n) >> 28) -#define BIST_ERROR_COUNTER(n) ((n) & 0xffff) -#define BIST_RECEIVER_MODE 0 -#define BIST_TRANSMIT_MODE 1 -#define BIST_RETURNED_COUNTER 2 -#define BIST_CARRIER_MODE_0 3 -#define BIST_CARRIER_MODE_1 4 -#define BIST_CARRIER_MODE_2 5 -#define BIST_CARRIER_MODE_3 6 -#define BIST_EYE_PATTERN 7 -#define BIST_TEST_DATA 8 - -#define SVID_DISCOVERY_MAX 16 - -/* Timers */ -#define PD_T_SINK_TX (18*MSEC) /* between 16ms and 20 */ -#define PD_T_CHUNK_SENDER_RSP (24*MSEC) /* between 24ms and 30ms */ -#define PD_T_CHUNK_SENDER_REQ (24*MSEC) /* between 24ms and 30ms */ -#define PD_T_HARD_RESET_COMPLETE (5*MSEC) /* between 4ms and 5ms*/ -#define PD_T_HARD_RESET_RETRY (1*MSEC) /* 1ms */ -#define PD_T_SEND_SOURCE_CAP (100*MSEC) /* between 100ms and 200ms */ -#define PD_T_SINK_WAIT_CAP (600*MSEC) /* between 310ms and 620ms */ -#define PD_T_SINK_TRANSITION (35*MSEC) /* between 20ms and 35ms */ -#define PD_T_SOURCE_ACTIVITY (45*MSEC) /* between 40ms and 50ms */ -#define PD_T_SENDER_RESPONSE (30*MSEC) /* between 24ms and 30ms */ -#define PD_T_PS_TRANSITION (500*MSEC) /* between 450ms and 550ms */ -#define PD_T_PS_SOURCE_ON (480*MSEC) /* between 390ms and 480ms */ -#define PD_T_PS_SOURCE_OFF (920*MSEC) /* between 750ms and 920ms */ -#define PD_T_PS_HARD_RESET (25*MSEC) /* between 25ms and 35ms */ -#define PD_T_ERROR_RECOVERY (25*MSEC) /* 25ms */ -#define PD_T_CC_DEBOUNCE (100*MSEC) /* between 100ms and 200ms */ -/* DRP_SNK + DRP_SRC must be between 50ms and 100ms with 30%-70% duty cycle */ -#define PD_T_DRP_SNK (40*MSEC) /* toggle time for sink DRP */ -#define PD_T_DRP_SRC (30*MSEC) /* toggle time for source DRP */ -#define PD_T_DEBOUNCE (15*MSEC) /* between 10ms and 20ms */ -#define PD_T_TRY_CC_DEBOUNCE (15*MSEC) /* between 10ms and 20ms */ -#define PD_T_SINK_ADJ (55*MSEC) /* between PD_T_DEBOUNCE and 60ms */ -#define PD_T_SRC_RECOVER (760*MSEC) /* between 660ms and 1000ms */ -#define PD_T_SRC_RECOVER_MAX (1000*MSEC) /* 1000ms */ -#define PD_T_SRC_TURN_ON (275*MSEC) /* 275ms */ -#define PD_T_SAFE_0V (650*MSEC) /* 650ms */ -#define PD_T_NO_RESPONSE (5500*MSEC) /* between 4.5s and 5.5s */ -#define PD_T_BIST_TRANSMIT (50*MSEC) /* 50ms (used for task_wait arg) */ -#define PD_T_BIST_RECEIVE (60*MSEC) /* 60ms (max time to process bist) */ -#define PD_T_BIST_CONT_MODE (60*MSEC) /* 30ms to 60ms */ -#define PD_T_VCONN_SOURCE_ON (100*MSEC) /* 100ms */ -#define PD_T_DRP_TRY (125*MSEC) /* btween 75 and 150ms(monitor Vbus) */ -#define PD_T_TRY_TIMEOUT (550*MSEC) /* between 550ms and 1100ms */ -#define PD_T_TRY_WAIT (600*MSEC) /* Max time for TryWait.SNK state */ -#define PD_T_SINK_REQUEST (100*MSEC) /* Wait 100ms before next request */ -#define PD_T_PD_DEBOUNCE (15*MSEC) /* between 10ms and 20ms */ -#define PD_T_CHUNK_SENDER_RESPONSE (25*MSEC) /* 25ms */ -#define PD_T_CHUNK_SENDER_REQUEST (25*MSEC) /* 25ms */ -#define PD_T_SWAP_SOURCE_START (25*MSEC) /* Min of 20ms */ -#define PD_T_RP_VALUE_CHANGE (20*MSEC) /* 20ms */ -#define PD_T_SRC_DISCONNECT (15*MSEC) /* 15ms */ -#define PD_T_VCONN_STABLE (50*MSEC) /* 50ms */ -#define PD_T_DISCOVER_IDENTITY (45*MSEC) /* between 40ms and 50ms */ - -/* number of edges and time window to detect CC line is not idle */ -#define PD_RX_TRANSITION_COUNT 3 -#define PD_RX_TRANSITION_WINDOW 20 /* between 12us and 20us */ - -/* from USB Type-C Specification Table 5-1 */ -#define PD_T_AME (1*SECOND) /* timeout from UFP attach to Alt Mode Entry */ - -/* VDM Timers ( USB PD Spec Rev2.0 Table 6-30 )*/ -#define PD_T_VDM_BUSY (100*MSEC) /* at least 100ms */ -#define PD_T_VDM_E_MODE (25*MSEC) /* enter/exit the same max */ -#define PD_T_VDM_RCVR_RSP (15*MSEC) /* max of 15ms */ -#define PD_T_VDM_SNDR_RSP (30*MSEC) /* max of 30ms */ -#define PD_T_VDM_WAIT_MODE_E (100*MSEC) /* enter/exit the same max */ - -/* CTVPD Timers ( USB Type-C ECN Table 4-27 ) */ -#define PD_T_VPDDETACH (20*MSEC) /* max of 20*MSEC */ -#define PD_T_VPDCTDD (4*MSEC) /* max of 4ms */ -#define PD_T_VPDDISABLE (25*MSEC) /* min of 25ms */ - -/* function table for entered mode */ -struct amode_fx { - int (*status)(int port, uint32_t *payload); - int (*config)(int port, uint32_t *payload); -}; - -/* 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); - struct amode_fx *amode; -}; - -struct svdm_svid_data { - uint16_t svid; - int mode_cnt; - uint32_t mode_vdo[PDO_MODES]; -}; - -struct svdm_amode_fx { - uint16_t svid; - int (*enter)(int port, uint32_t mode_caps); - int (*status)(int port, uint32_t *payload); - int (*config)(int port, uint32_t *payload); - void (*post_config)(int port); - int (*attention)(int port, uint32_t *payload); - void (*exit)(int port); -}; - -/* defined in <board>/usb_pd_policy.c */ -/* All UFP_U should have */ -extern const struct svdm_response svdm_rsp; -/* All DFP_U should have */ -extern const struct svdm_amode_fx supported_modes[]; -extern const int supported_modes_cnt; - -/* DFP data needed to support alternate mode entry and exit */ -struct svdm_amode_data { - const struct svdm_amode_fx *fx; - /* VDM object position */ - int opos; - /* mode capabilities specific to SVID amode. */ - struct svdm_svid_data *data; -}; - -enum hpd_event { - hpd_none, - hpd_low, - hpd_high, - hpd_irq, -}; - -/* DisplayPort flags */ -#define DP_FLAGS_DP_ON BIT(0) /* Display port mode is on */ -#define DP_FLAGS_HPD_HI_PENDING BIT(1) /* Pending HPD_HI */ - -/* supported alternate modes */ -enum pd_alternate_modes { - PD_AMODE_GOOGLE, - PD_AMODE_DISPLAYPORT, - /* not a real mode */ - PD_AMODE_COUNT, -}; - -/* 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; - /* SVDM identity info (Id, Cert Stat, 0-4 Typec specific) */ - uint32_t identity[PDO_MAX_OBJECTS - 1]; - /* supported svids & corresponding vdo mode data */ - struct svdm_svid_data svids[SVID_DISCOVERY_MAX]; - /* active modes */ - struct svdm_amode_data amodes[PD_AMODE_COUNT]; - /* Next index to insert DFP alternate mode into amodes */ - int amode_idx; -}; - -/* - * VDO : Vendor Defined Message Object - * VDM object is minimum of VDM header + 6 additional data objects. - */ -#define VDO_HDR_SIZE 1 -#define VDO_MAX_SIZE 7 - -#define VDM_VER10 0 -#define VDM_VER20 1 - -#define PD_VDO_INVALID -1 - -/* - * VDM header - * ---------- - * <31:16> :: SVID - * <15> :: VDM type ( 1b == structured, 0b == unstructured ) - * <14:13> :: Structured VDM version (00b == Rev 2.0, 01b == Rev 3.0 ) - * <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(vid, type, custom) \ - (((vid) << 16) | \ - ((type) << 15) | \ - ((custom) & 0x7FFF)) - -#define VDO_SVDM_TYPE BIT(15) -#define VDO_SVDM_VERS(x) (x << 13) -#define VDO_OPOS(x) (x << 8) -#define VDO_CMDT(x) (x << 6) -#define VDO_OPOS_MASK VDO_OPOS(0x7) -#define VDO_CMDT_MASK VDO_CMDT(0x3) - -#define CMDT_INIT 0 -#define CMDT_RSP_ACK 1 -#define CMDT_RSP_NAK 2 -#define CMDT_RSP_BUSY 3 - - -/* reserved for SVDM ... for Google UVDM */ -#define VDO_SRC_INITIATOR (0 << 5) -#define VDO_SRC_RESPONDER BIT(5) - -#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 CMD_DP_STATUS 16 -#define CMD_DP_CONFIG 17 - -#define VDO_CMD_VENDOR(x) (((10 + (x)) & 0x1f)) - -/* ChromeOS specific commands */ -#define VDO_CMD_VERSION VDO_CMD_VENDOR(0) -#define VDO_CMD_SEND_INFO VDO_CMD_VENDOR(1) -#define VDO_CMD_READ_INFO VDO_CMD_VENDOR(2) -#define VDO_CMD_REBOOT VDO_CMD_VENDOR(5) -#define VDO_CMD_FLASH_ERASE VDO_CMD_VENDOR(6) -#define VDO_CMD_FLASH_WRITE VDO_CMD_VENDOR(7) -#define VDO_CMD_ERASE_SIG VDO_CMD_VENDOR(8) -#define VDO_CMD_PING_ENABLE VDO_CMD_VENDOR(10) -#define VDO_CMD_CURRENT VDO_CMD_VENDOR(11) -#define VDO_CMD_FLIP VDO_CMD_VENDOR(12) -#define VDO_CMD_GET_LOG VDO_CMD_VENDOR(13) -#define VDO_CMD_CCD_EN VDO_CMD_VENDOR(14) - -#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. - * In case of Active cables, the response is 5 data objects: - * [0] :: SVDM header - * [1] :: Identitiy header - * [2] :: Cert Stat VDO - * [3] :: (Product | Cable) VDO - * [4] :: AMA VDO - * [4] :: Product type Cable VDO 1 - * [5] :: Product type Cable VDO 2 - * - */ -#define VDO_INDEX_HDR 0 -#define VDO_INDEX_IDH 1 -#define VDO_INDEX_CSTAT 2 -#define VDO_INDEX_CABLE 3 -#define VDO_INDEX_PRODUCT 3 -#define VDO_INDEX_AMA 4 -#define VDO_INDEX_PTYPE_CABLE1 4 -#define VDO_INDEX_PTYPE_CABLE2 5 -#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 - */ - -enum idh_ptype { - IDH_PTYPE_UNDEF, - IDH_PTYPE_HUB, - IDH_PTYPE_PERIPH, - IDH_PTYPE_PCABLE, - IDH_PTYPE_ACABLE, - IDH_PTYPE_AMA, - IDH_PTYPE_VPD, - IDH_PTYPE_COUNT, -}; -#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) - -/* - * Product VDO - * ----------- - * <31:16> : USB Product ID - * <15:0> : USB bcdDevice - */ -#define VDO_PRODUCT(pid, bcd) (((pid) & 0xffff) << 16 | ((bcd) & 0xffff)) -#define PD_PRODUCT_PID(vdo) (((vdo) >> 16) & 0xffff) - -/* - * Cable VDO (Ref: PD Spec 2.0 Version 1.3 - Table 6-28 and 6-29) - * --------- - * <31:28> :: Cable HW version - * <27:24> :: Cable FW version - * <23:20> :: Reserved - * <19:18> :: Type-C to Type-A/B/C (00b == A, 01 == B, 10 == C) - * <17> :: Reserved - * <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 - */ -enum usb_ss_support { - USB_SS_U2_ONLY, - USB_SS_U31_GEN1, - USB_SS_U31_GEN2, -}; - -enum cable_outlet { - CABLE_PLUG = 0, - CABLE_RECEPTACLE = 1, -}; - -enum current_capacity { - CABLE_CURRENT_3A = 1, - CABLE_CURRENT_5A, -}; - -enum cable_dir_support { - CABLE_FIXED, - CABLE_CHANGEABLE, -}; - -enum connector_type { - CONNECTOR_ATYPE, - CONNECTOR_BTYPE, - CONNECTOR_CTYPE, - CONNECTOR_CAPTIVE, -}; - -struct vdo_rev20 { - enum usb_ss_support ss: 3; - uint32_t controller : 1; - uint32_t vbus_cable : 1; - enum current_capacity current : 2; - enum cable_dir_support ssrx2 : 1; - enum cable_dir_support ssrx1 : 1; - enum cable_dir_support sstx2 : 1; - enum cable_dir_support sstx1 : 1; - uint32_t termination : 2; - uint32_t latency : 4; - uint32_t reserved0 : 1; - enum connector_type connector : 2; - uint32_t reserved1 : 4; - uint32_t fw_version : 4; - uint32_t hw_version : 4; -}; - -#define VDO_CABLE(hw, fw, cbl, lat, term, tx1d, tx2d, rx1d, rx2d, cur, vps, \ - sopp, usbss) \ - (((hw) & 0xF) << 28 | ((fw) & 0xF) << 24 | ((cbl) & 0x3) << 18 \ - | ((lat) & 0xF) << 13 | ((term) & 0x3) << 11 | ((tx1d) & 0x1) << 10 \ - | ((tx2d) & 0x1) << 9 | ((rx1d) & 0x1) << 8 | ((rx2d) & 0x1) << 7 \ - | ((cur) & 0x3) << 5 | ((vps) & 0x1) << 4 | ((sopp) & 0x1) << 3 \ - | ((usbss) & 0x7)) - -/* - * Passive Cable VDO (Ref: PD Spec 3.0 Version 1.2 - Table 6-35) - * --------- - * <31:28> :: Cable HW version - * <27:24> :: Cable FW version - * <23:21> :: VDO version - * <20> :: Reserved - * <19:18> :: Connector Type (10b == USB-C, 11b == Captive) - * <17> :: Reserved - * <16:13> :: Cable latency (0001 == <10ns(~1m length)) - * <12:11> :: Cable termination type (00b == VCONN not req, 01b = VCONN req) - * <10:9> :: Maximum cable vbus voltage - * <8:7> :: Reserved - * <6:5> :: Vbus current handling capability - * <4:3> :: Reserved - * <2:0> :: USB SS Signaling support - */ -enum max_vbus_vtg { - CABLE_VBUS_20V, - CABLE_VBUS_30V, - CABLE_VBUS_40V, - CABLE_VBUS_50V, -}; - -struct passive_cable_vdo_rev30 { - enum usb_ss_support ss: 3; - uint32_t reserved0 : 2; - enum current_capacity current : 2; - uint32_t reserved1 : 2; - enum max_vbus_vtg vbus_max : 2; - uint32_t termination : 2; - uint32_t latency : 4; - uint32_t reserved2 : 1; - enum connector_type connector : 2; - uint32_t reserved3 : 1; - uint32_t vdo_version : 3; - uint32_t fw_version : 4; - uint32_t hw_version : 4; -}; - -#define PASSIVE_VDO_CABLE_REV3(hw, fw, vdover, cbl, lat, term, vbusv, \ - cur, usbss) \ - (((hw) & 0xF) << 28 | ((fw) & 0xF) << 24 | ((vdov & 0x7) << 21) \ - | ((cbl) & 0x3) << 18 | ((lat) & 0xF) << 13 | ((term) & 0x3) << 11 \ - | ((vbusv) & 0x3) << 9 | ((cur) & 0x3) << 5 | ((usbss) & 0x7)) - -/* - * Active Cable VDO1 (Ref: PD Spec 3.0 Version 1.2 - Table 6-36) - * --------- - * <31:28> :: Cable HW version - * <27:24> :: Cable FW version - * <23:21> :: VDO version - * <20> :: Reserved - * <19:18> :: Connector Type (10b == USB-C, 11b == Captive) - * <17> :: Reserved - * <16:13> :: Cable latency (0001 == <10ns(~1m length)) - * <12:11> :: Cable termination type (11b == both ends active VCONN req) - * <10:9> :: Maximum cable vbus voltage - * <8> :: SBU Supported? (0b == yes, 1b == no) - * <7> :: SBU Type (0b == passive, 1b == active) - * <6:5> :: Vbus current handling capability - * <4> :: Vbus through cable (0b == no, 1b == yes) - * <3> :: SOP" controller present? (0b == no, 1b == yes) - * <2:0> :: Reserved - */ -struct active_cable_vdo_rev30 { - uint32_t reserved0 : 3; - uint32_t controller : 1; - uint32_t vbus_cable : 1; - enum current_capacity current : 2; - uint32_t sbu_type : 1; - uint32_t sbu_support : 1; - enum max_vbus_vtg vbus_max : 2; - uint32_t termination : 2; - uint32_t latency : 4; - uint32_t reserved1 : 1; - enum connector_type connector : 2; - uint32_t reserved2 : 1; - uint32_t vdo_version : 3; - uint32_t cable_fw_version : 4; - uint32_t cable_hw_version : 4; -}; -#define ACTIVE_VDO1_CABLE_REV3(hw, fw, vdover, cbl, lat, term, vbusv, sbus, \ - sbut, cur, vps, sopp) \ - (((hw) & 0xF) << 28 | ((fw) & 0xF) << 24 | ((vdov) & 0x7) << 21 \ - | ((cbl) & 0x3) << 18 | ((lat) & 0xF) << 13 | ((term) & 0x3) << 11 \ - | ((vbusv) & 0x3) << 9 | ((sbus) & 0x1) << 8 | ((sbut) & 0x1) << 7 \ - | ((cur) & 0x3) << 5 | ((vps) & 0x1) << 4 | ((sopp) & 0x1) << 3) - -struct cable_vdo { - union { - struct vdo_rev20 rev20; - struct passive_cable_vdo_rev30 p_rev30; - struct active_cable_vdo_rev30 a_rev30; - uint32_t raw_value; - }; -}; - -/* - * Active Cable VDO2 (Ref: PD Spec 3.0 Version 1.2 - Table 6-37) - * --------- - * <31:24> :: Maximum operating temperature - * <23:16> :: Shutdown temperature - * <15> :: Reserved - * <14:12> :: USB3 power (000 == >10mW) - * <11> :: U3 to U0 transition - * <10:8> :: Reserved - * <7:6> :: USB 2.0 Hub Hops Consumed - * <5> :: USB 2.0 Supported? (0b == yes, 1b == no) - * <4> :: SS Supported? (0b == yes, 1b == no) - * <3> :: SS lanes supported (0b == one, 1b == two) - * <2> :: Reserved - * <1:0> :: SS signaling (0b == Gen1, 01b == Gen2) - */ -#define ACTIVE_CABLE_VDO2_CABLE_REV3(opt, sdt, usb3p, u3u0, hhc, usb2, \ - ss, ssl, sss) \ - (((opt) & 0xFF) << 24 | ((sdt) & 0xFF) << 16 | ((usb3p) & 0x7) << 12 \ - | ((u3u0) & 0x1) << 11 | ((usb2) & 0x3) << 6 | ((ss) & 0x1) << 4 \ - | ((ssl) & 0x1) << 3 | (sss) & 0x3) - -enum ss_signaling { - USB_SS_SIGNAL_SS_GEN1, - USB_SS_SIGNAL_SS_GEN2, -}; - -enum ss_lane_support { - USB_SS_ONE_LANE, - USB_SS_TWO_LANES, -}; - -enum u0u3_transition_mode { - U0_U3_DIRECT, - U0_U3_U3S, -}; - -enum u3_power_support { - U3_POWER_10mW, - U3_POWER_5_10mW, - U3_POWER_1_5mW, - U3_POWER_500_1000uW, - U3_POWER_200_500uW, - U3_POWER_50_200uW, - U3_POWER_0_50uW, -}; - -struct active_cable_vdo2_rev30 { - enum ss_signaling sss: 2; - uint32_t reserved0 : 1; - enum ss_lane_support lanes : 1; - uint32_t usb_ss_support : 1; - uint32_t usb2_support : 1; - uint32_t usb2_hub_hops : 2; - uint32_t reserved1 : 3; - enum u0u3_transition_mode u0u3: 1; - enum u3_power_support u3_power : 3; - uint32_t reserved2: 1; - uint32_t shutdown_temp; - uint32_t max_operating_temp; -}; - -struct active_cable_vdo2 { - union { - struct active_cable_vdo2_rev30 a2_rev30; - uint32_t raw_value; - }; -}; - -/* Cable structure for storing cable attributes */ -struct pd_cable { - uint8_t is_identified; - /* Type of cable */ - enum idh_ptype type; - /* Cable flags. See CABLE_FLAGS_* */ - uint8_t flags; - /* Cable attribues */ - struct cable_vdo attr; - /* Cable revision */ - uint8_t rev; - /* For USB PD REV3, active cable has 2 VDOs */ - struct active_cable_vdo2 attr2; -}; - -/* Flag for sending SOP Prime packet */ -#define CABLE_FLAGS_SOP_PRIME_ENABLE BIT(0) - -/* - * 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) - -#define AMA_VCONN_PWR_1W 0 -#define AMA_VCONN_PWR_1W5 1 -#define AMA_VCONN_PWR_2W 2 -#define AMA_VCONN_PWR_3W 3 -#define AMA_VCONN_PWR_4W 4 -#define AMA_VCONN_PWR_5W 5 -#define AMA_VCONN_PWR_6W 6 -#define AMA_USBSS_U2_ONLY 0 -#define AMA_USBSS_U31_GEN1 1 -#define AMA_USBSS_U31_GEN2 2 -#define AMA_USBSS_BBONLY 3 - -/* - * VPD VDO - * --------- - * <31:28> :: HW version - * <27:24> :: FW version - * <23:21> :: VDO version - * <20:17> :: SBZ - * <16:15> :: Maximum VBUS Voltage - * <14:13> :: SBZ - * <12:7> :: VBUS Impedance - * <6:1> :: Ground Impedance - * <0> :: Charge Through Support - */ -#define VDO_VPD(hw, fw, vbus, vbusz, gndz, cts) \ - (((hw) & 0xf) << 28 | ((fw) & 0xf) << 24 \ - | ((vbus) & 0x3) << 15 \ - | ((vbusz) & 0x3f) << 7 \ - | ((gndz) & 0x3f) << 1 | (cts)) - -#define VPD_MAX_VBUS_20V 0 -#define VPD_MAX_VBUS_30V 1 -#define VPD_MAX_VBUS_40V 2 -#define VPD_MAX_VBUS_50V 3 -#define VPD_VBUS_IMP(mo) ((mo + 1) >> 1) -#define VPD_GND_IMP(mo) (mo) -#define VPD_CTS_SUPPORTED 1 -#define VPD_CTS_NOT_SUPPORTED 0 - -/* - * 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) - -#define VPD_VDO_MAX_VBUS(vdo) (((vdo) >> 15) & 0x3) -#define VPD_VDO_VBUS_IMP(vdo) (((vdo) >> 7) & 0x3f) -#define VPD_VDO_GND_IMP(vdo) (((vdo) >> 1) & 0x3f) -#define VPD_VDO_CTS(vdo) ((vdo) & 1) - -/* - * Google modes capabilities - * <31:8> : reserved - * <7:0> : mode - */ -#define VDO_MODE_GOOGLE(mode) (mode & 0xff) - -#define MODE_GOOGLE_FU 1 /* Firmware Update mode */ - -/* - * 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> : UFP_D pin assignment supported - * <15:8> : DFP_D pin assignment supported - * <7> : USB 2.0 signaling (0b=yes, 1b=no) - * <6> : Plug | Receptacle (0b == plug, 1b == receptacle) - * <5:2> : xxx1: Supports DPv1.3, xx1x Supports USB Gen 2 signaling - * Other bits are reserved. - * <1:0> : signal direction ( 00b=rsv, 01b=sink, 10b=src 11b=both ) - */ -#define VDO_MODE_DP(snkp, srcp, usb, gdr, sign, sdir) \ - (((snkp) & 0xff) << 16 | ((srcp) & 0xff) << 8 \ - | ((usb) & 1) << 7 | ((gdr) & 1) << 6 | ((sign) & 0xF) << 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_PIN_F 0x20 -#define MODE_DP_PIN_ALL 0x3f - -#define MODE_DP_DFP_PIN_SHIFT 8 -#define MODE_DP_UFP_PIN_SHIFT 16 - -/* Pin configs B/D/F support multi-function */ -#define MODE_DP_PIN_MF_MASK 0x2a -/* Pin configs A/B support BR2 signaling levels */ -#define MODE_DP_PIN_BR2_MASK 0x3 -/* Pin configs C/D/E/F support DP signaling levels */ -#define MODE_DP_PIN_DP_MASK 0x3c -/* Pin configs A/B/C/D/E/F */ -#define MODE_DP_PIN_CAPS_MASK 0x3f - -#define MODE_DP_V13 0x1 -#define MODE_DP_GEN2 0x2 - -#define MODE_DP_SNK 0x1 -#define MODE_DP_SRC 0x2 -#define MODE_DP_BOTH 0x3 - -#define MODE_DP_CABLE_SHIFT 6 - -/* - * Determine which pin assignments are valid for DP - * - * Based on whether the DP adapter identifies itself as a plug (permanently - * attached cable) or a receptacle, the pin assignments may be in the DFP_D - * field or the UFP_D field. - * - * Refer to DisplayPort Alt Mode On USB Type-C Standard version 1.0, table 5-2 - * depending on state of receptacle bit, use pins for DFP_D (if receptacle==0) - * or UFP_D (if receptacle==1) - * Also refer to DisplayPort Alt Mode Capabilities Clarification (4/30/2015) - */ -#define PD_DP_PIN_CAPS(x) ((((x) >> MODE_DP_CABLE_SHIFT) & 0x1) \ - ? (((x) >> MODE_DP_UFP_PIN_SHIFT) & MODE_DP_PIN_CAPS_MASK) \ - : (((x) >> MODE_DP_DFP_PIN_SHIFT) & MODE_DP_PIN_CAPS_MASK)) - -/* - * DisplayPort Status VDO - * ---------------------- - * <31:9> : SBZ - * <8> : IRQ_HPD : 1 == irq arrived since last message otherwise 0. - * <7> : HPD state : 0 = HPD_LOW, 1 == HPD_HIGH - * <6> : Exit DP Alt mode: 0 == maintain, 1 == exit - * <5> : USB config : 0 == maintain current, 1 == switch to USB from DP - * <4> : Multi-function preference : 0 == no pref, 1 == MF preferred. - * <3> : enabled : is DPout on/off. - * <2> : power low : 0 == normal or LPM disabled, 1 == DP disabled for LPM - * <1:0> : connect status : 00b == no (DFP|UFP)_D is connected or disabled. - * 01b == DFP_D connected, 10b == UFP_D connected, 11b == both. - */ -#define VDO_DP_STATUS(irq, lvl, amode, usbc, mf, en, lp, conn) \ - (((irq) & 1) << 8 | ((lvl) & 1) << 7 | ((amode) & 1) << 6 \ - | ((usbc) & 1) << 5 | ((mf) & 1) << 4 | ((en) & 1) << 3 \ - | ((lp) & 1) << 2 | ((conn & 0x3) << 0)) - -#define PD_VDO_DPSTS_MF_MASK BIT(4) - -#define PD_VDO_DPSTS_HPD_IRQ(x) (((x) >> 8) & 1) -#define PD_VDO_DPSTS_HPD_LVL(x) (((x) >> 7) & 1) -#define PD_VDO_DPSTS_MF_PREF(x) (((x) >> 4) & 1) - -/* Per DisplayPort Spec v1.3 Section 3.3 */ -#define HPD_USTREAM_DEBOUNCE_LVL (2*MSEC) -#define HPD_USTREAM_DEBOUNCE_IRQ (250) -#define HPD_DSTREAM_DEBOUNCE_IRQ (500) /* between 500-1000us */ - -/* - * DisplayPort Configure VDO - * ------------------------- - * <31:24> : SBZ - * <23:16> : SBZ - * <15:8> : Pin assignment requested. Choose one from mode caps. - * <7:6> : SBZ - * <5:2> : signalling : 1h == DP v1.3, 2h == Gen 2 - * Oh is only for USB, remaining values are reserved - * <1:0> : cfg : 00 == USB, 01 == DFP_D, 10 == UFP_D, 11 == reserved - */ -#define VDO_DP_CFG(pin, sig, cfg) \ - (((pin) & 0xff) << 8 | ((sig) & 0xf) << 2 | ((cfg) & 0x3)) - -#define PD_DP_CFG_DPON(x) (((x & 0x3) == 1) || ((x & 0x3) == 2)) -/* - * Get the pin assignment mask - * for backward compatibility, if it is null, - * get the former sink pin assignment we used to be in <23:16>. - */ -#define PD_DP_CFG_PIN(x) ((((x) >> 8) & 0xff) ? (((x) >> 8) & 0xff) \ - : (((x) >> 16) & 0xff)) -/* - * ChromeOS specific PD device Hardware IDs. Used to identify unique - * products and used in VDO_INFO. Note this field is 10 bits. - */ -#define USB_PD_HW_DEV_ID_RESERVED 0 -#define USB_PD_HW_DEV_ID_ZINGER 1 -#define USB_PD_HW_DEV_ID_MINIMUFFIN 2 -#define USB_PD_HW_DEV_ID_DINGDONG 3 -#define USB_PD_HW_DEV_ID_HOHO 4 -#define USB_PD_HW_DEV_ID_HONEYBUNS 5 - -/* - * ChromeOS specific VDO_CMD_READ_INFO responds with device info including: - * RW Hash: First 20 bytes of SHA-256 of RW (20 bytes) - * HW Device ID: unique descriptor for each ChromeOS model (2 bytes) - * top 6 bits are minor revision, bottom 10 bits are major - * SW Debug Version: Software version useful for debugging (15 bits) - * IS RW: True if currently in RW, False otherwise (1 bit) - */ -#define VDO_INFO(id, id_minor, ver, is_rw) ((id_minor) << 26 \ - | ((id) & 0x3ff) << 16 \ - | ((ver) & 0x7fff) << 1 \ - | ((is_rw) & 1)) -#define VDO_INFO_HW_DEV_ID(x) ((x) >> 16) -#define VDO_INFO_SW_DBG_VER(x) (((x) >> 1) & 0x7fff) -#define VDO_INFO_IS_RW(x) ((x) & 1) - -#define HW_DEV_ID_MAJ(x) (x & 0x3ff) -#define HW_DEV_ID_MIN(x) ((x) >> 10) - -/* USB-IF SIDs */ -#define USB_SID_PD 0xff00 /* power delivery */ -#define USB_SID_DISPLAYPORT 0xff01 - -#define USB_GOOGLE_TYPEC_URL "http://www.google.com/chrome/devices/typec" -/* USB Vendor ID assigned to Google Inc. */ -#define USB_VID_GOOGLE 0x18d1 - -/* Other Vendor IDs */ -#define USB_VID_APPLE 0x05ac -#define USB_PID1_APPLE 0x1012 -#define USB_PID2_APPLE 0x1013 - -/* Timeout for message receive in microseconds */ -#define USB_PD_RX_TMOUT_US 1800 - -/* --- Protocol layer functions --- */ - -enum pd_states { - PD_STATE_DISABLED, /* C0 */ - PD_STATE_SUSPENDED, /* C1 */ - PD_STATE_SNK_DISCONNECTED, /* C2 */ - PD_STATE_SNK_DISCONNECTED_DEBOUNCE, /* C3 */ - PD_STATE_SNK_HARD_RESET_RECOVER, /* C4 */ - PD_STATE_SNK_DISCOVERY, /* C5 */ - PD_STATE_SNK_REQUESTED, /* C6 */ - PD_STATE_SNK_TRANSITION, /* C7 */ - PD_STATE_SNK_READY, /* C8 */ - PD_STATE_SNK_SWAP_INIT, /* C9 */ - PD_STATE_SNK_SWAP_SNK_DISABLE, /* C10 */ - PD_STATE_SNK_SWAP_SRC_DISABLE, /* C11 */ - PD_STATE_SNK_SWAP_STANDBY, /* C12 */ - PD_STATE_SNK_SWAP_COMPLETE, /* C13 */ - PD_STATE_SRC_DISCONNECTED, /* C14 */ - PD_STATE_SRC_DISCONNECTED_DEBOUNCE, /* C15 */ - PD_STATE_SRC_HARD_RESET_RECOVER, /* C16 */ - PD_STATE_SRC_STARTUP, /* C17 */ - PD_STATE_SRC_DISCOVERY, /* C18 */ - PD_STATE_SRC_NEGOCIATE, /* C19 */ - PD_STATE_SRC_ACCEPTED, /* C20 */ - PD_STATE_SRC_POWERED, /* C21 */ - PD_STATE_SRC_TRANSITION, /* C22 */ - PD_STATE_SRC_READY, /* C23 */ - PD_STATE_SRC_GET_SINK_CAP, /* C24 */ - PD_STATE_DR_SWAP, /* C25 */ - PD_STATE_SRC_SWAP_INIT, /* C26 */ - PD_STATE_SRC_SWAP_SNK_DISABLE, /* C27 */ - PD_STATE_SRC_SWAP_SRC_DISABLE, /* C28 */ - PD_STATE_SRC_SWAP_STANDBY, /* C29 */ - PD_STATE_VCONN_SWAP_SEND, /* C30 */ - PD_STATE_VCONN_SWAP_INIT, /* C31 */ - PD_STATE_VCONN_SWAP_READY, /* C32 */ - PD_STATE_SOFT_RESET, /* C33 */ - PD_STATE_HARD_RESET_SEND, /* C34 */ - PD_STATE_HARD_RESET_EXECUTE, /* C35 */ - PD_STATE_BIST_RX, /* C36 */ - PD_STATE_BIST_TX, /* C37 */ - PD_STATE_DRP_AUTO_TOGGLE, /* C38 */ - /* Number of states. Not an actual state. */ - PD_STATE_COUNT, -}; - -/* Generate compile-time errors for unsupported states */ -#ifndef CONFIG_USB_PD_DUAL_ROLE -#define PD_STATE_SNK_DISCONNECTED UNSUPPORTED_PD_STATE_SNK_DISCONNECTED -#define PD_STATE_SNK_DISCONNECTED_DEBOUNCE UNSUPPORTED_SNK_DISCONNECTED_DEBOUNCE -#define PD_STATE_SNK_HARD_RESET_RECOVER UNSUPPORTED_SNK_HARD_RESET_RECOVER -#define PD_STATE_SNK_DISCOVERY UNSUPPORTED_PD_STATE_SNK_DISCOVERY -#define PD_STATE_SNK_REQUESTED UNSUPPORTED_PD_STATE_SNK_REQUESTED -#define PD_STATE_SNK_TRANSITION UNSUPPORTED_PD_STATE_SNK_TRANSITION -#define PD_STATE_SNK_READY UNSUPPORTED_PD_STATE_SNK_READY -#define PD_STATE_SNK_SWAP_INIT UNSUPPORTED_PD_STATE_SNK_SWAP_INIT -#define PD_STATE_SNK_SWAP_SNK_DISABLE UNSUPPORTED_PD_STATE_SNK_SWAP_SNK_DISABLE -#define PD_STATE_SNK_SWAP_SRC_DISABLE UNSUPPORTED_PD_STATE_SNK_SWAP_SRC_DISABLE -#define PD_STATE_SNK_SWAP_STANDBY UNSUPPORTED_PD_STATE_SNK_SWAP_STANDBY -#define PD_STATE_SNK_SWAP_COMPLETE UNSUPPORTED_PD_STATE_SNK_SWAP_COMPLETE -#define PD_STATE_SRC_SWAP_INIT UNSUPPORTED_PD_STATE_SRC_SWAP_INIT -#define PD_STATE_SRC_SWAP_SNK_DISABLE UNSUPPORTED_PD_STATE_SRC_SWAP_SNK_DISABLE -#define PD_STATE_SRC_SWAP_SRC_DISABLE UNSUPPORTED_PD_STATE_SRC_SWAP_SRC_DISABLE -#define PD_STATE_SRC_SWAP_STANDBY UNSUPPORTED_PD_STATE_SRC_SWAP_STANDBY -#endif /* CONFIG_USB_PD_DUAL_ROLE */ - -/* Generate compile-time errors for unsupported states */ -#if !defined(CONFIG_USBC_VCONN_SWAP) || !defined(CONFIG_USB_PD_DUAL_ROLE) -#define PD_STATE_VCONN_SWAP_SEND UNSUPPORTED_PD_STATE_VCONN_SWAP_SEND -#define PD_STATE_VCONN_SWAP_INIT UNSUPPORTED_PD_STATE_VCONN_SWAP_INIT -#define PD_STATE_VCONN_SWAP_READY UNSUPPORTED_PD_STATE_VCONN_SWAP_READY -#endif - -/* Generate compile-time errors for unsupported states */ -#ifndef CONFIG_COMMON_RUNTIME -#define PD_STATE_BIST_RX UNSUPPORTED_PD_STATE_BIST_RX -#define PD_STATE_BIST_TX UNSUPPORTED_PD_STATE_BIST_TX -#endif - -/* Generate compile-time errors for unsupported states */ -#ifndef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE -#define PD_STATE_DRP_AUTO_TOGGLE UNSUPPORTED_PD_STATE_DRP_AUTO_TOGGLE -#endif - -#define PD_FLAGS_PING_ENABLED BIT(0) /* SRC_READY pings enabled */ -#define PD_FLAGS_PARTNER_DR_POWER BIT(1) /* port partner is dualrole power */ -#define PD_FLAGS_PARTNER_DR_DATA BIT(2) /* port partner is dualrole data */ -#define PD_FLAGS_CHECK_IDENTITY BIT(3) /* discover identity in READY */ -#define PD_FLAGS_SNK_CAP_RECVD BIT(4) /* sink capabilities received */ -#define PD_FLAGS_TCPC_DRP_TOGGLE BIT(5) /* TCPC-controlled DRP toggling */ -#define PD_FLAGS_EXPLICIT_CONTRACT BIT(6) /* explicit pwr contract in place */ -#define PD_FLAGS_VBUS_NEVER_LOW BIT(7) /* VBUS input has never been low */ -#define PD_FLAGS_PREVIOUS_PD_CONN BIT(8) /* previously PD connected */ -#define PD_FLAGS_CHECK_PR_ROLE BIT(9) /* check power role in READY */ -#define PD_FLAGS_CHECK_DR_ROLE BIT(10)/* check data role in READY */ -#define PD_FLAGS_PARTNER_EXTPOWER BIT(11)/* port partner has external pwr */ -#define PD_FLAGS_VCONN_ON BIT(12)/* vconn is being sourced */ -#define PD_FLAGS_TRY_SRC BIT(13)/* Try.SRC states are active */ -#define PD_FLAGS_PARTNER_USB_COMM BIT(14)/* port partner is USB comms */ -#define PD_FLAGS_UPDATE_SRC_CAPS BIT(15)/* send new source capabilities */ -#define PD_FLAGS_TS_DTS_PARTNER BIT(16)/* partner has rp/rp or rd/rd */ -/* - * These PD_FLAGS_LPM* flags track the software state (PD_LPM_FLAGS_REQUESTED) - * and hardware state (PD_LPM_FLAGS_ENGAGED) of the TCPC low power mode. - * PD_FLAGS_LPM_TRANSITION is set while the HW is transitioning into or out of - * low power (when PD_LPM_FLAGS_ENGAGED is changing). - */ -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER -#define PD_FLAGS_LPM_REQUESTED BIT(17)/* Tracks SW LPM state */ -#define PD_FLAGS_LPM_ENGAGED BIT(18)/* Tracks HW LPM state */ -#define PD_FLAGS_LPM_TRANSITION BIT(19)/* Tracks HW LPM transition */ -#endif - -/* - * Tracks whether port negotiation may have stalled due to not starting reset - * timers in SNK_DISCOVERY - */ -#define PD_FLAGS_SNK_WAITING_BATT BIT(20) - -/* Flags to clear on a disconnect */ -#define PD_FLAGS_RESET_ON_DISCONNECT_MASK (PD_FLAGS_PARTNER_DR_POWER | \ - PD_FLAGS_PARTNER_DR_DATA | \ - PD_FLAGS_CHECK_IDENTITY | \ - PD_FLAGS_SNK_CAP_RECVD | \ - PD_FLAGS_TCPC_DRP_TOGGLE | \ - PD_FLAGS_EXPLICIT_CONTRACT | \ - PD_FLAGS_PREVIOUS_PD_CONN | \ - PD_FLAGS_CHECK_PR_ROLE | \ - PD_FLAGS_CHECK_DR_ROLE | \ - PD_FLAGS_PARTNER_EXTPOWER | \ - PD_FLAGS_VCONN_ON | \ - PD_FLAGS_TRY_SRC | \ - PD_FLAGS_PARTNER_USB_COMM | \ - PD_FLAGS_UPDATE_SRC_CAPS | \ - PD_FLAGS_TS_DTS_PARTNER | \ - PD_FLAGS_SNK_WAITING_BATT) - -/* Per-port battery backed RAM flags */ -#define PD_BBRMFLG_EXPLICIT_CONTRACT BIT(0) -#define PD_BBRMFLG_POWER_ROLE BIT(1) -#define PD_BBRMFLG_DATA_ROLE BIT(2) -#define PD_BBRMFLG_VCONN_ROLE BIT(3) - -/* Initial value for CC debounce variable */ -#define PD_CC_UNSET -1 - -enum pd_dual_role_states { - /* While disconnected, toggle between src and sink */ - PD_DRP_TOGGLE_ON, - /* Stay in src until disconnect, then stay in sink forever */ - PD_DRP_TOGGLE_OFF, - /* Stay in current power role, don't switch. No auto-toggle support */ - PD_DRP_FREEZE, - /* Switch to sink */ - PD_DRP_FORCE_SINK, - /* Switch to source */ - PD_DRP_FORCE_SOURCE, -}; -/** - * Get dual role state - * - * @param port Port number from which to get state - * @return Current dual-role state, from enum pd_dual_role_states - */ -enum pd_dual_role_states pd_get_dual_role(int port); -/** - * Set dual role state, from among enum pd_dual_role_states - * - * @param port Port number of which to set state - * @param state New state of dual-role port, selected from - * enum pd_dual_role_states - */ -void pd_set_dual_role(int port, enum pd_dual_role_states state); - -/** - * Get role, from among PD_ROLE_SINK and PD_ROLE_SOURCE - * - * @param port Port number from which to get role - */ -int pd_get_role(int port); - -/* Control Message type */ -enum pd_ctrl_msg_type { - /* 0 Reserved */ - PD_CTRL_GOOD_CRC = 1, - PD_CTRL_GOTO_MIN = 2, - PD_CTRL_ACCEPT = 3, - PD_CTRL_REJECT = 4, - PD_CTRL_PING = 5, - PD_CTRL_PS_RDY = 6, - PD_CTRL_GET_SOURCE_CAP = 7, - PD_CTRL_GET_SINK_CAP = 8, - PD_CTRL_DR_SWAP = 9, - PD_CTRL_PR_SWAP = 10, - PD_CTRL_VCONN_SWAP = 11, - PD_CTRL_WAIT = 12, - PD_CTRL_SOFT_RESET = 13, - /* 14-15 Reserved */ - - /* Used for REV 3.0 */ - PD_CTRL_NOT_SUPPORTED = 16, - PD_CTRL_GET_SOURCE_CAP_EXT = 17, - PD_CTRL_GET_STATUS = 18, - PD_CTRL_FR_SWAP = 19, - PD_CTRL_GET_PPS_STATUS = 20, - PD_CTRL_GET_COUNTRY_CODES = 21, - /* 22-31 Reserved */ -}; - -/* Battery Status Data Object fields for REV 3.0 */ -#define BSDO_CAP_UNKNOWN 0xffff -#define BSDO_CAP(n) (((n) & 0xffff) << 16) -#define BSDO_INVALID BIT(8) -#define BSDO_PRESENT BIT(9) -#define BSDO_DISCHARGING BIT(10) -#define BSDO_IDLE BIT(11) - -/* Get Battery Cap Message fields for REV 3.0 */ -#define BATT_CAP_REF(n) (((n) >> 16) & 0xff) - -/* Extended message type for REV 3.0 */ -enum pd_ext_msg_type { - /* 0 Reserved */ - PD_EXT_SOURCE_CAP = 1, - PD_EXT_STATUS = 2, - PD_EXT_GET_BATTERY_CAP = 3, - PD_EXT_GET_BATTERY_STATUS = 4, - PD_EXT_BATTERY_CAP = 5, - PD_EXT_GET_MANUFACTURER_INFO = 6, - PD_EXT_MANUFACTURER_INFO = 7, - PD_EXT_SECURITY_REQUEST = 8, - PD_EXT_SECURITY_RESPONSE = 9, - PD_EXT_FIRMWARE_UPDATE_REQUEST = 10, - PD_EXT_FIRMWARE_UPDATE_RESPONSE = 11, - PD_EXT_PPS_STATUS = 12, - PD_EXT_COUNTRY_INFO = 13, - PD_EXT_COUNTRY_CODES = 14, - /* 15-31 Reserved */ -}; - -/* Data message type */ -enum pd_data_msg_type { - /* 0 Reserved */ - PD_DATA_SOURCE_CAP = 1, - PD_DATA_REQUEST = 2, - PD_DATA_BIST = 3, - PD_DATA_SINK_CAP = 4, - /* 5-14 Reserved for REV 2.0 */ - PD_DATA_BATTERY_STATUS = 5, - PD_DATA_ALERT = 6, - PD_DATA_GET_COUNTRY_INFO = 7, - /* 8-14 Reserved for REV 3.0 */ - PD_DATA_VENDOR_DEF = 15, -}; - -/* CC Polarity type */ -enum pd_cc_polarity_type { - POLARITY_CC1 = 0, - POLARITY_CC2 = 1, -}; - -/* Protocol revision */ -enum pd_rev_type { - PD_REV10, - PD_REV20, - PD_REV30 -}; - -/* - * Power role. See 6.2.1.1.4 Port Power Role. Only applies to SOP packets. - * Replaced by pd_cable_plug for SOP' and SOP" packets. - */ -enum pd_power_role { - PD_ROLE_SINK = 0, - PD_ROLE_SOURCE = 1 -}; - -/* - * Data role. See 6.2.1.1.6 Port Data Role. Only applies to SOP. - * Replaced by reserved field for SOP' and SOP" packets. - */ -enum pd_data_role { - PD_ROLE_UFP = 0, - PD_ROLE_DFP = 1, - PD_ROLE_DISCONNECTED = 2, -}; - -/* - * Cable plug. See 6.2.1.1.7 Cable Plug. Only applies to SOP' and SOP". - * Replaced by pd_power_role for SOP packets. - */ -enum pd_cable_plug { - PD_PLUG_FROM_DFP_UFP = 0, - PD_PLUG_FROM_CABLE = 1 -}; - -/* Vconn role */ -#define PD_ROLE_VCONN_OFF 0 -#define PD_ROLE_VCONN_ON 1 - -/* chunk is a request or response in REV 3.0 */ -#define CHUNK_RESPONSE 0 -#define CHUNK_REQUEST 1 - -/* collision avoidance Rp values in REV 3.0 */ -#define SINK_TX_OK TYPEC_RP_3A0 -#define SINK_TX_NG TYPEC_RP_1A5 - -/* Port role at startup */ -#ifndef PD_ROLE_DEFAULT -#ifdef CONFIG_USB_PD_DUAL_ROLE -#define PD_ROLE_DEFAULT(port) PD_ROLE_SINK -#else -#define PD_ROLE_DEFAULT(port) PD_ROLE_SOURCE -#endif -#endif - -/* Port default state at startup */ -#ifdef CONFIG_USB_PD_DUAL_ROLE -#define PD_DEFAULT_STATE(port) ((PD_ROLE_DEFAULT(port) == PD_ROLE_SOURCE) ? \ - PD_STATE_SRC_DISCONNECTED : \ - PD_STATE_SNK_DISCONNECTED) -#else -#define PD_DEFAULT_STATE(port) PD_STATE_SRC_DISCONNECTED -#endif - -/* build extended message header */ -/* All extended messages are chunked, so set bit 15 */ -#define PD_EXT_HEADER(cnum, rchk, dsize) \ - (BIT(15) | ((cnum) << 11) | \ - ((rchk) << 10) | (dsize)) - -/* build message header */ -#define PD_HEADER(type, prole, drole, id, cnt, rev, ext) \ - ((type) | ((rev) << 6) | \ - ((drole) << 5) | ((prole) << 8) | \ - ((id) << 9) | ((cnt) << 12) | ((ext) << 15)) - -/* Used for processing pd header */ -#define PD_HEADER_EXT(header) (((header) >> 15) & 1) -#define PD_HEADER_CNT(header) (((header) >> 12) & 7) -/* - * NOTE: bit 4 was added in PD 3.0, and should be reserved and set to 0 in PD - * 2.0 messages - */ -#define PD_HEADER_TYPE(header) ((header) & 0x1F) -#define PD_HEADER_ID(header) (((header) >> 9) & 7) -#define PD_HEADER_PROLE(header) (((header) >> 8) & 1) -#define PD_HEADER_REV(header) (((header) >> 6) & 3) -#define PD_HEADER_DROLE(header) (((header) >> 5) & 1) - -/* - * The message header is a 16-bit value that's stored in a 32-bit data type. - * SOP* is encoded in bits 31 to 28 of the 32-bit data type. - * NOTE: This is not part of the PD spec. - */ -#define PD_HEADER_GET_SOP(header) (((header) >> 28) & 0xf) -#define PD_HEADER_SOP(sop) ((sop) << 28) -#define PD_MSG_SOP 0 -#define PD_MSG_SOPP 1 -#define PD_MSG_SOPPP 2 -#define PD_MSG_SOP_DBGP 3 -#define PD_MSG_SOP_DBGPP 4 -#define PD_MSG_SOP_CBL_RST 5 - -/* Used for processing pd extended header */ -#define PD_EXT_HEADER_CHUNKED(header) (((header) >> 15) & 1) -#define PD_EXT_HEADER_CHUNK_NUM(header) (((header) >> 11) & 0xf) -#define PD_EXT_HEADER_REQ_CHUNK(header) (((header) >> 10) & 1) -#define PD_EXT_HEADER_DATA_SIZE(header) ((header) & 0x1ff) - -/* Used to get extended header from the first 32-bit word of the message */ -#define GET_EXT_HEADER(msg) (msg & 0xffff) - -/* K-codes for special symbols */ -#define PD_SYNC1 0x18 -#define PD_SYNC2 0x11 -#define PD_SYNC3 0x06 -#define PD_RST1 0x07 -#define PD_RST2 0x19 -#define PD_EOP 0x0D - -/* Minimum PD supply current (mA) */ -#define PD_MIN_MA 500 - -/* Minimum PD voltage (mV) */ -#define PD_MIN_MV 5000 - -/* No connect voltage threshold for sources based on Rp */ -#define PD_SRC_DEF_VNC_MV 1600 -#define PD_SRC_1_5_VNC_MV 1600 -#define PD_SRC_3_0_VNC_MV 2600 - -/* Rd voltage threshold for sources based on Rp */ -#define PD_SRC_DEF_RD_THRESH_MV 200 -#define PD_SRC_1_5_RD_THRESH_MV 400 -#define PD_SRC_3_0_RD_THRESH_MV 800 - -/* Voltage threshold to detect connection when presenting Rd */ -#define PD_SNK_VA_MV 250 - -/* --- Policy layer functions --- */ - -/** Schedules the interrupt handler for the TCPC on a high priority task. */ -void schedule_deferred_pd_interrupt(int port); - -/* Request types for pd_build_request() */ -enum pd_request_type { - PD_REQUEST_VSAFE5V, - PD_REQUEST_MAX, -}; - -#ifdef CONFIG_USB_PD_REV30 -/** - * Get current PD Revision - * - * @param port USB-C port number - * @return 0 for PD_REV1.0, 1 for PD_REV2.0, 2 for PD_REV3.0 - */ -int pd_get_rev(int port); - -/** - * Get current PD VDO Version - * - * @param port USB-C port number - * @return 0 for PD_REV1.0, 1 for PD_REV2.0 - */ -int pd_get_vdo_ver(int port); -#else -#define pd_get_rev(n) PD_REV20 -#define pd_get_vdo_ver(n) VDM_VER10 -#endif - -/** - * Check if max voltage request is allowed (only used if - * CONFIG_USB_PD_CHECK_MAX_REQUEST_ALLOWED is defined). - * - * @return True if max voltage request allowed, False otherwise - */ -int pd_is_max_request_allowed(void); - -/** - * Waits for the TCPC to exit low power mode (including re-initializing) if it - * is currently in low power mode. If not, then the function immediately - * returns. - * - * @param port USB-C port number - */ -void pd_wait_exit_low_power(int port); - -/** - * Informs the TCPM state machine that code within the EC has accessed the TCPC - * via its communication bus (e.g. i2c). This is important to keep track of as - * accessing a TCPC may pull the hardware out of low-power mode. - * - * Note: Call this function after finished accessing the hardware. - * - * @param port USB-C port number - */ -void pd_device_accessed(int port); - -/** - * Prevents the TCPC from going back into low power mode. Invocations must be - * called in a pair from the same task, otherwise the TCPC will never re-enter - * low power mode. - * - * Note: This will not wake the device up if it is in LPM. - * - * @param port USB-C port number - * @param prevent 1 to prevent this port from entering LPM - */ -void pd_prevent_low_power_mode(int port, int prevent); - -/** - * Process source capabilities packet - * - * @param port USB-C port number - * @param cnt the number of Power Data Objects. - * @param src_caps Power Data Objects representing the source capabilities. - */ -void pd_process_source_cap(int port, int cnt, uint32_t *src_caps); - -/** - * Reduce the sink power consumption to a minimum value. - * - * @param port USB-C port number - * @param ma reduce current to minimum value. - * @param mv reduce voltage to minimum value. - */ -void pd_snk_give_back(int port, uint32_t * const ma, uint32_t * const mv); - -/** - * Put a cap on the max voltage requested as a sink. - * @param mv maximum voltage in millivolts. - */ -void pd_set_max_voltage(unsigned mv); - -/** - * Get the max voltage that can be requested as set by pd_set_max_voltage(). - * @return max voltage - */ -unsigned pd_get_max_voltage(void); - -/** - * Check if this board supports the given input voltage. - * - * @mv input voltage - * @return 1 if voltage supported, 0 if not - */ -int pd_is_valid_input_voltage(int mv); - -/** - * Request a new operating voltage. - * - * @param rdo Request Data Object with the selected operating point. - * @param port The port which the request came in on. - * @return EC_SUCCESS if we can get the requested voltage/OP, <0 else. - */ -int pd_check_requested_voltage(uint32_t rdo, const int port); - -/** - * Run board specific checks on request message - * - * @param rdo the request data object word sent by the sink. - * @param pdo_cnt the total number of source PDOs. - * @return EC_SUCCESS if request is ok , <0 else. - */ -int pd_board_check_request(uint32_t rdo, int pdo_cnt); - -/** - * Select a new output voltage. - * - * param idx index of the new voltage in the source PDO table. - */ -void pd_transition_voltage(int idx); - -/** - * Go back to the default/safe state of the power supply - * - * @param port USB-C port number - */ -void pd_power_supply_reset(int port); - -/** - * Enable or disable VBUS discharge for a given port. - * - * @param port USB-C port number - * @enable 1 if enabling discharge, 0 if disabling - */ -void pd_set_vbus_discharge(int port, int enable); - -/** - * Enable the power supply output after the ready delay. - * - * @param port USB-C port number - * @return EC_SUCCESS if the power supply is ready, <0 else. - */ -int pd_set_power_supply_ready(int port); - -/** - * Ask the specified voltage from the PD source. - * - * It triggers a new negotiation sequence with the source. - * @param port USB-C port number - * @param mv request voltage in millivolts. - */ -void pd_request_source_voltage(int port, int mv); - -/** - * Set a voltage limit from the PD source. - * - * If the source is currently active, it triggers a new negotiation. - * @param port USB-C port number - * @param mv limit voltage in millivolts. - */ -void pd_set_external_voltage_limit(int port, int mv); - -/** - * Set the PD input current limit. - * - * @param port USB-C port number - * @param max_ma Maximum current limit - * @param supply_voltage Voltage at which current limit is applied - */ -void pd_set_input_current_limit(int port, uint32_t max_ma, - uint32_t supply_voltage); - - -/** - * Update the power contract if it exists. - * - * @param port USB-C port number. - */ -void pd_update_contract(int port); - -/* Encode DTS status of port partner in current limit parameter */ -typedef uint32_t typec_current_t; -#define TYPEC_CURRENT_DTS_MASK BIT(31) -#define TYPEC_CURRENT_ILIM_MASK (~TYPEC_CURRENT_DTS_MASK) - -/** - * Set the type-C input current limit. - * - * @param port USB-C port number - * @param max_ma Maximum current limit - * @param supply_voltage Voltage at which current limit is applied - */ -void typec_set_input_current_limit(int port, typec_current_t max_ma, - uint32_t supply_voltage); - -/** - * Set the type-C current limit when sourcing current.. - * - * @param port USB-C port number - * @param rp One of enum tcpc_rp_value (eg TYPEC_RP_3A0) defining the limit. - */ -void typec_set_source_current_limit(int port, enum tcpc_rp_value rp); - -/** - * Verify board specific health status : current, voltages... - * - * @return EC_SUCCESS if the board is good, <0 else. - */ -int pd_board_checks(void); - -/** - * Return if VBUS is detected on type-C port - * - * @param port USB-C port number - * @return VBUS is detected - */ -int pd_snk_is_vbus_provided(int port); - -/** - * Notify PD protocol that VBUS has gone low - * - * @param port USB-C port number - */ -void pd_vbus_low(int port); - -/** - * Check if power swap is allowed. - * - * @param port USB-C port number - * @return True if power swap is allowed, False otherwise - */ -int pd_check_power_swap(int port); - -/** - * Check if data swap is allowed. - * - * @param port USB-C port number - * @param data_role current data role - * @return True if data swap is allowed, False otherwise - */ -int pd_check_data_swap(int port, int data_role); - -/** - * Check if vconn swap is allowed. - * - * @param port USB-C port number - * @return True if vconn swap is allowed, False otherwise - */ - -int pd_check_vconn_swap(int port); - -/** - * Check current power role for potential power swap - * - * @param port USB-C port number - * @param pr_role Our power role - * @param flags PD flags - */ -void pd_check_pr_role(int port, int pr_role, int flags); - -/** - * Check current data role for potential data swap - * - * @param port USB-C port number - * @param dr_role Our data role - * @param flags PD flags - */ -void pd_check_dr_role(int port, int dr_role, int flags); - -/** - * Check if we should charge from this device. This is - * basically a allow-list for chargers that are dual-role, - * don't set the externally powered bit, but we should charge - * from by default. - * - * @param vid Port partner Vendor ID - * @param pid Port partner Product ID - */ -int pd_charge_from_device(uint16_t vid, uint16_t pid); - -/** - * Execute data swap. - * - * @param port USB-C port number - * @param data_role new data role - */ -void pd_execute_data_swap(int port, int data_role); - -/** - * Get PD device info used for VDO_CMD_SEND_INFO / VDO_CMD_READ_INFO - * - * @param info_data pointer to info data array - */ -void pd_get_info(uint32_t *info_data); - -/** - * Handle 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_custom_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); - -/** - * Handle Custom VDMs for flashing. - * - * @param port USB-C port number - * @param cnt number of data objects in the payload. - * @param payload payload data. - * @return if >0, number of VDOs to send back. - */ -int pd_custom_flash_vdm(int port, int cnt, uint32_t *payload); - -/** - * Enter alternate mode on DFP - * - * @param port USB-C port number - * @param svid USB standard or vendor id to exit or zero for DFP amode reset. - * @param opos object position of mode to exit. - * @return vdm for UFP to be sent to enter mode or zero if not. - */ -uint32_t pd_dfp_enter_mode(int port, uint16_t svid, int opos); - -/** - * Get DisplayPort pin mode for DFP to request from UFP's capabilities. - * - * @param port USB-C port number. - * @param status DisplayPort Status VDO. - * @return one-hot PIN config to request. - */ -int pd_dfp_dp_get_pin_mode(int port, uint32_t status); - -/** - * Exit alternate mode on DFP - * - * @param port USB-C port number - * @param svid USB standard or vendor id to exit or zero for DFP amode reset. - * @param opos object position of mode to exit. - * @return 1 if UFP should be sent exit mode VDM. - */ -int pd_dfp_exit_mode(int port, uint16_t svid, int opos); - -/** - * Initialize policy engine for DFP - * - * @param port USB-C port number - */ -void pd_dfp_pe_init(int port); - -/** - * Return the VID of the USB PD accessory connected to a specified port - * - * @param port USB-C port number - * @return the USB Vendor Identifier or 0 if it doesn't exist - */ -uint16_t pd_get_identity_vid(int port); - -/** - * Return the PID of the USB PD accessory connected to a specified port - * - * @param port USB-C port number - * @return the USB Product Identifier or 0 if it doesn't exist - */ -uint16_t pd_get_identity_pid(int port); - -/** - * Returns the status of cable flag - CABLE_FLAGS_SOP_PRIME_ENABLE - * - * @param port USB-C port number - * @param data_role current data role - * @param pd_flags current pd flags - * @return For rev3.0, true if vconn is on - * For rev2.0, true if vconn is on and data_role is dfp - * False otherwise - */ -uint8_t is_sop_prime_ready(int port, uint8_t data_role, uint32_t pd_flags); - -/** - * Reset Cable type, Cable attributes and cable flags - * - * @param port USB-C port number - */ -void reset_pd_cable(int port); - -/** - * Return the type of cable attached - * - * @param port USB-C port number - * @return cable type - */ -enum idh_ptype get_usb_pd_mux_cable_type(int port); - -/** - * Store Device ID & RW hash of device - * - * @param port USB-C port number - * @param dev_id device identifier - * @param rw_hash pointer to rw_hash - * @param current_image current image: RW or RO - * @return true if the dev / hash match an existing hash - * in our table, false otherwise - */ -int pd_dev_store_rw_hash(int port, uint16_t dev_id, uint32_t *rw_hash, - uint32_t ec_current_image); - -/** - * Fast Role Swap was detected - * - * @param port USB-C port number - */ -void pd_got_frs_signal(int port); - -/** - * Try to fetch one PD log entry from accessory - * - * @param port USB-C accessory port number - * @return EC_RES_SUCCESS if the VDM was sent properly else error code - */ -int pd_fetch_acc_log_entry(int port); - -/** - * Analyze the log entry received as the VDO_CMD_GET_LOG payload. - * - * @param port USB-C accessory port number - * @param cnt number of data objects in payload - * @param payload payload data - */ -void pd_log_recv_vdm(int port, int cnt, uint32_t *payload); - -/** - * Send Vendor Defined Message - * - * @param port USB-C port number - * @param vid Vendor ID - * @param cmd VDO command number - * @param data Pointer to payload to send - * @param count number of data objects in payload - */ -void pd_send_vdm(int port, uint32_t vid, int cmd, const uint32_t *data, - int count); - -/* Power Data Objects for the source and the sink */ -extern const uint32_t pd_src_pdo[]; -extern const int pd_src_pdo_cnt; -extern const uint32_t pd_src_pdo_max[]; -extern const int pd_src_pdo_max_cnt; -extern const uint32_t pd_snk_pdo[]; -extern const int pd_snk_pdo_cnt; - -/** - * Request that a host event be sent to notify the AP of a PD power event. - * - * @param mask host event mask. - */ -#if defined(HAS_TASK_HOSTCMD) && !defined(TEST_BUILD) -void pd_send_host_event(int mask); -#else -static inline void pd_send_host_event(int mask) { } -#endif - -/** - * Determine if in alternate mode or not. - * - * @param port port number. - * @param svid USB standard or vendor id - * @return object position of mode chosen in alternate mode otherwise zero. - */ -int pd_alt_mode(int port, uint16_t svid); - -/** - * Send hpd over USB PD. - * - * @param port port number. - * @param hpd hotplug detect type. - */ -void pd_send_hpd(int port, enum hpd_event hpd); - -/** - * Enable USB Billboard Device. - */ -extern const struct deferred_data pd_usb_billboard_deferred_data; -/* --- Physical layer functions : chip specific --- */ - -/* Packet preparation/retrieval */ - -/** - * Prepare packet reading state machine. - * - * @param port USB-C port number - */ -void pd_init_dequeue(int port); - -/** - * Prepare packet reading state machine. - * - * @param port USB-C port number - * @param off current position in the packet buffer. - * @param len minimum size to read in bits. - * @param val the read bits. - * @return new position in the packet buffer. - */ -int pd_dequeue_bits(int port, int off, int len, uint32_t *val); - -/** - * Advance until the end of the preamble. - * - * @param port USB-C port number - * @return new position in the packet buffer. - */ -int pd_find_preamble(int port); - -/** - * Write the preamble in the TX buffer. - * - * @param port USB-C port number - * @return new position in the packet buffer. - */ -int pd_write_preamble(int port); - -/** - * Write one 10-period symbol in the TX packet. - * corresponding to a quartet with 4b5b encoding - * and Biphase Mark Coding. - * - * @param port USB-C port number - * @param bit_off current position in the packet buffer. - * @param val10 the 10-bit integer. - * @return new position in the packet buffer. - */ -int pd_write_sym(int port, int bit_off, uint32_t val10); - - -/** - * Ensure that we have an edge after EOP and we end up at level 0, - * also fill the last byte. - * - * @param port USB-C port number - * @param bit_off current position in the packet buffer. - * @return new position in the packet buffer. - */ -int pd_write_last_edge(int port, int bit_off); - -/** - * Do 4B5B encoding on a 32-bit word. - * - * @param port USB-C port number - * @param off current offset in bits inside the message - * @param val32 32-bit word value to encode - * @return new offset in the message in bits. - */ -int encode_word(int port, int off, uint32_t val32); - -/** - * Ensure that we have an edge after EOP and we end up at level 0, - * also fill the last byte. - * - * @param port USB-C port number - * @param header PD packet header - * @param cnt number of payload words - * @param data payload content - * @return length of the message in bits. - */ -int prepare_message(int port, uint16_t header, uint8_t cnt, - const uint32_t *data); - -/** - * Dump the current PD packet on the console for debug. - * - * @param port USB-C port number - * @param msg context string. - */ -void pd_dump_packet(int port, const char *msg); - -/** - * Change the TX data clock frequency. - * - * @param port USB-C port number - * @param freq frequency in hertz. - */ -void pd_set_clock(int port, int freq); - -/* TX/RX callbacks */ - -/** - * Start sending over the wire the prepared packet. - * - * @param port USB-C port number - * @param polarity plug polarity (0=CC1, 1=CC2). - * @param bit_len size of the packet in bits. - * @return length transmitted or negative if error - */ -int pd_start_tx(int port, int polarity, int bit_len); - -/** - * Set PD TX DMA to use circular mode. Call this before pd_start_tx() to - * continually loop over the transmit buffer given in pd_start_tx(). - * - * @param port USB-C port number - */ -void pd_tx_set_circular_mode(int port); - -/** - * Stop PD TX DMA circular mode transaction already in progress. - * - * @param port USB-C port number - */ -void pd_tx_clear_circular_mode(int port); - -/** - * Call when we are done sending a packet. - * - * @param port USB-C port number - * @param polarity plug polarity (0=CC1, 1=CC2). - */ -void pd_tx_done(int port, int polarity); - -/** - * Check whether the PD reception is started. - * - * @param port USB-C port number - * @return true if the reception is on-going. - */ -int pd_rx_started(int port); - -/** - * Suspend the PD task. - * @param port USB-C port number - * @param enable pass 0 to resume, anything else to suspend - */ -void pd_set_suspend(int port, int enable); - -/** - * Resume the PD task for a port after a period of time has elapsed. - * @param port USB-C port number - */ -void pd_deferred_resume(int port); - -/** - * Check if the port has been initialized and PD task has not been - * suspended. - * - * @param port USB-C port number - * @return true if the PD task is not suspended. - */ -int pd_is_port_enabled(int port); - -/* Callback when the hardware has detected an incoming packet */ -void pd_rx_event(int port); -/* Start sampling the CC line for reception */ -void pd_rx_start(int port); -/* Call when we are done reading a packet */ -void pd_rx_complete(int port); - -/* restart listening to the CC wire */ -void pd_rx_enable_monitoring(int port); -/* stop listening to the CC wire during transmissions */ -void pd_rx_disable_monitoring(int port); - -/* get time since last RX edge interrupt */ -uint64_t get_time_since_last_edge(int port); - -/** - * Deinitialize the hardware used for PD. - * - * @param port USB-C port number - */ -void pd_hw_release(int port); - -/** - * Initialize the hardware used for PD RX/TX. - * - * @param port USB-C port number - * @param role Role to initialize pins in - */ -void pd_hw_init(int port, int role); - -/** - * Initialize the reception side of hardware used for PD. - * - * This is a subset of pd_hw_init() including only : - * the comparators + the RX edge delay timer + the RX DMA. - * - * @param port USB-C port number - */ -void pd_hw_init_rx(int port); - -/* --- Protocol layer functions --- */ - -/** - * Decode a raw packet in the RX buffer. - * - * @param port USB-C port number - * @param payload buffer to store the packet payload (must be 7x 32-bit) - * @return the packet header or <0 in case of error - */ -int pd_analyze_rx(int port, uint32_t *payload); - -/** - * Check if PD communication is enabled - * - * @return true if it's enabled or false otherwise - */ -int pd_comm_is_enabled(int port); - -/** - * Get connected state - * - * @param port USB-C port number - * @return True if port is in connected state - */ -int pd_is_connected(int port); - -/** - * Execute a hard reset - * - * @param port USB-C port number - */ -void pd_execute_hard_reset(int port); - -/** - * Signal to protocol layer that PD transmit is complete - * - * @param port USB-C port number - * @param status status of the transmission - */ -void pd_transmit_complete(int port, int status); - -/** - * Get port polarity. - * - * @param port USB-C port number - */ -int pd_get_polarity(int port); - -/** - * Get port partner data swap capable status - * - * @param port USB-C port number - */ -int pd_get_partner_data_swap_capable(int port); - -/** - * Handle an overcurrent protection event. The port acting as a source has - * reported an overcurrent event. - * - * @param port: USB-C port number. - */ -void pd_handle_overcurrent(int port); - -/** - * Request power swap command to be issued - * - * @param port USB-C port number - */ -void pd_request_power_swap(int port); - -/** - * Try to become the VCONN source, if we are not already the source and the - * other side is willing to accept a VCONN swap. - * - * @param port USB-C port number - */ -void pd_try_vconn_src(int port); - -/** - * Request data swap command to be issued - * - * @param port USB-C port number - */ -void pd_request_data_swap(int port); - -/** - * Set the PD communication enabled flag. When communication is disabled, - * the port can still detect connection and source power but will not - * send or respond to any PD communication. - * - * @param port USB-C port number - * @param enable Enable flag to set - */ -void pd_comm_enable(int port, int enable); - -/** - * Set the PD pings enabled flag. When source has negotiated power over - * PD successfully, it can optionally send pings periodically based on - * this enable flag. - * - * @param port USB-C port number - * @param enable Enable flag to set - */ -void pd_ping_enable(int port, int enable); - -/* Issue PD soft reset */ -void pd_soft_reset(void); - -/* Prepare PD communication for reset */ -void pd_prepare_reset(void); - -/** - * Signal power request to indicate a charger update that affects the port. - * - * @param port USB-C port number - */ -void pd_set_new_power_request(int port); - -/** - * Return true if partner port is a DTS or TS capable of entering debug - * mode (eg. is presenting Rp/Rp or Rd/Rd). - * - * @param port USB-C port number - */ -int pd_ts_dts_plugged(int port); - -/** - * Return true if partner port is known to be PD capable. - * - * @param port USB-C port number - */ -int pd_capable(int port); - -/** - * Returns the source caps list - * - * @param port USB-C port number - */ -const uint32_t * const pd_get_src_caps(int port); - -/** - * Returns the number of source caps - * - * @param port USB-C port number - */ -uint8_t pd_get_src_cap_cnt(int port); - -/** - * Return true if partner port is capable of communication over USB data - * lines. - * - * @param port USB-C port number - */ -int pd_get_partner_usb_comm_capable(int port); - -/** - * Return true if vbus is present on the specified port. - * - * @param port USB-C port number - */ -int pd_is_vbus_present(int port); - -/** - * Get board specific current DisplayPort pin mode on the specified port. - * - * @param port USB-C port number - * @return MODE_DP_PIN_[A-E] if used else 0 - */ -uint8_t board_get_dp_pin_mode(int port); - -#ifdef CONFIG_USB_PD_PORT_MAX_COUNT -#ifdef CONFIG_USB_POWER_DELIVERY -/** - * Get board specific usb pd port count - * - * @return <= CONFIG_USB_PD_PORT_MAX_COUNT if configured in board file, - * else return CONFIG_USB_PD_PORT_MAX_COUNT - */ -uint8_t board_get_usb_pd_port_count(void); -#else -static inline uint8_t board_get_usb_pd_port_count(void) -{ - return CONFIG_USB_PD_PORT_MAX_COUNT; -} -#endif /* CONFIG_USB_POWER_DELIVERY */ -#endif /* CONFIG_USB_PD_PORT_MAX_COUNT */ - -/** - * Return true if specified PD port partner is UFP. - * - * @param port USB-C port number - */ -int pd_partner_is_ufp(int port); - -/** - * Return true if specified PD port is debug accessory. - * - * @param port USB-C port number - */ -int pd_is_debug_acc(int port); - -/* - * Notify the AP that we have entered into DisplayPort Alternate Mode. This - * sets a DP_ALT_MODE_ENTERED MKBP event which may wake the AP. - */ -void pd_notify_dp_alt_mode_entry(void); - -/* - * Determines the PD state of the port partner according to Table 4-10 in USB PD - * specification. - */ -enum pd_cc_states pd_get_cc_state( - enum tcpc_cc_voltage_status cc1, enum tcpc_cc_voltage_status cc2); - -/* ----- Logging ----- */ -#ifdef CONFIG_USB_PD_LOGGING -/** - * Record one event in the PD logging FIFO. - * - * @param type event type as defined by PD_EVENT_xx in ec_commands.h - * @param size_port payload size and port num (defined by PD_LOG_PORT_SIZE) - * @param data type-defined information - * @param payload pointer to the optional payload (0..16 bytes) - */ -void pd_log_event(uint8_t type, uint8_t size_port, - uint16_t data, void *payload); - -/** - * Retrieve one logged event and prepare a VDM with it. - * - * Used to answer the VDO_CMD_GET_LOG unstructured VDM. - * - * @param payload pointer to the payload data buffer (must be 7 words) - * @return number of 32-bit words in the VDM payload. - */ -int pd_vdm_get_log_entry(uint32_t *payload); -#else /* CONFIG_USB_PD_LOGGING */ -static inline void pd_log_event(uint8_t type, uint8_t size_port, - uint16_t data, void *payload) {} -static inline int pd_vdm_get_log_entry(uint32_t *payload) { return 0; } -#endif /* CONFIG_USB_PD_LOGGING */ - -#ifdef CONFIG_USB_PD_ALT_MODE_DFP -/** - * Prepare for a sysjump by exiting any alternate modes, if PD communication is - * allowed. - * - * Note: this call will block until the PD task has finished its exit mode and - * re-awoken the calling task. - */ -void pd_prepare_sysjump(void); -#endif - -#endif /* __CROS_EC_USB_PD_H */ diff --git a/include/usb_pd_tcpc.h b/include/usb_pd_tcpc.h deleted file mode 100644 index 4ac22d4d86..0000000000 --- a/include/usb_pd_tcpc.h +++ /dev/null @@ -1,66 +0,0 @@ -/* 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. - */ - -/* USB Power delivery port controller */ - -#ifndef __CROS_EC_USB_PD_TCPC_H -#define __CROS_EC_USB_PD_TCPC_H - -#include <stdint.h> -#include "usb_pd_tcpm.h" - -/* If we are a TCPC but do not a TCPM, then we implement the slave TCPCI */ -#if defined(CONFIG_USB_PD_TCPC) && !defined(CONFIG_USB_PD_TCPM_STUB) -#define TCPCI_I2C_SLAVE -#endif - -#ifdef TCPCI_I2C_SLAVE -/* Convert TCPC address to type-C port number */ -#define TCPC_ADDR_TO_PORT(addr) ((addr) \ - - I2C_GET_ADDR(CONFIG_TCPC_I2C_BASE_ADDR_FLAGS)) -/* Check if the i2c address belongs to TCPC */ -#define ADDR_IS_TCPC(addr) (((addr) & 0x7E) \ - == I2C_GET_ADDR(CONFIG_TCPC_I2C_BASE_ADDR_FLAGS)) -#endif - -/** - * Process incoming TCPCI I2C command - * - * @param read This is a read request. If 0, this is a write request. - * @param len Length of incoming payload - * @param payload Pointer to incoming and outgoing data - * @param send_response Function to call to send response if necessary - */ -void tcpc_i2c_process(int read, int port, int len, uint8_t *payload, - void (*send_response)(int)); - -/** - * Handle VBUS wake interrupts - * - * @param signal The VBUS wake interrupt signal - */ -void pd_vbus_evt_p0(enum gpio_signal signal); -void pd_vbus_evt_p1(enum gpio_signal signal); - -/* Methods for TCPCI slaves (e.g. zinger) to get/set their internal state */ -int tcpc_alert_status(int port, int *alert); -int tcpc_alert_status_clear(int port, uint16_t mask); -int tcpc_alert_mask_set(int port, uint16_t mask); -int tcpc_get_cc(int port, enum tcpc_cc_voltage_status *cc1, - enum tcpc_cc_voltage_status *cc2); -int tcpc_select_rp_value(int port, int rp); -int tcpc_set_cc(int port, int pull); -int tcpc_set_polarity(int port, int polarity); -int tcpc_set_power_status_mask(int port, uint8_t mask); -int tcpc_set_vconn(int port, int enable); -int tcpc_set_msg_header(int port, int power_role, int data_role); -int tcpc_set_rx_enable(int port, int enable); -int tcpc_get_message(int port, uint32_t *payload, int *head); -int tcpc_transmit(int port, enum tcpm_transmit_type type, uint16_t header, - const uint32_t *data); -int rx_buf_is_empty(int port); -void rx_buf_clear(int port); - -#endif /* __CROS_EC_USB_PD_TCPC_H */ diff --git a/include/usb_pd_tcpm.h b/include/usb_pd_tcpm.h deleted file mode 100644 index ab70c9a7f7..0000000000 --- a/include/usb_pd_tcpm.h +++ /dev/null @@ -1,429 +0,0 @@ -/* 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. - */ - -/* USB Power delivery port management */ - -#ifndef __CROS_EC_USB_PD_TCPM_H -#define __CROS_EC_USB_PD_TCPM_H - -#include "ec_commands.h" -#include "i2c.h" - -/* Default retry count for transmitting */ -#define PD_RETRY_COUNT 3 - -/* Time to wait for TCPC to complete transmit */ -#define PD_T_TCPC_TX_TIMEOUT (100*MSEC) - -enum usbpd_cc_pin { - USBPD_CC_PIN_1, - USBPD_CC_PIN_2, -}; - -/* Detected resistor values of port partner */ -enum tcpc_cc_voltage_status { - TYPEC_CC_VOLT_OPEN = 0, - TYPEC_CC_VOLT_RA = 1, /* Port partner is applying Ra */ - TYPEC_CC_VOLT_RD = 2, /* Port partner is applying Rd */ - TYPEC_CC_VOLT_RP_DEF = 5, /* Port partner is applying Rp (0.5A) */ - TYPEC_CC_VOLT_RP_1_5 = 6, /* Port partner is applying Rp (1.5A) */ - TYPEC_CC_VOLT_RP_3_0 = 7, /* Port partner is applying Rp (3.0A) */ -}; - -/* Resistor types we apply on our side of the CC lines */ -enum tcpc_cc_pull { - TYPEC_CC_RA = 0, - TYPEC_CC_RP = 1, - TYPEC_CC_RD = 2, - TYPEC_CC_OPEN = 3, - TYPEC_CC_RA_RD = 4, /* Powered cable with Sink */ -}; - -/* Pull-up values we apply as a SRC to advertise different current limits */ -enum tcpc_rp_value { - TYPEC_RP_USB = 0, - TYPEC_RP_1A5 = 1, - TYPEC_RP_3A0 = 2, - TYPEC_RP_RESERVED = 3, -}; - -enum tcpm_transmit_type { - TCPC_TX_SOP = 0, - TCPC_TX_SOP_PRIME = 1, - TCPC_TX_SOP_PRIME_PRIME = 2, - TCPC_TX_SOP_DEBUG_PRIME = 3, - TCPC_TX_SOP_DEBUG_PRIME_PRIME = 4, - TCPC_TX_HARD_RESET = 5, - TCPC_TX_CABLE_RESET = 6, - TCPC_TX_BIST_MODE_2 = 7 -}; - -/* Number of valid Transmit Types */ -#define NUM_SOP_STAR_TYPES (TCPC_TX_SOP_DEBUG_PRIME_PRIME + 1) - -enum tcpc_transmit_complete { - TCPC_TX_UNSET = -1, - TCPC_TX_COMPLETE_SUCCESS = 0, - TCPC_TX_COMPLETE_DISCARDED = 1, - TCPC_TX_COMPLETE_FAILED = 2, -}; - -/** - * Returns whether the sink has detected a Rp resistor on the other side. - */ -static inline int cc_is_rp(enum tcpc_cc_voltage_status cc) -{ - return (cc == TYPEC_CC_VOLT_RP_DEF) || (cc == TYPEC_CC_VOLT_RP_1_5) || - (cc == TYPEC_CC_VOLT_RP_3_0); -} - -/** - * Returns true if both CC lines are completely open. - */ -static inline int cc_is_open(enum tcpc_cc_voltage_status cc1, - enum tcpc_cc_voltage_status cc2) -{ - return cc1 == TYPEC_CC_VOLT_OPEN && cc2 == TYPEC_CC_VOLT_OPEN; -} - -/** - * Returns true if we detect the port partner is a snk debug accessory. - */ -static inline int cc_is_snk_dbg_acc(enum tcpc_cc_voltage_status cc1, - enum tcpc_cc_voltage_status cc2) -{ - return cc1 == TYPEC_CC_VOLT_RD && cc2 == TYPEC_CC_VOLT_RD; -} - -/** - * Returns true if the port partner is an audio accessory. - */ -static inline int cc_is_audio_acc(enum tcpc_cc_voltage_status cc1, - enum tcpc_cc_voltage_status cc2) -{ - return cc1 == TYPEC_CC_VOLT_RA && cc2 == TYPEC_CC_VOLT_RA; -} - -/** - * Returns true if the port partner is presenting at least one Rd - */ -static inline int cc_is_at_least_one_rd(enum tcpc_cc_voltage_status cc1, - enum tcpc_cc_voltage_status cc2) -{ - return cc1 == TYPEC_CC_VOLT_RD || cc2 == TYPEC_CC_VOLT_RD; -} - -/** - * Returns true if the port partner is presenting Rd on only one CC line. - */ -static inline int cc_is_only_one_rd(enum tcpc_cc_voltage_status cc1, - enum tcpc_cc_voltage_status cc2) -{ - return cc_is_at_least_one_rd(cc1, cc2) && cc1 != cc2; -} - -struct tcpm_drv { - /** - * Initialize TCPM driver and wait for TCPC readiness. - * - * @param port Type-C port number - * - * @return EC_SUCCESS or error - */ - int (*init)(int port); - - /** - * Release the TCPM hardware and disconnect the driver. - * Only .init() can be called after .release(). - * - * @param port Type-C port number - * - * @return EC_SUCCESS or error - */ - int (*release)(int port); - - /** - * Read the CC line status. - * - * @param port Type-C port number - * @param cc1 pointer to CC status for CC1 - * @param cc2 pointer to CC status for CC2 - * - * @return EC_SUCCESS or error - */ - int (*get_cc)(int port, enum tcpc_cc_voltage_status *cc1, - enum tcpc_cc_voltage_status *cc2); - - /** - * Read VBUS - * - * @param port Type-C port number - * - * @return 0 => VBUS not detected, 1 => VBUS detected - */ - int (*get_vbus_level)(int port); - - /** - * Set the value of the CC pull-up used when we are a source. - * - * @param port Type-C port number - * @param rp One of enum tcpc_rp_value - * - * @return EC_SUCCESS or error - */ - int (*select_rp_value)(int port, int rp); - - /** - * Set the CC pull resistor. This sets our role as either source or sink. - * - * @param port Type-C port number - * @param pull One of enum tcpc_cc_pull - * - * @return EC_SUCCESS or error - */ - int (*set_cc)(int port, int pull); - - /** - * Set polarity - * - * @param port Type-C port number - * @param polarity 0=> transmit on CC1, 1=> transmit on CC2 - * - * @return EC_SUCCESS or error - */ - int (*set_polarity)(int port, int polarity); - - /** - * Set Vconn. - * - * @param port Type-C port number - * @param polarity Polarity of the CC line to read - * - * @return EC_SUCCESS or error - */ - int (*set_vconn)(int port, int enable); - - /** - * Set PD message header to use for goodCRC - * - * @param port Type-C port number - * @param power_role Power role to use in header - * @param data_role Data role to use in header - * - * @return EC_SUCCESS or error - */ - int (*set_msg_header)(int port, int power_role, int data_role); - - /** - * Set RX enable flag - * - * @param port Type-C port number - * @enable true for enable, false for disable - * - * @return EC_SUCCESS or error - */ - int (*set_rx_enable)(int port, int enable); - - /** - * Read received PD message from the TCPC - * - * @param port Type-C port number - * @param payload Pointer to location to copy payload of message - * @param header of message - * - * @return EC_SUCCESS or error - */ - int (*get_message_raw)(int port, uint32_t *payload, int *head); - - /** - * Transmit PD message - * - * @param port Type-C port number - * @param type Transmit type - * @param header Packet header - * @param cnt Number of bytes in payload - * @param data Payload - * - * @return EC_SUCCESS or error - */ - int (*transmit)(int port, enum tcpm_transmit_type type, uint16_t header, - const uint32_t *data); - - /** - * TCPC is asserting alert - * - * @param port Type-C port number - */ - void (*tcpc_alert)(int port); - - /** - * Discharge PD VBUS on src/sink disconnect & power role swap - * - * @param port Type-C port number - * @param enable Discharge enable or disable - */ - void (*tcpc_discharge_vbus)(int port, int enable); - -#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE - /** - * Enable TCPC auto DRP toggling. - * - * @param port Type-C port number - * - * @return EC_SUCCESS or error - */ - int (*drp_toggle)(int port); -#endif - - /** - * Get firmware version. - * - * @param port Type-C port number - * @param live Fetch live chip info or hard-coded + cached info - * @param info Pointer to pointer to PD chip info - * - * @return EC_SUCCESS or error - */ - int (*get_chip_info)(int port, int live, - struct ec_response_pd_chip_info_v1 **info); - -#ifdef CONFIG_USBC_PPC - /** - * Send SinkVBUS or DisableSinkVBUS command - * - * @param port Type-C port number - * @enable true for enable, false for disable - * - * @return EC_SUCCESS or error - */ - int (*set_snk_ctrl)(int port, int enable); - - /** - * Send SourceVBUS or DisableSourceVBUS command - * - * @param port Type-C port number - * @enable true for enable, false for disable - * - * @return EC_SUCCESS or error - */ - int (*set_src_ctrl)(int port, int enable); -#endif - -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER - /** - * Instructs the TCPC to enter into low power mode. - * - * NOTE: Do no use tcpc_(read|write) style helper methods in this - * function. You must use i2c_(read|write) directly. - * - * @param port Type-C port number - * - * @return EC_SUCCESS or error - */ - int (*enter_low_power_mode)(int port); -#endif - - /** - * Enable/Disable TCPC FRS detection - * - * @param port Type-C port number - * @param enable FRS enable (true) disable (false) - */ - void (*set_frs_enable)(int port, int enable); -}; - -/* - * Macros for tcpc_config_t flags field. - * - * Bit 0 --> Polarity for TCPC alert. Set to 1 if alert is active high. - * Bit 1 --> Set to 1 if TCPC alert line is open-drain instead of push-pull. - * Bit 2 --> Polarity for TCPC reset. Set to 1 if reset line is active high. - */ -#define TCPC_FLAGS_ALERT_ACTIVE_HIGH BIT(0) -#define TCPC_FLAGS_ALERT_OD BIT(1) -#define TCPC_FLAGS_RESET_ACTIVE_HIGH BIT(2) - -struct tcpc_config_t { - enum ec_bus_type bus_type; /* enum ec_bus_type */ - union { - struct i2c_info_t i2c_info; - }; - const struct tcpm_drv *drv; - /* See TCPC_FLAGS_* above */ - uint32_t flags; -#ifdef CONFIG_INTEL_VIRTUAL_MUX - /* - * 0-3: Corresponding USB2 port number (1 ~ 15) - * 4-7: Corresponding USB3 port number (1 ~ 15) - */ - uint8_t usb23; -#endif -}; - -#ifndef CONFIG_USB_PD_TCPC_RUNTIME_CONFIG -extern const struct tcpc_config_t tcpc_config[]; -#else -extern struct tcpc_config_t tcpc_config[]; -#endif - -/** - * Returns the PD_STATUS_TCPC_ALERT_* mask corresponding to the TCPC ports - * that are currently asserting ALERT. - * - * @return PD_STATUS_TCPC_ALERT_* mask. - */ -uint16_t tcpc_get_alert_status(void); - -/** - * Optional, set the TCPC power mode. - * - * @param port Type-C port number - * @param mode 0: off/sleep, 1: on/awake - */ -void board_set_tcpc_power_mode(int port, int mode) __attribute__((weak)); - -/** - * Initialize TCPC. - * - * @param port Type-C port number - */ -void tcpc_init(int port); - -/** - * TCPC is asserting alert - * - * @param port Type-C port number - */ -void tcpc_alert_clear(int port); - -/** - * Run TCPC task once. This checks for incoming messages, processes - * any outgoing messages, and reads CC lines. - * - * @param port Type-C port number - * @param evt Event type that woke up this task - */ -int tcpc_run(int port, int evt); - -/** - * Initialize board specific TCPC functions post TCPC initialization. - * - * @param port Type-C port number - * - * @return EC_SUCCESS or error - */ -int board_tcpc_post_init(int port) __attribute__((weak)); - -/** - * Turn on/off VCONN power switch in board specific code. - * - * @param port Type-C port number - * @param cc_pin 0:CC pin 0, 1: CC pin 1 - * @param enabled 1: Enable VCONN, 0: Disable VCONN - * - */ -void board_pd_vconn_ctrl(int port, enum usbpd_cc_pin cc_pin, int enabled); - -#endif /* __CROS_EC_USB_PD_TCPM_H */ |