mirror of
https://github.com/u-boot/u-boot.git
synced 2025-04-22 12:54:37 +00:00

This adds TCPM framework in preparation for fusb302 support, which can handle USB power delivery messages. This is needed to solve issues with devices, that are running from a USB-C port supporting USB-PD, but not having a battery. Such a device currently boots to the kernel without interacting with the power-supply at all. If there are no USB-PD message replies within 5 seconds, the power-supply assumes the peripheral is not capable of USB-PD. It usually takes more than 5 seconds for the system to reach the kernel and probe the I2C based fusb302 chip driver. Thus the system always runs into this state. The power-supply's solution to fix this error state is a hard reset, which involves removing the power from VBUS. Boards without a battery (or huge capacitors) will reset at this point resulting in a boot loop. This imports the TCPM framework from the kernel. The porting has originally been done by Rockchip using hardware timers and the Linux kernel's TCPM code from some years ago. I had a look at upgrading to the latest TCPM kernel code, but that beast became a lot more complex due to adding more USB-C features. I believe these features are not needed in U-Boot and with multiple kthreads and hrtimers being involved it is non-trivial to port them. Instead I worked on stripping down features from the Rockchip port to an even more basic level. Also the TCPM code has been reworked to avoid complete use of any timers (Rockchip used SoC specific hardware timers + IRQ to implement delayed work mechanism). Instead the delayed state changes are handled directly from the poll loop. Note, that (in contrast to the original Rockchip port) the state machine has the same hard reset quirk, that the kernel has - i.e. it avoids disabling the CC pin resistors for devices that are not self-powered. Without that quirk, the Radxa Rock 5B will not just end up doing a machine reset when a hard reset is triggered, but will not even recover, because the CPU will loose power and the FUSB302 will keep this state because of leak voltage arriving through the RX serial pin (assuming a serial adapter is connected). This also includes a 'tcpm' command, which can be used to get information about the current state and the negotiated voltage and current. Co-developed-by: Wang Jie <dave.wang@rock-chips.com> Signed-off-by: Wang Jie <dave.wang@rock-chips.com> Tested-by: Soeren Moch <smoch@web.de> Tested-by: Anand Moon <linux.amoon@gmail.com> Reviewed-by: Jonas Karlman <jonas@kwiboo.se> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
173 lines
3.9 KiB
C
173 lines
3.9 KiB
C
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
/*
|
|
* Copyright 2015-2017 Google, Inc
|
|
* Copyright 2024 Collabora
|
|
*/
|
|
|
|
#ifndef __LINUX_USB_TCPM_INTERNAL_H
|
|
#define __LINUX_USB_TCPM_INTERNAL_H
|
|
|
|
#define FOREACH_TCPM_STATE(S) \
|
|
S(INVALID_STATE), \
|
|
S(TOGGLING), \
|
|
S(SRC_UNATTACHED), \
|
|
S(SRC_ATTACH_WAIT), \
|
|
S(SRC_ATTACHED), \
|
|
S(SRC_STARTUP), \
|
|
S(SRC_SEND_CAPABILITIES), \
|
|
S(SRC_SEND_CAPABILITIES_TIMEOUT), \
|
|
S(SRC_NEGOTIATE_CAPABILITIES), \
|
|
S(SRC_TRANSITION_SUPPLY), \
|
|
S(SRC_READY), \
|
|
S(SRC_WAIT_NEW_CAPABILITIES), \
|
|
\
|
|
S(SNK_UNATTACHED), \
|
|
S(SNK_ATTACH_WAIT), \
|
|
S(SNK_DEBOUNCED), \
|
|
S(SNK_ATTACHED), \
|
|
S(SNK_STARTUP), \
|
|
S(SNK_DISCOVERY), \
|
|
S(SNK_DISCOVERY_DEBOUNCE), \
|
|
S(SNK_DISCOVERY_DEBOUNCE_DONE), \
|
|
S(SNK_WAIT_CAPABILITIES), \
|
|
S(SNK_NEGOTIATE_CAPABILITIES), \
|
|
S(SNK_TRANSITION_SINK), \
|
|
S(SNK_TRANSITION_SINK_VBUS), \
|
|
S(SNK_READY), \
|
|
\
|
|
S(HARD_RESET_SEND), \
|
|
S(HARD_RESET_START), \
|
|
S(SRC_HARD_RESET_VBUS_OFF), \
|
|
S(SRC_HARD_RESET_VBUS_ON), \
|
|
S(SNK_HARD_RESET_SINK_OFF), \
|
|
S(SNK_HARD_RESET_WAIT_VBUS), \
|
|
S(SNK_HARD_RESET_SINK_ON), \
|
|
\
|
|
S(SOFT_RESET), \
|
|
S(SOFT_RESET_SEND), \
|
|
\
|
|
S(DR_SWAP_ACCEPT), \
|
|
S(DR_SWAP_CHANGE_DR), \
|
|
\
|
|
S(ERROR_RECOVERY), \
|
|
S(PORT_RESET), \
|
|
S(PORT_RESET_WAIT_OFF)
|
|
|
|
#define GENERATE_TCPM_ENUM(e) e
|
|
#define GENERATE_TCPM_STRING(s) #s
|
|
#define TCPM_POLL_EVENT_TIME_OUT 2000
|
|
|
|
enum tcpm_state {
|
|
FOREACH_TCPM_STATE(GENERATE_TCPM_ENUM)
|
|
};
|
|
|
|
enum pd_msg_request {
|
|
PD_MSG_NONE = 0,
|
|
PD_MSG_CTRL_REJECT,
|
|
PD_MSG_CTRL_WAIT,
|
|
PD_MSG_CTRL_NOT_SUPP,
|
|
PD_MSG_DATA_SINK_CAP,
|
|
PD_MSG_DATA_SOURCE_CAP,
|
|
};
|
|
|
|
struct tcpm_port {
|
|
enum typec_port_type typec_type;
|
|
int typec_prefer_role;
|
|
|
|
enum typec_role vconn_role;
|
|
enum typec_role pwr_role;
|
|
enum typec_data_role data_role;
|
|
|
|
struct typec_partner *partner;
|
|
|
|
enum typec_cc_status cc_req;
|
|
enum typec_cc_status cc1;
|
|
enum typec_cc_status cc2;
|
|
enum typec_cc_polarity polarity;
|
|
|
|
bool attached;
|
|
bool connected;
|
|
int poll_event_cnt;
|
|
enum typec_port_type port_type;
|
|
|
|
/*
|
|
* Set to true when vbus is greater than VSAFE5V min.
|
|
* Set to false when vbus falls below vSinkDisconnect max threshold.
|
|
*/
|
|
bool vbus_present;
|
|
|
|
/*
|
|
* Set to true when vbus is less than VSAFE0V max.
|
|
* Set to false when vbus is greater than VSAFE0V max.
|
|
*/
|
|
bool vbus_vsafe0v;
|
|
|
|
bool vbus_never_low;
|
|
bool vbus_source;
|
|
bool vbus_charge;
|
|
|
|
int try_role;
|
|
|
|
enum pd_msg_request queued_message;
|
|
|
|
enum tcpm_state enter_state;
|
|
enum tcpm_state prev_state;
|
|
enum tcpm_state state;
|
|
enum tcpm_state delayed_state;
|
|
unsigned long delay_ms;
|
|
|
|
bool state_machine_running;
|
|
|
|
bool tx_complete;
|
|
enum tcpm_transmit_status tx_status;
|
|
|
|
unsigned int negotiated_rev;
|
|
unsigned int message_id;
|
|
unsigned int caps_count;
|
|
unsigned int hard_reset_count;
|
|
bool pd_capable;
|
|
bool explicit_contract;
|
|
unsigned int rx_msgid;
|
|
|
|
/* Partner capabilities/requests */
|
|
u32 sink_request;
|
|
u32 source_caps[PDO_MAX_OBJECTS];
|
|
unsigned int nr_source_caps;
|
|
u32 sink_caps[PDO_MAX_OBJECTS];
|
|
unsigned int nr_sink_caps;
|
|
|
|
/*
|
|
* whether to wait for the Type-C device to send the DR_SWAP Message flag
|
|
* For Type-C device with Dual-Role Power and Dual-Role Data, the port side
|
|
* is used as sink + ufp, then the tcpm framework needs to wait for Type-C
|
|
* device to initiate DR_swap Message.
|
|
*/
|
|
bool wait_dr_swap_message;
|
|
|
|
/* Local capabilities */
|
|
u32 src_pdo[PDO_MAX_OBJECTS];
|
|
unsigned int nr_src_pdo;
|
|
u32 snk_pdo[PDO_MAX_OBJECTS];
|
|
unsigned int nr_snk_pdo;
|
|
|
|
unsigned int operating_snk_mw;
|
|
bool update_sink_caps;
|
|
|
|
/* Requested current / voltage to the port partner */
|
|
u32 req_current_limit;
|
|
u32 req_supply_voltage;
|
|
/* Actual current / voltage limit of the local port */
|
|
u32 current_limit;
|
|
u32 supply_voltage;
|
|
|
|
/* port belongs to a self powered device */
|
|
bool self_powered;
|
|
|
|
unsigned long delay_target;
|
|
};
|
|
|
|
extern const char * const tcpm_states[];
|
|
|
|
int tcpm_post_probe(struct udevice *dev);
|
|
|
|
#endif
|