mirror of
https://github.com/armbian/linux-cix.git
synced 2026-01-06 12:30:45 -08:00
Merge tag 'thunderbolt-for-v6.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/westeri/thunderbolt into usb-next
Mika writes: thunderbolt: Changes for v6.3 merge window This includes following Thunderbolt/USB4 changes for the v6.3 merge window: - Add support for DisplayPort bandwidth allocation mode - Debug logging improvements - Minor cleanups. All these have been in linux-next with no reported issues. * tag 'thunderbolt-for-v6.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/westeri/thunderbolt: thunderbolt: Add missing kernel-doc comment to tb_tunnel_maximum_bandwidth() thunderbolt: Handle bandwidth allocation mode enablement notification thunderbolt: Add support for DisplayPort bandwidth allocation mode thunderbolt: Include the additional DP IN double word in debugfs dump thunderbolt: Add functions to support DisplayPort bandwidth allocation mode thunderbolt: Increase timeout of DP OUT adapter handshake thunderbolt: Take CL states into account when waiting for link to come up thunderbolt: Improve debug logging in tb_available_bandwidth() thunderbolt: Log DP adapter type thunderbolt: Use decimal port number in control and tunnel logs too thunderbolt: Refactor tb_acpi_add_link() thunderbolt: Use correct type in tb_port_is_clx_enabled() prototype
This commit is contained in:
@@ -36,16 +36,13 @@ static acpi_status tb_acpi_add_link(acpi_handle handle, u32 level, void *data,
|
||||
* We need to do this because the xHCI driver might not yet be
|
||||
* bound so the USB3 SuperSpeed ports are not yet created.
|
||||
*/
|
||||
dev = acpi_get_first_physical_node(adev);
|
||||
while (!dev) {
|
||||
adev = acpi_dev_parent(adev);
|
||||
if (!adev)
|
||||
break;
|
||||
do {
|
||||
dev = acpi_get_first_physical_node(adev);
|
||||
}
|
||||
if (dev)
|
||||
break;
|
||||
|
||||
if (!dev)
|
||||
goto out_put;
|
||||
adev = acpi_dev_parent(adev);
|
||||
} while (adev);
|
||||
|
||||
/*
|
||||
* Check that the device is PCIe. This is because USB3
|
||||
|
||||
@@ -230,7 +230,6 @@ static int check_config_address(struct tb_cfg_address addr,
|
||||
static struct tb_cfg_result decode_error(const struct ctl_pkg *response)
|
||||
{
|
||||
struct cfg_error_pkg *pkg = response->buffer;
|
||||
struct tb_ctl *ctl = response->ctl;
|
||||
struct tb_cfg_result res = { 0 };
|
||||
res.response_route = tb_cfg_get_route(&pkg->header);
|
||||
res.response_port = 0;
|
||||
@@ -239,13 +238,6 @@ static struct tb_cfg_result decode_error(const struct ctl_pkg *response)
|
||||
if (res.err)
|
||||
return res;
|
||||
|
||||
if (pkg->zero1)
|
||||
tb_ctl_warn(ctl, "pkg->zero1 is %#x\n", pkg->zero1);
|
||||
if (pkg->zero2)
|
||||
tb_ctl_warn(ctl, "pkg->zero2 is %#x\n", pkg->zero2);
|
||||
if (pkg->zero3)
|
||||
tb_ctl_warn(ctl, "pkg->zero3 is %#x\n", pkg->zero3);
|
||||
|
||||
res.err = 1;
|
||||
res.tb_error = pkg->error;
|
||||
res.response_port = pkg->port;
|
||||
@@ -416,6 +408,7 @@ static int tb_async_error(const struct ctl_pkg *pkg)
|
||||
case TB_CFG_ERROR_LINK_ERROR:
|
||||
case TB_CFG_ERROR_HEC_ERROR_DETECTED:
|
||||
case TB_CFG_ERROR_FLOW_CONTROL_ERROR:
|
||||
case TB_CFG_ERROR_DP_BW:
|
||||
return true;
|
||||
|
||||
default:
|
||||
@@ -735,6 +728,47 @@ void tb_ctl_stop(struct tb_ctl *ctl)
|
||||
|
||||
/* public interface, commands */
|
||||
|
||||
/**
|
||||
* tb_cfg_ack_notification() - Ack notification
|
||||
* @ctl: Control channel to use
|
||||
* @route: Router that originated the event
|
||||
* @error: Pointer to the notification package
|
||||
*
|
||||
* Call this as response for non-plug notification to ack it. Returns
|
||||
* %0 on success or an error code on failure.
|
||||
*/
|
||||
int tb_cfg_ack_notification(struct tb_ctl *ctl, u64 route,
|
||||
const struct cfg_error_pkg *error)
|
||||
{
|
||||
struct cfg_ack_pkg pkg = {
|
||||
.header = tb_cfg_make_header(route),
|
||||
};
|
||||
const char *name;
|
||||
|
||||
switch (error->error) {
|
||||
case TB_CFG_ERROR_LINK_ERROR:
|
||||
name = "link error";
|
||||
break;
|
||||
case TB_CFG_ERROR_HEC_ERROR_DETECTED:
|
||||
name = "HEC error";
|
||||
break;
|
||||
case TB_CFG_ERROR_FLOW_CONTROL_ERROR:
|
||||
name = "flow control error";
|
||||
break;
|
||||
case TB_CFG_ERROR_DP_BW:
|
||||
name = "DP_BW";
|
||||
break;
|
||||
default:
|
||||
name = "unknown";
|
||||
break;
|
||||
}
|
||||
|
||||
tb_ctl_dbg(ctl, "acking %s (%#x) notification on %llx\n", name,
|
||||
error->error, route);
|
||||
|
||||
return tb_ctl_tx(ctl, &pkg, sizeof(pkg), TB_CFG_PKG_NOTIFY_ACK);
|
||||
}
|
||||
|
||||
/**
|
||||
* tb_cfg_ack_plug() - Ack hot plug/unplug event
|
||||
* @ctl: Control channel to use
|
||||
@@ -754,7 +788,7 @@ int tb_cfg_ack_plug(struct tb_ctl *ctl, u64 route, u32 port, bool unplug)
|
||||
.pg = unplug ? TB_CFG_ERROR_PG_HOT_UNPLUG
|
||||
: TB_CFG_ERROR_PG_HOT_PLUG,
|
||||
};
|
||||
tb_ctl_dbg(ctl, "acking hot %splug event on %llx:%x\n",
|
||||
tb_ctl_dbg(ctl, "acking hot %splug event on %llx:%u\n",
|
||||
unplug ? "un" : "", route, port);
|
||||
return tb_ctl_tx(ctl, &pkg, sizeof(pkg), TB_CFG_PKG_ERROR);
|
||||
}
|
||||
|
||||
@@ -122,6 +122,8 @@ static inline struct tb_cfg_header tb_cfg_make_header(u64 route)
|
||||
return header;
|
||||
}
|
||||
|
||||
int tb_cfg_ack_notification(struct tb_ctl *ctl, u64 route,
|
||||
const struct cfg_error_pkg *error);
|
||||
int tb_cfg_ack_plug(struct tb_ctl *ctl, u64 route, u32 port, bool unplug);
|
||||
struct tb_cfg_result tb_cfg_reset(struct tb_ctl *ctl, u64 route);
|
||||
struct tb_cfg_result tb_cfg_read_raw(struct tb_ctl *ctl, void *buffer,
|
||||
|
||||
@@ -1159,7 +1159,10 @@ static void port_cap_show(struct tb_port *port, struct seq_file *s,
|
||||
if (tb_port_is_pcie_down(port) || tb_port_is_pcie_up(port)) {
|
||||
length = PORT_CAP_PCIE_LEN;
|
||||
} else if (tb_port_is_dpin(port) || tb_port_is_dpout(port)) {
|
||||
length = PORT_CAP_DP_LEN;
|
||||
if (usb4_dp_port_bw_mode_supported(port))
|
||||
length = PORT_CAP_DP_LEN + 1;
|
||||
else
|
||||
length = PORT_CAP_DP_LEN;
|
||||
} else if (tb_port_is_usb3_down(port) ||
|
||||
tb_port_is_usb3_up(port)) {
|
||||
length = PORT_CAP_USB3_LEN;
|
||||
|
||||
@@ -513,36 +513,44 @@ int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged)
|
||||
|
||||
while (retries--) {
|
||||
state = tb_port_state(port);
|
||||
if (state < 0)
|
||||
return state;
|
||||
if (state == TB_PORT_DISABLED) {
|
||||
switch (state) {
|
||||
case TB_PORT_DISABLED:
|
||||
tb_port_dbg(port, "is disabled (state: 0)\n");
|
||||
return 0;
|
||||
}
|
||||
if (state == TB_PORT_UNPLUGGED) {
|
||||
|
||||
case TB_PORT_UNPLUGGED:
|
||||
if (wait_if_unplugged) {
|
||||
/* used during resume */
|
||||
tb_port_dbg(port,
|
||||
"is unplugged (state: 7), retrying...\n");
|
||||
msleep(100);
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
tb_port_dbg(port, "is unplugged (state: 7)\n");
|
||||
return 0;
|
||||
}
|
||||
if (state == TB_PORT_UP) {
|
||||
tb_port_dbg(port, "is connected, link is up (state: 2)\n");
|
||||
|
||||
case TB_PORT_UP:
|
||||
case TB_PORT_TX_CL0S:
|
||||
case TB_PORT_RX_CL0S:
|
||||
case TB_PORT_CL1:
|
||||
case TB_PORT_CL2:
|
||||
tb_port_dbg(port, "is connected, link is up (state: %d)\n", state);
|
||||
return 1;
|
||||
|
||||
default:
|
||||
if (state < 0)
|
||||
return state;
|
||||
|
||||
/*
|
||||
* After plug-in the state is TB_PORT_CONNECTING. Give it some
|
||||
* time.
|
||||
*/
|
||||
tb_port_dbg(port,
|
||||
"is connected, link is not up (state: %d), retrying...\n",
|
||||
state);
|
||||
msleep(100);
|
||||
}
|
||||
|
||||
/*
|
||||
* After plug-in the state is TB_PORT_CONNECTING. Give it some
|
||||
* time.
|
||||
*/
|
||||
tb_port_dbg(port,
|
||||
"is connected, link is not up (state: %d), retrying...\n",
|
||||
state);
|
||||
msleep(100);
|
||||
}
|
||||
tb_port_warn(port,
|
||||
"failed to reach state TB_PORT_UP. Ignoring port...\n");
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -223,6 +223,23 @@ struct tb_switch {
|
||||
enum tb_clx clx;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct tb_bandwidth_group - Bandwidth management group
|
||||
* @tb: Pointer to the domain the group belongs to
|
||||
* @index: Index of the group (aka Group_ID). Valid values %1-%7
|
||||
* @ports: DP IN adapters belonging to this group are linked here
|
||||
*
|
||||
* Any tunnel that requires isochronous bandwidth (that's DP for now) is
|
||||
* attached to a bandwidth group. All tunnels going through the same
|
||||
* USB4 links share the same group and can dynamically distribute the
|
||||
* bandwidth within the group.
|
||||
*/
|
||||
struct tb_bandwidth_group {
|
||||
struct tb *tb;
|
||||
int index;
|
||||
struct list_head ports;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct tb_port - a thunderbolt port, part of a tb_switch
|
||||
* @config: Cached port configuration read from registers
|
||||
@@ -247,6 +264,9 @@ struct tb_switch {
|
||||
* @ctl_credits: Buffers reserved for control path
|
||||
* @dma_credits: Number of credits allocated for DMA tunneling for all
|
||||
* DMA paths through this port.
|
||||
* @group: Bandwidth allocation group the adapter is assigned to. Only
|
||||
* used for DP IN adapters for now.
|
||||
* @group_list: The adapter is linked to the group's list of ports through this
|
||||
*
|
||||
* In USB4 terminology this structure represents an adapter (protocol or
|
||||
* lane adapter).
|
||||
@@ -272,6 +292,8 @@ struct tb_port {
|
||||
unsigned int total_credits;
|
||||
unsigned int ctl_credits;
|
||||
unsigned int dma_credits;
|
||||
struct tb_bandwidth_group *group;
|
||||
struct list_head group_list;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1047,7 +1069,7 @@ void tb_port_lane_bonding_disable(struct tb_port *port);
|
||||
int tb_port_wait_for_link_width(struct tb_port *port, int width,
|
||||
int timeout_msec);
|
||||
int tb_port_update_credits(struct tb_port *port);
|
||||
bool tb_port_is_clx_enabled(struct tb_port *port, enum tb_clx clx);
|
||||
bool tb_port_is_clx_enabled(struct tb_port *port, unsigned int clx);
|
||||
|
||||
int tb_switch_find_vse_cap(struct tb_switch *sw, enum tb_switch_vse_cap vsec);
|
||||
int tb_switch_find_cap(struct tb_switch *sw, enum tb_switch_cap cap);
|
||||
@@ -1238,6 +1260,21 @@ int usb4_usb3_port_allocate_bandwidth(struct tb_port *port, int *upstream_bw,
|
||||
int usb4_usb3_port_release_bandwidth(struct tb_port *port, int *upstream_bw,
|
||||
int *downstream_bw);
|
||||
|
||||
int usb4_dp_port_set_cm_id(struct tb_port *port, int cm_id);
|
||||
bool usb4_dp_port_bw_mode_supported(struct tb_port *port);
|
||||
bool usb4_dp_port_bw_mode_enabled(struct tb_port *port);
|
||||
int usb4_dp_port_set_cm_bw_mode_supported(struct tb_port *port, bool supported);
|
||||
int usb4_dp_port_group_id(struct tb_port *port);
|
||||
int usb4_dp_port_set_group_id(struct tb_port *port, int group_id);
|
||||
int usb4_dp_port_nrd(struct tb_port *port, int *rate, int *lanes);
|
||||
int usb4_dp_port_set_nrd(struct tb_port *port, int rate, int lanes);
|
||||
int usb4_dp_port_granularity(struct tb_port *port);
|
||||
int usb4_dp_port_set_granularity(struct tb_port *port, int granularity);
|
||||
int usb4_dp_port_set_estimated_bw(struct tb_port *port, int bw);
|
||||
int usb4_dp_port_allocated_bw(struct tb_port *port);
|
||||
int usb4_dp_port_allocate_bw(struct tb_port *port, int bw);
|
||||
int usb4_dp_port_requested_bw(struct tb_port *port);
|
||||
|
||||
static inline bool tb_is_usb4_port_device(const struct device *dev)
|
||||
{
|
||||
return dev->type == &usb4_port_device_type;
|
||||
|
||||
@@ -29,6 +29,7 @@ enum tb_cfg_error {
|
||||
TB_CFG_ERROR_HEC_ERROR_DETECTED = 12,
|
||||
TB_CFG_ERROR_FLOW_CONTROL_ERROR = 13,
|
||||
TB_CFG_ERROR_LOCK = 15,
|
||||
TB_CFG_ERROR_DP_BW = 32,
|
||||
};
|
||||
|
||||
/* common header */
|
||||
@@ -64,14 +65,16 @@ struct cfg_write_pkg {
|
||||
/* TB_CFG_PKG_ERROR */
|
||||
struct cfg_error_pkg {
|
||||
struct tb_cfg_header header;
|
||||
enum tb_cfg_error error:4;
|
||||
u32 zero1:4;
|
||||
enum tb_cfg_error error:8;
|
||||
u32 port:6;
|
||||
u32 zero2:2; /* Both should be zero, still they are different fields. */
|
||||
u32 zero3:14;
|
||||
u32 reserved:16;
|
||||
u32 pg:2;
|
||||
} __packed;
|
||||
|
||||
struct cfg_ack_pkg {
|
||||
struct tb_cfg_header header;
|
||||
};
|
||||
|
||||
#define TB_CFG_ERROR_PG_HOT_PLUG 0x2
|
||||
#define TB_CFG_ERROR_PG_HOT_UNPLUG 0x3
|
||||
|
||||
|
||||
@@ -50,6 +50,10 @@ enum tb_port_state {
|
||||
TB_PORT_DISABLED = 0, /* tb_cap_phy.disable == 1 */
|
||||
TB_PORT_CONNECTING = 1, /* retry */
|
||||
TB_PORT_UP = 2,
|
||||
TB_PORT_TX_CL0S = 3,
|
||||
TB_PORT_RX_CL0S = 4,
|
||||
TB_PORT_CL1 = 5,
|
||||
TB_PORT_CL2 = 6,
|
||||
TB_PORT_UNPLUGGED = 7,
|
||||
};
|
||||
|
||||
@@ -381,15 +385,42 @@ struct tb_regs_port_header {
|
||||
#define ADP_DP_CS_1_AUX_RX_HOPID_MASK GENMASK(21, 11)
|
||||
#define ADP_DP_CS_1_AUX_RX_HOPID_SHIFT 11
|
||||
#define ADP_DP_CS_2 0x02
|
||||
#define ADP_DP_CS_2_NRD_MLC_MASK GENMASK(2, 0)
|
||||
#define ADP_DP_CS_2_HDP BIT(6)
|
||||
#define ADP_DP_CS_2_NRD_MLR_MASK GENMASK(9, 7)
|
||||
#define ADP_DP_CS_2_NRD_MLR_SHIFT 7
|
||||
#define ADP_DP_CS_2_CA BIT(10)
|
||||
#define ADP_DP_CS_2_GR_MASK GENMASK(12, 11)
|
||||
#define ADP_DP_CS_2_GR_SHIFT 11
|
||||
#define ADP_DP_CS_2_GR_0_25G 0x0
|
||||
#define ADP_DP_CS_2_GR_0_5G 0x1
|
||||
#define ADP_DP_CS_2_GR_1G 0x2
|
||||
#define ADP_DP_CS_2_GROUP_ID_MASK GENMASK(15, 13)
|
||||
#define ADP_DP_CS_2_GROUP_ID_SHIFT 13
|
||||
#define ADP_DP_CS_2_CM_ID_MASK GENMASK(19, 16)
|
||||
#define ADP_DP_CS_2_CM_ID_SHIFT 16
|
||||
#define ADP_DP_CS_2_CMMS BIT(20)
|
||||
#define ADP_DP_CS_2_ESTIMATED_BW_MASK GENMASK(31, 24)
|
||||
#define ADP_DP_CS_2_ESTIMATED_BW_SHIFT 24
|
||||
#define ADP_DP_CS_3 0x03
|
||||
#define ADP_DP_CS_3_HDPC BIT(9)
|
||||
#define DP_LOCAL_CAP 0x04
|
||||
#define DP_REMOTE_CAP 0x05
|
||||
/* For DP IN adapter */
|
||||
#define DP_STATUS 0x06
|
||||
#define DP_STATUS_ALLOCATED_BW_MASK GENMASK(31, 24)
|
||||
#define DP_STATUS_ALLOCATED_BW_SHIFT 24
|
||||
/* For DP OUT adapter */
|
||||
#define DP_STATUS_CTRL 0x06
|
||||
#define DP_STATUS_CTRL_CMHS BIT(25)
|
||||
#define DP_STATUS_CTRL_UF BIT(26)
|
||||
#define DP_COMMON_CAP 0x07
|
||||
/* Only if DP IN supports BW allocation mode */
|
||||
#define ADP_DP_CS_8 0x08
|
||||
#define ADP_DP_CS_8_REQUESTED_BW_MASK GENMASK(7, 0)
|
||||
#define ADP_DP_CS_8_DPME BIT(30)
|
||||
#define ADP_DP_CS_8_DR BIT(31)
|
||||
|
||||
/*
|
||||
* DP_COMMON_CAP offsets work also for DP_LOCAL_CAP and DP_REMOTE_CAP
|
||||
* with exception of DPRX done.
|
||||
@@ -406,7 +437,12 @@ struct tb_regs_port_header {
|
||||
#define DP_COMMON_CAP_2_LANES 0x1
|
||||
#define DP_COMMON_CAP_4_LANES 0x2
|
||||
#define DP_COMMON_CAP_LTTPR_NS BIT(27)
|
||||
#define DP_COMMON_CAP_BW_MODE BIT(28)
|
||||
#define DP_COMMON_CAP_DPRX_DONE BIT(31)
|
||||
/* Only present if DP IN supports BW allocation mode */
|
||||
#define ADP_DP_CS_8 0x08
|
||||
#define ADP_DP_CS_8_DPME BIT(30)
|
||||
#define ADP_DP_CS_8_DR BIT(31)
|
||||
|
||||
/* PCIe adapter registers */
|
||||
#define ADP_PCIE_CS_0 0x00
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -29,6 +29,9 @@ enum tb_tunnel_type {
|
||||
* @init: Optional tunnel specific initialization
|
||||
* @deinit: Optional tunnel specific de-initialization
|
||||
* @activate: Optional tunnel specific activation/deactivation
|
||||
* @maximum_bandwidth: Returns maximum possible bandwidth for this tunnel
|
||||
* @allocated_bandwidth: Return how much bandwidth is allocated for the tunnel
|
||||
* @alloc_bandwidth: Change tunnel bandwidth allocation
|
||||
* @consumed_bandwidth: Return how much bandwidth the tunnel consumes
|
||||
* @release_unused_bandwidth: Release all unused bandwidth
|
||||
* @reclaim_available_bandwidth: Reclaim back available bandwidth
|
||||
@@ -40,6 +43,8 @@ enum tb_tunnel_type {
|
||||
* Only set if the bandwidth needs to be limited.
|
||||
* @allocated_up: Allocated upstream bandwidth (only for USB3)
|
||||
* @allocated_down: Allocated downstream bandwidth (only for USB3)
|
||||
* @bw_mode: DP bandwidth allocation mode registers can be used to
|
||||
* determine consumed and allocated bandwidth
|
||||
*/
|
||||
struct tb_tunnel {
|
||||
struct tb *tb;
|
||||
@@ -50,6 +55,12 @@ struct tb_tunnel {
|
||||
int (*init)(struct tb_tunnel *tunnel);
|
||||
void (*deinit)(struct tb_tunnel *tunnel);
|
||||
int (*activate)(struct tb_tunnel *tunnel, bool activate);
|
||||
int (*maximum_bandwidth)(struct tb_tunnel *tunnel, int *max_up,
|
||||
int *max_down);
|
||||
int (*allocated_bandwidth)(struct tb_tunnel *tunnel, int *allocated_up,
|
||||
int *allocated_down);
|
||||
int (*alloc_bandwidth)(struct tb_tunnel *tunnel, int *alloc_up,
|
||||
int *alloc_down);
|
||||
int (*consumed_bandwidth)(struct tb_tunnel *tunnel, int *consumed_up,
|
||||
int *consumed_down);
|
||||
int (*release_unused_bandwidth)(struct tb_tunnel *tunnel);
|
||||
@@ -62,6 +73,7 @@ struct tb_tunnel {
|
||||
int max_down;
|
||||
int allocated_up;
|
||||
int allocated_down;
|
||||
bool bw_mode;
|
||||
};
|
||||
|
||||
struct tb_tunnel *tb_tunnel_discover_pci(struct tb *tb, struct tb_port *down,
|
||||
@@ -92,6 +104,12 @@ void tb_tunnel_deactivate(struct tb_tunnel *tunnel);
|
||||
bool tb_tunnel_is_invalid(struct tb_tunnel *tunnel);
|
||||
bool tb_tunnel_port_on_path(const struct tb_tunnel *tunnel,
|
||||
const struct tb_port *port);
|
||||
int tb_tunnel_maximum_bandwidth(struct tb_tunnel *tunnel, int *max_up,
|
||||
int *max_down);
|
||||
int tb_tunnel_allocated_bandwidth(struct tb_tunnel *tunnel, int *allocated_up,
|
||||
int *allocated_down);
|
||||
int tb_tunnel_alloc_bandwidth(struct tb_tunnel *tunnel, int *alloc_up,
|
||||
int *alloc_down);
|
||||
int tb_tunnel_consumed_bandwidth(struct tb_tunnel *tunnel, int *consumed_up,
|
||||
int *consumed_down);
|
||||
int tb_tunnel_release_unused_bandwidth(struct tb_tunnel *tunnel);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user