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:
Greg Kroah-Hartman
2023-02-08 12:49:26 +01:00
12 changed files with 1710 additions and 94 deletions

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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,

View File

@@ -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;

View File

@@ -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

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -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