mirror of
https://github.com/u-boot/u-boot.git
synced 2025-04-11 07:24:46 +00:00
usb: tcpm: add core framework
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>
This commit is contained in:
parent
608a31bdec
commit
1db4c0ac77
15 changed files with 3451 additions and 0 deletions
1
Makefile
1
Makefile
|
@ -887,6 +887,7 @@ libs-y += drivers/usb/musb/
|
|||
libs-y += drivers/usb/musb-new/
|
||||
libs-y += drivers/usb/isp1760/
|
||||
libs-y += drivers/usb/phy/
|
||||
libs-y += drivers/usb/tcpm/
|
||||
libs-y += drivers/usb/ulpi/
|
||||
ifdef CONFIG_POST
|
||||
libs-y += post/
|
||||
|
|
|
@ -221,6 +221,13 @@ config CMD_REGINFO
|
|||
help
|
||||
Register dump
|
||||
|
||||
config CMD_TCPM
|
||||
bool "tcpm"
|
||||
depends on TYPEC_TCPM
|
||||
help
|
||||
Show voltage and current negotiated via USB PD as well as the
|
||||
current state of the Type C Port Manager (TCPM) state machine.
|
||||
|
||||
config CMD_TLV_EEPROM
|
||||
bool "tlv_eeprom"
|
||||
depends on I2C_EEPROM
|
||||
|
|
|
@ -179,6 +179,7 @@ obj-$(CONFIG_CMD_SMBIOS) += smbios.o
|
|||
obj-$(CONFIG_CMD_SMC) += smccc.o
|
||||
obj-$(CONFIG_CMD_SYSBOOT) += sysboot.o
|
||||
obj-$(CONFIG_CMD_STACKPROTECTOR_TEST) += stackprot_test.o
|
||||
obj-$(CONFIG_CMD_TCPM) += tcpm.o
|
||||
obj-$(CONFIG_CMD_TEMPERATURE) += temperature.o
|
||||
obj-$(CONFIG_CMD_TERMINAL) += terminal.o
|
||||
obj-$(CONFIG_CMD_TIME) += time.o
|
||||
|
|
136
cmd/tcpm.c
Normal file
136
cmd/tcpm.c
Normal file
|
@ -0,0 +1,136 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2024 Collabora
|
||||
*/
|
||||
|
||||
#include <command.h>
|
||||
#include <errno.h>
|
||||
#include <dm.h>
|
||||
#include <dm/uclass-internal.h>
|
||||
#include <usb/tcpm.h>
|
||||
|
||||
#define LIMIT_DEV 32
|
||||
#define LIMIT_PARENT 20
|
||||
|
||||
static struct udevice *currdev;
|
||||
|
||||
static int do_dev(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
|
||||
{
|
||||
int devnum, ret;
|
||||
|
||||
switch (argc) {
|
||||
case 2:
|
||||
devnum = (int)dectoul(argv[1], NULL);
|
||||
ret = tcpm_get(devnum, &currdev);
|
||||
if (ret) {
|
||||
log_err("Can't get TCPM %d: %d (%s)!\n", devnum, ret, errno_str(ret));
|
||||
return CMD_RET_FAILURE;
|
||||
}
|
||||
case 1:
|
||||
if (!currdev) {
|
||||
log_err("TCPM device is not set!\n\n");
|
||||
return CMD_RET_USAGE;
|
||||
}
|
||||
|
||||
printf("dev: %d @ %s\n", dev_seq(currdev), currdev->name);
|
||||
}
|
||||
|
||||
return CMD_RET_SUCCESS;
|
||||
}
|
||||
|
||||
static int do_list(struct cmd_tbl *cmdtp, int flag, int argc,
|
||||
char *const argv[])
|
||||
{
|
||||
struct udevice *dev;
|
||||
int ret, err = 0;
|
||||
|
||||
printf("| ID | %-*.*s| %-*.*s| %s @ %s\n",
|
||||
LIMIT_DEV, LIMIT_DEV, "Name",
|
||||
LIMIT_PARENT, LIMIT_PARENT, "Parent name",
|
||||
"Parent uclass", "seq");
|
||||
|
||||
for (ret = uclass_first_device_check(UCLASS_TCPM, &dev); dev;
|
||||
ret = uclass_next_device_check(&dev)) {
|
||||
if (ret)
|
||||
err = ret;
|
||||
|
||||
printf("| %2d | %-*.*s| %-*.*s| %s @ %d | status: %i\n",
|
||||
dev_seq(dev),
|
||||
LIMIT_DEV, LIMIT_DEV, dev->name,
|
||||
LIMIT_PARENT, LIMIT_PARENT, dev->parent->name,
|
||||
dev_get_uclass_name(dev->parent), dev_seq(dev->parent),
|
||||
ret);
|
||||
}
|
||||
|
||||
if (err)
|
||||
return CMD_RET_FAILURE;
|
||||
|
||||
return CMD_RET_SUCCESS;
|
||||
}
|
||||
|
||||
int do_print_info(struct udevice *dev)
|
||||
{
|
||||
enum typec_orientation orientation = tcpm_get_orientation(dev);
|
||||
const char *state = tcpm_get_state(dev);
|
||||
int pd_rev = tcpm_get_pd_rev(dev);
|
||||
int mv = tcpm_get_voltage(dev);
|
||||
int ma = tcpm_get_current(dev);
|
||||
enum typec_role pwr_role = tcpm_get_pwr_role(dev);
|
||||
enum typec_data_role data_role = tcpm_get_data_role(dev);
|
||||
bool connected = tcpm_is_connected(dev);
|
||||
|
||||
if (!connected) {
|
||||
printf("TCPM State: %s\n", state);
|
||||
return 0;
|
||||
}
|
||||
|
||||
printf("Orientation: %s\n", typec_orientation_name[orientation]);
|
||||
printf("PD Revision: %s\n", typec_pd_rev_name[pd_rev]);
|
||||
printf("Power Role: %s\n", typec_role_name[pwr_role]);
|
||||
printf("Data Role: %s\n", typec_data_role_name[data_role]);
|
||||
printf("Voltage: %2d.%03d V\n", mv / 1000, mv % 1000);
|
||||
printf("Current: %2d.%03d A\n", ma / 1000, ma % 1000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_info(struct cmd_tbl *cmdtp, int flag, int argc,
|
||||
char *const argv[])
|
||||
{
|
||||
if (!currdev) {
|
||||
printf("First, set the TCPM device!\n");
|
||||
return CMD_RET_USAGE;
|
||||
}
|
||||
|
||||
return do_print_info(currdev);
|
||||
}
|
||||
|
||||
static struct cmd_tbl subcmd[] = {
|
||||
U_BOOT_CMD_MKENT(dev, 2, 1, do_dev, "", ""),
|
||||
U_BOOT_CMD_MKENT(list, 1, 1, do_list, "", ""),
|
||||
U_BOOT_CMD_MKENT(info, 1, 1, do_info, "", ""),
|
||||
};
|
||||
|
||||
static int do_tcpm(struct cmd_tbl *cmdtp, int flag, int argc,
|
||||
char *const argv[])
|
||||
{
|
||||
struct cmd_tbl *cmd;
|
||||
|
||||
argc--;
|
||||
argv++;
|
||||
|
||||
cmd = find_cmd_tbl(argv[0], subcmd, ARRAY_SIZE(subcmd));
|
||||
if (!cmd || argc > cmd->maxargs)
|
||||
return CMD_RET_USAGE;
|
||||
|
||||
return cmd->cmd(cmdtp, flag, argc, argv);
|
||||
}
|
||||
|
||||
/**************************************************/
|
||||
|
||||
U_BOOT_CMD(tcpm, CONFIG_SYS_MAXARGS, 1, do_tcpm,
|
||||
"TCPM sub-system",
|
||||
"list - list TCPM devices\n"
|
||||
"tcpm dev [ID] - show or [set] operating TCPM device\n"
|
||||
"tcpm info - dump information\n"
|
||||
);
|
66
doc/usage/cmd/tcpm.rst
Normal file
66
doc/usage/cmd/tcpm.rst
Normal file
|
@ -0,0 +1,66 @@
|
|||
.. SPDX-License-Identifier: GPL-2.0+:
|
||||
|
||||
.. index::
|
||||
single: tcpm (command)
|
||||
|
||||
tcpm command
|
||||
============
|
||||
|
||||
Synopsis
|
||||
--------
|
||||
|
||||
::
|
||||
|
||||
tcpm dev [devnum]
|
||||
tcpm info
|
||||
tcpm list
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
The tcpm command is used to control USB-PD controllers, also known as TypeC Port Manager (TCPM).
|
||||
|
||||
The 'tcpm dev' command shows or set current TCPM device.
|
||||
|
||||
devnum
|
||||
device number to change
|
||||
|
||||
The 'tcpm info' command displays the current state of the device
|
||||
|
||||
The 'tcpm list' command displays the list available devices.
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
The 'tcpm info' command displays device's status:
|
||||
::
|
||||
|
||||
=> tcpm info
|
||||
Orientation: normal
|
||||
PD Revision: rev3
|
||||
Power Role: sink
|
||||
Data Role: device
|
||||
Voltage: 20.000 V
|
||||
Current: 2.250 A
|
||||
|
||||
The current device can be shown or set via 'tcpm dev' command:
|
||||
::
|
||||
|
||||
=> tcpm dev
|
||||
TCPM device is not set!
|
||||
=> tcpm dev 0
|
||||
dev: 0 @ usb-typec@22
|
||||
=> tcpm dev
|
||||
dev: 0 @ usb-typec@22
|
||||
|
||||
The list of available devices can be shown via 'tcpm list' command:
|
||||
::
|
||||
|
||||
=> tcpm list
|
||||
| ID | Name | Parent name | Parent uclass @ seq
|
||||
| 0 | usb-typec@22 | i2c@feac0000 | i2c @ 4 | status: 0
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
The tcpm command is only available if CONFIG_CMD_TCPM=y.
|
|
@ -112,6 +112,7 @@ Shell commands
|
|||
cmd/smbios
|
||||
cmd/sound
|
||||
cmd/source
|
||||
cmd/tcpm
|
||||
cmd/temperature
|
||||
cmd/tftpput
|
||||
cmd/trace
|
||||
|
|
|
@ -85,6 +85,8 @@ source "drivers/usb/emul/Kconfig"
|
|||
|
||||
source "drivers/usb/phy/Kconfig"
|
||||
|
||||
source "drivers/usb/tcpm/Kconfig"
|
||||
|
||||
source "drivers/usb/ulpi/Kconfig"
|
||||
|
||||
if USB_HOST
|
||||
|
|
8
drivers/usb/tcpm/Kconfig
Normal file
8
drivers/usb/tcpm/Kconfig
Normal file
|
@ -0,0 +1,8 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
config TYPEC_TCPM
|
||||
tristate "USB Type-C Port Controller Manager"
|
||||
depends on DM
|
||||
help
|
||||
The Type-C Port Controller Manager provides a USB PD and USB Type-C
|
||||
state machine for use with Type-C Port Controllers.
|
3
drivers/usb/tcpm/Makefile
Normal file
3
drivers/usb/tcpm/Makefile
Normal file
|
@ -0,0 +1,3 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-$(CONFIG_TYPEC_TCPM) += tcpm.o tcpm-uclass.o
|
173
drivers/usb/tcpm/tcpm-internal.h
Normal file
173
drivers/usb/tcpm/tcpm-internal.h
Normal file
|
@ -0,0 +1,173 @@
|
|||
/* 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
|
149
drivers/usb/tcpm/tcpm-uclass.c
Normal file
149
drivers/usb/tcpm/tcpm-uclass.c
Normal file
|
@ -0,0 +1,149 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2024 Collabora Ltd.
|
||||
*
|
||||
* USB Power Delivery protocol stack.
|
||||
*/
|
||||
|
||||
#include <dm/device.h>
|
||||
#include <dm/device_compat.h>
|
||||
#include <dm/uclass.h>
|
||||
#include <linux/err.h>
|
||||
#include <usb/tcpm.h>
|
||||
#include "tcpm-internal.h"
|
||||
|
||||
int tcpm_get_voltage(struct udevice *dev)
|
||||
{
|
||||
struct tcpm_port *port = dev_get_uclass_plat(dev);
|
||||
|
||||
return port->supply_voltage;
|
||||
}
|
||||
|
||||
int tcpm_get_current(struct udevice *dev)
|
||||
{
|
||||
struct tcpm_port *port = dev_get_uclass_plat(dev);
|
||||
|
||||
return port->current_limit;
|
||||
}
|
||||
|
||||
enum typec_orientation tcpm_get_orientation(struct udevice *dev)
|
||||
{
|
||||
struct tcpm_port *port = dev_get_uclass_plat(dev);
|
||||
|
||||
switch (port->polarity) {
|
||||
case TYPEC_POLARITY_CC1:
|
||||
return TYPEC_ORIENTATION_NORMAL;
|
||||
case TYPEC_POLARITY_CC2:
|
||||
return TYPEC_ORIENTATION_REVERSE;
|
||||
default:
|
||||
return TYPEC_ORIENTATION_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
const char *tcpm_get_state(struct udevice *dev)
|
||||
{
|
||||
struct tcpm_port *port = dev_get_uclass_plat(dev);
|
||||
|
||||
return tcpm_states[port->state];
|
||||
}
|
||||
|
||||
int tcpm_get_pd_rev(struct udevice *dev)
|
||||
{
|
||||
struct tcpm_port *port = dev_get_uclass_plat(dev);
|
||||
|
||||
return port->negotiated_rev;
|
||||
}
|
||||
|
||||
enum typec_role tcpm_get_pwr_role(struct udevice *dev)
|
||||
{
|
||||
struct tcpm_port *port = dev_get_uclass_plat(dev);
|
||||
|
||||
return port->pwr_role;
|
||||
}
|
||||
|
||||
enum typec_data_role tcpm_get_data_role(struct udevice *dev)
|
||||
{
|
||||
struct tcpm_port *port = dev_get_uclass_plat(dev);
|
||||
|
||||
return port->data_role;
|
||||
}
|
||||
|
||||
bool tcpm_is_connected(struct udevice *dev)
|
||||
{
|
||||
struct tcpm_port *port = dev_get_uclass_plat(dev);
|
||||
|
||||
return port->connected;
|
||||
}
|
||||
|
||||
int tcpm_get(int index, struct udevice **devp)
|
||||
{
|
||||
return uclass_get_device(UCLASS_TCPM, index, devp);
|
||||
}
|
||||
|
||||
static int tcpm_post_bind(struct udevice *dev)
|
||||
{
|
||||
const struct dm_tcpm_ops *drvops = dev_get_driver_ops(dev);
|
||||
const char *cap_str;
|
||||
ofnode node;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* USB Power Delivery (USB PD) specification requires, that communication
|
||||
* with a sink happens within roughly 5 seconds. Otherwise the source
|
||||
* might assume that the sink does not support USB PD. Starting to do
|
||||
* USB PD communication after that results in a hard reset, which briefly
|
||||
* removes any power from the USB-C port.
|
||||
*
|
||||
* On systems with alternative power supplies this is not an issue, but
|
||||
* systems, which get soleley powered through their USB-C port will end
|
||||
* up losing their power supply and doing a board level reset. The hard
|
||||
* reset will also restart the 5 second timeout. That means a operating
|
||||
* system initializing USB PD will put the system into a boot loop when
|
||||
* it takes more than 5 seconds from cold boot to the operating system
|
||||
* starting to transmit USB PD messages.
|
||||
*
|
||||
* The issue can be avoided by doing the initial USB PD communication
|
||||
* in U-Boot. The operating system can then re-negotiate by doing a
|
||||
* soft reset, which does not trigger removal of the supply voltage.
|
||||
*
|
||||
* Since the TCPM state machine is quite complex and depending on the
|
||||
* remote side can take quite some time to finish, this tries to limit
|
||||
* the automatic probing to systems probably relying on power being
|
||||
* provided by the USB-C port(s):
|
||||
*
|
||||
* 1. self-powered devices won't reset when the USB-C port looses power
|
||||
* 2. if the power is allowed to go into anything else than sink mode
|
||||
* it is not the only power source
|
||||
*/
|
||||
ret = drvops->get_connector_node(dev, &node);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (ofnode_read_bool(node, "self-powered"))
|
||||
return 0;
|
||||
|
||||
cap_str = ofnode_read_string(node, "power-role");
|
||||
if (!cap_str)
|
||||
return -EINVAL;
|
||||
|
||||
if (strcmp("sink", cap_str))
|
||||
return 0;
|
||||
|
||||
/* Do not auto-probe PD controller when PD is disabled */
|
||||
if (ofnode_read_bool(node, "pd-disable"))
|
||||
return 0;
|
||||
|
||||
dev_info(dev, "probing Type-C port manager...");
|
||||
|
||||
dev_or_flags(dev, DM_FLAG_PROBE_AFTER_BIND);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
UCLASS_DRIVER(tcpm) = {
|
||||
.id = UCLASS_TCPM,
|
||||
.name = "tcpm",
|
||||
.per_device_plat_auto = sizeof(struct tcpm_port),
|
||||
.post_bind = tcpm_post_bind,
|
||||
.post_probe = tcpm_post_probe,
|
||||
};
|
2288
drivers/usb/tcpm/tcpm.c
Normal file
2288
drivers/usb/tcpm/tcpm.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -139,6 +139,7 @@ enum uclass_id {
|
|||
UCLASS_SYSCON, /* System configuration device */
|
||||
UCLASS_SYSINFO, /* Device information from hardware */
|
||||
UCLASS_SYSRESET, /* System reset device */
|
||||
UCLASS_TCPM, /* TypeC port manager */
|
||||
UCLASS_TEE, /* Trusted Execution Environment device */
|
||||
UCLASS_THERMAL, /* Thermal sensor */
|
||||
UCLASS_TIMER, /* Timer device */
|
||||
|
|
516
include/usb/pd.h
Normal file
516
include/usb/pd.h
Normal file
|
@ -0,0 +1,516 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright 2015-2017 Google, Inc
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_USB_PD_H
|
||||
#define __LINUX_USB_PD_H
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
enum typec_port_type {
|
||||
TYPEC_PORT_SRC,
|
||||
TYPEC_PORT_SNK,
|
||||
TYPEC_PORT_DRP,
|
||||
};
|
||||
|
||||
enum typec_data_role {
|
||||
TYPEC_DEVICE,
|
||||
TYPEC_HOST,
|
||||
};
|
||||
|
||||
enum typec_role {
|
||||
TYPEC_SINK,
|
||||
TYPEC_SOURCE,
|
||||
};
|
||||
|
||||
/* USB PD Messages */
|
||||
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 */
|
||||
PD_CTRL_NOT_SUPP = 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 */
|
||||
};
|
||||
|
||||
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,
|
||||
PD_DATA_BATT_STATUS = 5,
|
||||
PD_DATA_ALERT = 6,
|
||||
PD_DATA_GET_COUNTRY_INFO = 7,
|
||||
PD_DATA_ENTER_USB = 8,
|
||||
/* 9-14 Reserved */
|
||||
PD_DATA_VENDOR_DEF = 15,
|
||||
/* 16-31 Reserved */
|
||||
};
|
||||
|
||||
enum pd_ext_msg_type {
|
||||
/* 0 Reserved */
|
||||
PD_EXT_SOURCE_CAP_EXT = 1,
|
||||
PD_EXT_STATUS = 2,
|
||||
PD_EXT_GET_BATT_CAP = 3,
|
||||
PD_EXT_GET_BATT_STATUS = 4,
|
||||
PD_EXT_BATT_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_FW_UPDATE_REQUEST = 10,
|
||||
PD_EXT_FW_UPDATE_RESPONSE = 11,
|
||||
PD_EXT_PPS_STATUS = 12,
|
||||
PD_EXT_COUNTRY_INFO = 13,
|
||||
PD_EXT_COUNTRY_CODES = 14,
|
||||
/* 15-31 Reserved */
|
||||
};
|
||||
|
||||
#define PD_REV10 0x0
|
||||
#define PD_REV20 0x1
|
||||
#define PD_REV30 0x2
|
||||
#define PD_MAX_REV PD_REV30
|
||||
|
||||
#define PD_HEADER_EXT_HDR BIT(15)
|
||||
#define PD_HEADER_CNT_SHIFT 12
|
||||
#define PD_HEADER_CNT_MASK 0x7
|
||||
#define PD_HEADER_ID_SHIFT 9
|
||||
#define PD_HEADER_ID_MASK 0x7
|
||||
#define PD_HEADER_PWR_ROLE BIT(8)
|
||||
#define PD_HEADER_REV_SHIFT 6
|
||||
#define PD_HEADER_REV_MASK 0x3
|
||||
#define PD_HEADER_DATA_ROLE BIT(5)
|
||||
#define PD_HEADER_TYPE_SHIFT 0
|
||||
#define PD_HEADER_TYPE_MASK 0x1f
|
||||
|
||||
#define PD_HEADER(type, pwr, data, rev, id, cnt, ext_hdr) \
|
||||
((((type) & PD_HEADER_TYPE_MASK) << PD_HEADER_TYPE_SHIFT) | \
|
||||
((pwr) == TYPEC_SOURCE ? PD_HEADER_PWR_ROLE : 0) | \
|
||||
((data) == TYPEC_HOST ? PD_HEADER_DATA_ROLE : 0) | \
|
||||
((rev) << PD_HEADER_REV_SHIFT) | \
|
||||
(((id) & PD_HEADER_ID_MASK) << PD_HEADER_ID_SHIFT) | \
|
||||
(((cnt) & PD_HEADER_CNT_MASK) << PD_HEADER_CNT_SHIFT) | \
|
||||
((ext_hdr) ? PD_HEADER_EXT_HDR : 0))
|
||||
|
||||
#define PD_HEADER_LE(type, pwr, data, rev, id, cnt) \
|
||||
cpu_to_le16(PD_HEADER((type), (pwr), (data), (rev), (id), (cnt), (0)))
|
||||
|
||||
static inline unsigned int pd_header_cnt(u16 header)
|
||||
{
|
||||
return (header >> PD_HEADER_CNT_SHIFT) & PD_HEADER_CNT_MASK;
|
||||
}
|
||||
|
||||
static inline unsigned int pd_header_cnt_le(__le16 header)
|
||||
{
|
||||
return pd_header_cnt(le16_to_cpu(header));
|
||||
}
|
||||
|
||||
static inline unsigned int pd_header_type(u16 header)
|
||||
{
|
||||
return (header >> PD_HEADER_TYPE_SHIFT) & PD_HEADER_TYPE_MASK;
|
||||
}
|
||||
|
||||
static inline unsigned int pd_header_type_le(__le16 header)
|
||||
{
|
||||
return pd_header_type(le16_to_cpu(header));
|
||||
}
|
||||
|
||||
static inline unsigned int pd_header_msgid(u16 header)
|
||||
{
|
||||
return (header >> PD_HEADER_ID_SHIFT) & PD_HEADER_ID_MASK;
|
||||
}
|
||||
|
||||
static inline unsigned int pd_header_msgid_le(__le16 header)
|
||||
{
|
||||
return pd_header_msgid(le16_to_cpu(header));
|
||||
}
|
||||
|
||||
static inline unsigned int pd_header_rev(u16 header)
|
||||
{
|
||||
return (header >> PD_HEADER_REV_SHIFT) & PD_HEADER_REV_MASK;
|
||||
}
|
||||
|
||||
static inline unsigned int pd_header_rev_le(__le16 header)
|
||||
{
|
||||
return pd_header_rev(le16_to_cpu(header));
|
||||
}
|
||||
|
||||
#define PD_EXT_HDR_CHUNKED BIT(15)
|
||||
#define PD_EXT_HDR_CHUNK_NUM_SHIFT 11
|
||||
#define PD_EXT_HDR_CHUNK_NUM_MASK 0xf
|
||||
#define PD_EXT_HDR_REQ_CHUNK BIT(10)
|
||||
#define PD_EXT_HDR_DATA_SIZE_SHIFT 0
|
||||
#define PD_EXT_HDR_DATA_SIZE_MASK 0x1ff
|
||||
|
||||
#define PD_EXT_HDR(data_size, req_chunk, chunk_num, chunked) \
|
||||
((((data_size) & PD_EXT_HDR_DATA_SIZE_MASK) << PD_EXT_HDR_DATA_SIZE_SHIFT) | \
|
||||
((req_chunk) ? PD_EXT_HDR_REQ_CHUNK : 0) | \
|
||||
(((chunk_num) & PD_EXT_HDR_CHUNK_NUM_MASK) << PD_EXT_HDR_CHUNK_NUM_SHIFT) | \
|
||||
((chunked) ? PD_EXT_HDR_CHUNKED : 0))
|
||||
|
||||
#define PD_EXT_HDR_LE(data_size, req_chunk, chunk_num, chunked) \
|
||||
cpu_to_le16(PD_EXT_HDR((data_size), (req_chunk), (chunk_num), (chunked)))
|
||||
|
||||
static inline unsigned int pd_ext_header_chunk_num(u16 ext_header)
|
||||
{
|
||||
return (ext_header >> PD_EXT_HDR_CHUNK_NUM_SHIFT) &
|
||||
PD_EXT_HDR_CHUNK_NUM_MASK;
|
||||
}
|
||||
|
||||
static inline unsigned int pd_ext_header_data_size(u16 ext_header)
|
||||
{
|
||||
return (ext_header >> PD_EXT_HDR_DATA_SIZE_SHIFT) &
|
||||
PD_EXT_HDR_DATA_SIZE_MASK;
|
||||
}
|
||||
|
||||
static inline unsigned int pd_ext_header_data_size_le(__le16 ext_header)
|
||||
{
|
||||
return pd_ext_header_data_size(le16_to_cpu(ext_header));
|
||||
}
|
||||
|
||||
#define PD_MAX_PAYLOAD 7
|
||||
#define PD_EXT_MAX_CHUNK_DATA 26
|
||||
|
||||
/*
|
||||
* struct pd_chunked_ext_message_data - PD chunked extended message data as
|
||||
* seen on wire
|
||||
* @header: PD extended message header
|
||||
* @data: PD extended message data
|
||||
*/
|
||||
struct pd_chunked_ext_message_data {
|
||||
__le16 header;
|
||||
u8 data[PD_EXT_MAX_CHUNK_DATA];
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* struct pd_message - PD message as seen on wire
|
||||
* @header: PD message header
|
||||
* @payload: PD message payload
|
||||
* @ext_msg: PD message chunked extended message data
|
||||
*/
|
||||
struct pd_message {
|
||||
__le16 header;
|
||||
union {
|
||||
__le32 payload[PD_MAX_PAYLOAD];
|
||||
struct pd_chunked_ext_message_data ext_msg;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
/* PDO: Power Data Object */
|
||||
#define PDO_MAX_OBJECTS 7
|
||||
|
||||
enum pd_pdo_type {
|
||||
PDO_TYPE_FIXED = 0,
|
||||
PDO_TYPE_BATT = 1,
|
||||
PDO_TYPE_VAR = 2,
|
||||
PDO_TYPE_APDO = 3,
|
||||
};
|
||||
|
||||
#define PDO_TYPE_SHIFT 30
|
||||
#define PDO_TYPE_MASK 0x3
|
||||
|
||||
#define PDO_TYPE(t) ((t) << PDO_TYPE_SHIFT)
|
||||
|
||||
#define PDO_VOLT_MASK 0x3ff
|
||||
#define PDO_CURR_MASK 0x3ff
|
||||
#define PDO_PWR_MASK 0x3ff
|
||||
|
||||
#define PDO_FIXED_DUAL_ROLE BIT(29) /* Power role swap supported */
|
||||
#define PDO_FIXED_SUSPEND BIT(28) /* USB Suspend supported (Source) */
|
||||
#define PDO_FIXED_HIGHER_CAP BIT(28) /* Requires more than vSafe5V (Sink) */
|
||||
#define PDO_FIXED_EXTPOWER BIT(27) /* Externally powered */
|
||||
#define PDO_FIXED_USB_COMM BIT(26) /* USB communications capable */
|
||||
#define PDO_FIXED_DATA_SWAP BIT(25) /* Data role swap supported */
|
||||
#define PDO_FIXED_UNCHUNK_EXT BIT(24) /* Unchunked Extended Message supported (Source) */
|
||||
#define PDO_FIXED_FRS_CURR_MASK (BIT(24) | BIT(23)) /* FR_Swap Current (Sink) */
|
||||
#define PDO_FIXED_FRS_CURR_SHIFT 23
|
||||
#define PDO_FIXED_VOLT_SHIFT 10 /* 50mV units */
|
||||
#define PDO_FIXED_CURR_SHIFT 0 /* 10mA units */
|
||||
|
||||
#define PDO_FIXED_VOLT(mv) ((((mv) / 50) & PDO_VOLT_MASK) << PDO_FIXED_VOLT_SHIFT)
|
||||
#define PDO_FIXED_CURR(ma) ((((ma) / 10) & PDO_CURR_MASK) << PDO_FIXED_CURR_SHIFT)
|
||||
|
||||
#define PDO_FIXED(mv, ma, flags) \
|
||||
(PDO_TYPE(PDO_TYPE_FIXED) | (flags) | \
|
||||
PDO_FIXED_VOLT(mv) | PDO_FIXED_CURR(ma))
|
||||
|
||||
#define VSAFE5V 5000 /* mv units */
|
||||
|
||||
#define PDO_BATT_MAX_VOLT_SHIFT 20 /* 50mV units */
|
||||
#define PDO_BATT_MIN_VOLT_SHIFT 10 /* 50mV units */
|
||||
#define PDO_BATT_MAX_PWR_SHIFT 0 /* 250mW units */
|
||||
|
||||
#define PDO_BATT_MIN_VOLT(mv) ((((mv) / 50) & PDO_VOLT_MASK) << PDO_BATT_MIN_VOLT_SHIFT)
|
||||
#define PDO_BATT_MAX_VOLT(mv) ((((mv) / 50) & PDO_VOLT_MASK) << PDO_BATT_MAX_VOLT_SHIFT)
|
||||
#define PDO_BATT_MAX_POWER(mw) ((((mw) / 250) & PDO_PWR_MASK) << PDO_BATT_MAX_PWR_SHIFT)
|
||||
|
||||
#define PDO_BATT(min_mv, max_mv, max_mw) \
|
||||
(PDO_TYPE(PDO_TYPE_BATT) | PDO_BATT_MIN_VOLT(min_mv) | \
|
||||
PDO_BATT_MAX_VOLT(max_mv) | PDO_BATT_MAX_POWER(max_mw))
|
||||
|
||||
#define PDO_VAR_MAX_VOLT_SHIFT 20 /* 50mV units */
|
||||
#define PDO_VAR_MIN_VOLT_SHIFT 10 /* 50mV units */
|
||||
#define PDO_VAR_MAX_CURR_SHIFT 0 /* 10mA units */
|
||||
|
||||
#define PDO_VAR_MIN_VOLT(mv) ((((mv) / 50) & PDO_VOLT_MASK) << PDO_VAR_MIN_VOLT_SHIFT)
|
||||
#define PDO_VAR_MAX_VOLT(mv) ((((mv) / 50) & PDO_VOLT_MASK) << PDO_VAR_MAX_VOLT_SHIFT)
|
||||
#define PDO_VAR_MAX_CURR(ma) ((((ma) / 10) & PDO_CURR_MASK) << PDO_VAR_MAX_CURR_SHIFT)
|
||||
|
||||
#define PDO_VAR(min_mv, max_mv, max_ma) \
|
||||
(PDO_TYPE(PDO_TYPE_VAR) | PDO_VAR_MIN_VOLT(min_mv) | \
|
||||
PDO_VAR_MAX_VOLT(max_mv) | PDO_VAR_MAX_CURR(max_ma))
|
||||
|
||||
enum pd_apdo_type {
|
||||
APDO_TYPE_PPS = 0,
|
||||
};
|
||||
|
||||
#define PDO_APDO_TYPE_SHIFT 28 /* Only valid value currently is 0x0 - PPS */
|
||||
#define PDO_APDO_TYPE_MASK 0x3
|
||||
|
||||
#define PDO_APDO_TYPE(t) ((t) << PDO_APDO_TYPE_SHIFT)
|
||||
|
||||
#define PDO_PPS_APDO_MAX_VOLT_SHIFT 17 /* 100mV units */
|
||||
#define PDO_PPS_APDO_MIN_VOLT_SHIFT 8 /* 100mV units */
|
||||
#define PDO_PPS_APDO_MAX_CURR_SHIFT 0 /* 50mA units */
|
||||
|
||||
#define PDO_PPS_APDO_VOLT_MASK 0xff
|
||||
#define PDO_PPS_APDO_CURR_MASK 0x7f
|
||||
|
||||
#define PDO_PPS_APDO_MIN_VOLT(mv) \
|
||||
((((mv) / 100) & PDO_PPS_APDO_VOLT_MASK) << PDO_PPS_APDO_MIN_VOLT_SHIFT)
|
||||
#define PDO_PPS_APDO_MAX_VOLT(mv) \
|
||||
((((mv) / 100) & PDO_PPS_APDO_VOLT_MASK) << PDO_PPS_APDO_MAX_VOLT_SHIFT)
|
||||
#define PDO_PPS_APDO_MAX_CURR(ma) \
|
||||
((((ma) / 50) & PDO_PPS_APDO_CURR_MASK) << PDO_PPS_APDO_MAX_CURR_SHIFT)
|
||||
|
||||
#define PDO_PPS_APDO(min_mv, max_mv, max_ma) \
|
||||
(PDO_TYPE(PDO_TYPE_APDO) | PDO_APDO_TYPE(APDO_TYPE_PPS) | \
|
||||
PDO_PPS_APDO_MIN_VOLT(min_mv) | PDO_PPS_APDO_MAX_VOLT(max_mv) | \
|
||||
PDO_PPS_APDO_MAX_CURR(max_ma))
|
||||
|
||||
static inline enum pd_pdo_type pdo_type(u32 pdo)
|
||||
{
|
||||
return (pdo >> PDO_TYPE_SHIFT) & PDO_TYPE_MASK;
|
||||
}
|
||||
|
||||
static inline unsigned int pdo_fixed_voltage(u32 pdo)
|
||||
{
|
||||
return ((pdo >> PDO_FIXED_VOLT_SHIFT) & PDO_VOLT_MASK) * 50;
|
||||
}
|
||||
|
||||
static inline unsigned int pdo_min_voltage(u32 pdo)
|
||||
{
|
||||
return ((pdo >> PDO_VAR_MIN_VOLT_SHIFT) & PDO_VOLT_MASK) * 50;
|
||||
}
|
||||
|
||||
static inline unsigned int pdo_max_voltage(u32 pdo)
|
||||
{
|
||||
return ((pdo >> PDO_VAR_MAX_VOLT_SHIFT) & PDO_VOLT_MASK) * 50;
|
||||
}
|
||||
|
||||
static inline unsigned int pdo_max_current(u32 pdo)
|
||||
{
|
||||
return ((pdo >> PDO_VAR_MAX_CURR_SHIFT) & PDO_CURR_MASK) * 10;
|
||||
}
|
||||
|
||||
static inline unsigned int pdo_max_power(u32 pdo)
|
||||
{
|
||||
return ((pdo >> PDO_BATT_MAX_PWR_SHIFT) & PDO_PWR_MASK) * 250;
|
||||
}
|
||||
|
||||
static inline enum pd_apdo_type pdo_apdo_type(u32 pdo)
|
||||
{
|
||||
return (pdo >> PDO_APDO_TYPE_SHIFT) & PDO_APDO_TYPE_MASK;
|
||||
}
|
||||
|
||||
static inline unsigned int pdo_pps_apdo_min_voltage(u32 pdo)
|
||||
{
|
||||
return ((pdo >> PDO_PPS_APDO_MIN_VOLT_SHIFT) &
|
||||
PDO_PPS_APDO_VOLT_MASK) * 100;
|
||||
}
|
||||
|
||||
static inline unsigned int pdo_pps_apdo_max_voltage(u32 pdo)
|
||||
{
|
||||
return ((pdo >> PDO_PPS_APDO_MAX_VOLT_SHIFT) &
|
||||
PDO_PPS_APDO_VOLT_MASK) * 100;
|
||||
}
|
||||
|
||||
static inline unsigned int pdo_pps_apdo_max_current(u32 pdo)
|
||||
{
|
||||
return ((pdo >> PDO_PPS_APDO_MAX_CURR_SHIFT) &
|
||||
PDO_PPS_APDO_CURR_MASK) * 50;
|
||||
}
|
||||
|
||||
/* RDO: Request Data Object */
|
||||
#define RDO_OBJ_POS_SHIFT 28
|
||||
#define RDO_OBJ_POS_MASK 0x7
|
||||
#define RDO_GIVE_BACK BIT(27) /* Supports reduced operating current */
|
||||
#define RDO_CAP_MISMATCH BIT(26) /* Not satisfied by source caps */
|
||||
#define RDO_USB_COMM BIT(25) /* USB communications capable */
|
||||
#define RDO_NO_SUSPEND BIT(24) /* USB Suspend not supported */
|
||||
|
||||
#define RDO_PWR_MASK 0x3ff
|
||||
#define RDO_CURR_MASK 0x3ff
|
||||
|
||||
#define RDO_FIXED_OP_CURR_SHIFT 10
|
||||
#define RDO_FIXED_MAX_CURR_SHIFT 0
|
||||
|
||||
#define RDO_OBJ(idx) (((idx) & RDO_OBJ_POS_MASK) << RDO_OBJ_POS_SHIFT)
|
||||
|
||||
#define PDO_FIXED_OP_CURR(ma) ((((ma) / 10) & RDO_CURR_MASK) << RDO_FIXED_OP_CURR_SHIFT)
|
||||
#define PDO_FIXED_MAX_CURR(ma) ((((ma) / 10) & RDO_CURR_MASK) << RDO_FIXED_MAX_CURR_SHIFT)
|
||||
|
||||
#define RDO_FIXED(idx, op_ma, max_ma, flags) \
|
||||
(RDO_OBJ(idx) | (flags) | \
|
||||
PDO_FIXED_OP_CURR(op_ma) | PDO_FIXED_MAX_CURR(max_ma))
|
||||
|
||||
#define RDO_BATT_OP_PWR_SHIFT 10 /* 250mW units */
|
||||
#define RDO_BATT_MAX_PWR_SHIFT 0 /* 250mW units */
|
||||
|
||||
#define RDO_BATT_OP_PWR(mw) ((((mw) / 250) & RDO_PWR_MASK) << RDO_BATT_OP_PWR_SHIFT)
|
||||
#define RDO_BATT_MAX_PWR(mw) ((((mw) / 250) & RDO_PWR_MASK) << RDO_BATT_MAX_PWR_SHIFT)
|
||||
|
||||
#define RDO_BATT(idx, op_mw, max_mw, flags) \
|
||||
(RDO_OBJ(idx) | (flags) | \
|
||||
RDO_BATT_OP_PWR(op_mw) | RDO_BATT_MAX_PWR(max_mw))
|
||||
|
||||
#define RDO_PROG_VOLT_MASK 0x7ff
|
||||
#define RDO_PROG_CURR_MASK 0x7f
|
||||
|
||||
#define RDO_PROG_VOLT_SHIFT 9
|
||||
#define RDO_PROG_CURR_SHIFT 0
|
||||
|
||||
#define RDO_PROG_VOLT_MV_STEP 20
|
||||
#define RDO_PROG_CURR_MA_STEP 50
|
||||
|
||||
#define PDO_PROG_OUT_VOLT(mv) \
|
||||
((((mv) / RDO_PROG_VOLT_MV_STEP) & RDO_PROG_VOLT_MASK) << RDO_PROG_VOLT_SHIFT)
|
||||
#define PDO_PROG_OP_CURR(ma) \
|
||||
((((ma) / RDO_PROG_CURR_MA_STEP) & RDO_PROG_CURR_MASK) << RDO_PROG_CURR_SHIFT)
|
||||
|
||||
#define RDO_PROG(idx, out_mv, op_ma, flags) \
|
||||
(RDO_OBJ(idx) | (flags) | \
|
||||
PDO_PROG_OUT_VOLT(out_mv) | PDO_PROG_OP_CURR(op_ma))
|
||||
|
||||
static inline unsigned int rdo_index(u32 rdo)
|
||||
{
|
||||
return (rdo >> RDO_OBJ_POS_SHIFT) & RDO_OBJ_POS_MASK;
|
||||
}
|
||||
|
||||
static inline unsigned int rdo_op_current(u32 rdo)
|
||||
{
|
||||
return ((rdo >> RDO_FIXED_OP_CURR_SHIFT) & RDO_CURR_MASK) * 10;
|
||||
}
|
||||
|
||||
static inline unsigned int rdo_max_current(u32 rdo)
|
||||
{
|
||||
return ((rdo >> RDO_FIXED_MAX_CURR_SHIFT) &
|
||||
RDO_CURR_MASK) * 10;
|
||||
}
|
||||
|
||||
static inline unsigned int rdo_op_power(u32 rdo)
|
||||
{
|
||||
return ((rdo >> RDO_BATT_OP_PWR_SHIFT) & RDO_PWR_MASK) * 250;
|
||||
}
|
||||
|
||||
static inline unsigned int rdo_max_power(u32 rdo)
|
||||
{
|
||||
return ((rdo >> RDO_BATT_MAX_PWR_SHIFT) & RDO_PWR_MASK) * 250;
|
||||
}
|
||||
|
||||
/* Enter_USB Data Object */
|
||||
#define EUDO_USB_MODE_MASK GENMASK(30, 28)
|
||||
#define EUDO_USB_MODE_SHIFT 28
|
||||
#define EUDO_USB_MODE_USB2 0
|
||||
#define EUDO_USB_MODE_USB3 1
|
||||
#define EUDO_USB_MODE_USB4 2
|
||||
#define EUDO_USB4_DRD BIT(26)
|
||||
#define EUDO_USB3_DRD BIT(25)
|
||||
#define EUDO_CABLE_SPEED_MASK GENMASK(23, 21)
|
||||
#define EUDO_CABLE_SPEED_SHIFT 21
|
||||
#define EUDO_CABLE_SPEED_USB2 0
|
||||
#define EUDO_CABLE_SPEED_USB3_GEN1 1
|
||||
#define EUDO_CABLE_SPEED_USB4_GEN2 2
|
||||
#define EUDO_CABLE_SPEED_USB4_GEN3 3
|
||||
#define EUDO_CABLE_TYPE_MASK GENMASK(20, 19)
|
||||
#define EUDO_CABLE_TYPE_SHIFT 19
|
||||
#define EUDO_CABLE_TYPE_PASSIVE 0
|
||||
#define EUDO_CABLE_TYPE_RE_TIMER 1
|
||||
#define EUDO_CABLE_TYPE_RE_DRIVER 2
|
||||
#define EUDO_CABLE_TYPE_OPTICAL 3
|
||||
#define EUDO_CABLE_CURRENT_MASK GENMASK(18, 17)
|
||||
#define EUDO_CABLE_CURRENT_SHIFT 17
|
||||
#define EUDO_CABLE_CURRENT_NOTSUPP 0
|
||||
#define EUDO_CABLE_CURRENT_3A 2
|
||||
#define EUDO_CABLE_CURRENT_5A 3
|
||||
#define EUDO_PCIE_SUPPORT BIT(16)
|
||||
#define EUDO_DP_SUPPORT BIT(15)
|
||||
#define EUDO_TBT_SUPPORT BIT(14)
|
||||
#define EUDO_HOST_PRESENT BIT(13)
|
||||
|
||||
/* USB PD timers and counters */
|
||||
#define PD_T_NO_RESPONSE 5000 /* 4.5 - 5.5 seconds */
|
||||
#define PD_T_DB_DETECT 10000 /* 10 - 15 seconds */
|
||||
#define PD_T_SEND_SOURCE_CAP 150 /* 100 - 200 ms */
|
||||
#define PD_T_SENDER_RESPONSE 60 /* 24 - 30 ms, relaxed */
|
||||
#define PD_T_RECEIVER_RESPONSE 15 /* 15ms max */
|
||||
#define PD_T_SOURCE_ACTIVITY 45
|
||||
#define PD_T_SINK_ACTIVITY 135
|
||||
#define PD_T_SINK_WAIT_CAP 310 /* 310 - 620 ms */
|
||||
#define PD_T_PS_TRANSITION 500
|
||||
#define PD_T_SRC_TRANSITION 35
|
||||
#define PD_T_DRP_SNK 40
|
||||
#define PD_T_DRP_SRC 30
|
||||
#define PD_T_PS_SOURCE_OFF 920
|
||||
#define PD_T_PS_SOURCE_ON 480
|
||||
#define PD_T_PS_SOURCE_ON_PRS 450 /* 390 - 480ms */
|
||||
#define PD_T_PS_HARD_RESET 30
|
||||
#define PD_T_SRC_RECOVER 760
|
||||
#define PD_T_SRC_RECOVER_MAX 1000
|
||||
#define PD_T_SRC_TURN_ON 275
|
||||
#define PD_T_SAFE_0V 650
|
||||
#define PD_T_VCONN_SOURCE_ON 100
|
||||
#define PD_T_SINK_REQUEST 100 /* 100 ms minimum */
|
||||
#define PD_T_ERROR_RECOVERY 100 /* minimum 25 is insufficient */
|
||||
#define PD_T_SRCSWAPSTDBY 625 /* Maximum of 650ms */
|
||||
#define PD_T_NEWSRC 250 /* Maximum of 275ms */
|
||||
#define PD_T_SWAP_SRC_START 20 /* Minimum of 20ms */
|
||||
#define PD_T_BIST_CONT_MODE 50 /* 30 - 60 ms */
|
||||
#define PD_T_SINK_TX 16 /* 16 - 20 ms */
|
||||
#define PD_T_CHUNK_NOT_SUPP 42 /* 40 - 50 ms */
|
||||
|
||||
#define PD_T_DRP_TRY 100 /* 75 - 150 ms */
|
||||
#define PD_T_DRP_TRYWAIT 600 /* 400 - 800 ms */
|
||||
|
||||
#define PD_T_CC_DEBOUNCE 200 /* 100 - 200 ms */
|
||||
#define PD_T_PD_DEBOUNCE 20 /* 10 - 20 ms */
|
||||
#define PD_T_TRY_CC_DEBOUNCE 15 /* 10 - 20 ms */
|
||||
|
||||
#define PD_N_CAPS_COUNT (PD_T_NO_RESPONSE / PD_T_SEND_SOURCE_CAP)
|
||||
#define PD_N_HARD_RESET_COUNT 1
|
||||
|
||||
#define PD_P_SNK_STDBY_MW 2500 /* 2500 mW */
|
||||
|
||||
/* Time to wait for TCPC to complete transmit */
|
||||
#define PD_T_TCPC_TX_TIMEOUT 100 /* in ms */
|
||||
|
||||
#endif /* __LINUX_USB_PD_H */
|
99
include/usb/tcpm.h
Normal file
99
include/usb/tcpm.h
Normal file
|
@ -0,0 +1,99 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright 2015-2017 Google, Inc
|
||||
* Copyright 2024 Collabora
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_USB_TCPM_H
|
||||
#define __LINUX_USB_TCPM_H
|
||||
|
||||
#include <dm/of.h>
|
||||
#include <linux/bitops.h>
|
||||
#include "pd.h"
|
||||
|
||||
enum typec_orientation {
|
||||
TYPEC_ORIENTATION_NONE,
|
||||
TYPEC_ORIENTATION_NORMAL,
|
||||
TYPEC_ORIENTATION_REVERSE,
|
||||
};
|
||||
|
||||
enum typec_cc_status {
|
||||
TYPEC_CC_OPEN,
|
||||
TYPEC_CC_RA,
|
||||
TYPEC_CC_RD,
|
||||
TYPEC_CC_RP_DEF,
|
||||
TYPEC_CC_RP_1_5,
|
||||
TYPEC_CC_RP_3_0,
|
||||
};
|
||||
|
||||
enum typec_cc_polarity {
|
||||
TYPEC_POLARITY_CC1,
|
||||
TYPEC_POLARITY_CC2,
|
||||
};
|
||||
|
||||
enum tcpm_transmit_status {
|
||||
TCPC_TX_SUCCESS = 0,
|
||||
TCPC_TX_DISCARDED = 1,
|
||||
TCPC_TX_FAILED = 2,
|
||||
};
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
struct dm_tcpm_ops {
|
||||
int (*get_connector_node)(struct udevice *dev, ofnode *connector_node);
|
||||
int (*init)(struct udevice *dev);
|
||||
int (*get_vbus)(struct udevice *dev);
|
||||
int (*set_cc)(struct udevice *dev, enum typec_cc_status cc);
|
||||
int (*get_cc)(struct udevice *dev, enum typec_cc_status *cc1,
|
||||
enum typec_cc_status *cc2);
|
||||
int (*set_polarity)(struct udevice *dev,
|
||||
enum typec_cc_polarity polarity);
|
||||
int (*set_vconn)(struct udevice *dev, bool on);
|
||||
int (*set_vbus)(struct udevice *dev, bool on, bool charge);
|
||||
int (*set_pd_rx)(struct udevice *dev, bool on);
|
||||
int (*set_roles)(struct udevice *dev, bool attached,
|
||||
enum typec_role role, enum typec_data_role data);
|
||||
int (*start_toggling)(struct udevice *dev,
|
||||
enum typec_port_type port_type,
|
||||
enum typec_cc_status cc);
|
||||
int (*pd_transmit)(struct udevice *dev, enum tcpm_transmit_type type,
|
||||
const struct pd_message *msg, unsigned int negotiated_rev);
|
||||
void (*poll_event)(struct udevice *dev);
|
||||
int (*enter_low_power_mode)(struct udevice *dev, bool attached, bool pd_capable);
|
||||
};
|
||||
|
||||
/* API for drivers */
|
||||
void tcpm_vbus_change(struct udevice *dev);
|
||||
void tcpm_cc_change(struct udevice *dev);
|
||||
void tcpm_pd_receive(struct udevice *dev, const struct pd_message *msg);
|
||||
void tcpm_pd_transmit_complete(struct udevice *dev,
|
||||
enum tcpm_transmit_status status);
|
||||
void tcpm_pd_hard_reset(struct udevice *dev);
|
||||
|
||||
/* API for boards */
|
||||
extern const char * const typec_pd_rev_name[];
|
||||
extern const char * const typec_orientation_name[];
|
||||
extern const char * const typec_role_name[];
|
||||
extern const char * const typec_data_role_name[];
|
||||
extern const char * const typec_cc_status_name[];
|
||||
|
||||
int tcpm_get(int index, struct udevice **devp);
|
||||
int tcpm_get_pd_rev(struct udevice *dev);
|
||||
int tcpm_get_current(struct udevice *dev);
|
||||
int tcpm_get_voltage(struct udevice *dev);
|
||||
enum typec_orientation tcpm_get_orientation(struct udevice *dev);
|
||||
enum typec_role tcpm_get_pwr_role(struct udevice *dev);
|
||||
enum typec_data_role tcpm_get_data_role(struct udevice *dev);
|
||||
bool tcpm_is_connected(struct udevice *dev);
|
||||
const char *tcpm_get_state(struct udevice *dev);
|
||||
|
||||
#endif /* __LINUX_USB_TCPM_H */
|
Loading…
Add table
Reference in a new issue