mirror of
https://github.com/torvalds/linux.git
synced 2024-11-22 20:22:09 +00:00
thunderbolt: Changes for v6.7 merge window
This includes following USB4/Thunderbolt changes for the v6.7 merge window: - Configure asymmetric link if the DisplayPort bandwidth requires so - Enable path power management packet support for USB4 v2 routers - Make the bandwidth reservations to follow the USB4 v2 connection manager guide suggestions - DisplayPort tunneling improvements - Small cleanups and improvements around the driver. All these have been in linux-next with no reported issues. -----BEGIN PGP SIGNATURE----- iQJUBAABCgA+FiEEVTdhRGBbNzLrSUBaAP2fSd+ZWKAFAmU6IT8gHG1pa2Eud2Vz dGVyYmVyZ0BsaW51eC5pbnRlbC5jb20ACgkQAP2fSd+ZWKCP4A//YPiWn0pzbvwB cYtkViTHuimKCG++NLWk2kOJG79AfdPfbophwdgBEs55FPb9oWtWtwyWyrGWGCRR n6Elts87EfjcFdkVq08tgtPSrqkJSwYEIhm/UHtOrSaw5VJR6Snk5/oHzUttX1W6 zL7Ii+I+qMjj4Tg6la9EtYV1UrgXy9iCU1IPaKf2KwPZyt8eTly13iEzQhoKzPq2 pkckjEN8oyqJZFKh/bB0MNezVmCSqtw0X5LWOWDRB3jG9i2e1aaTcCO7Y9amC1Qu mLWWOA/U10RFwjocZ+85iPurbymZMBEqvqHY1MJ1m+lQM/W5hjZbvYub/HPSKsHN 8DwaV/Ty/Dixn5/Epdt1JJtWl53kP/ofmSdRxUFHTDhbb3Jx1QyQdKolV37Z7juZ 66bFfQ7WDgDiEV7nHXR15uBtp2nvww1BpE6ACyB7BIHBziOaY961bEHDWzhwQmXe BhwIBYziEXLxlY4W0/1dFil1OJ4H4c9SA79VR9gSbBwFn+w0FWWthOkvXUl+OXoD fCpsiMVm9ZXlJ8SXjXmnqxNEZMSd0Jg+wWTJnjcBmsawPz6Ab56gxZBKTt8CJIL4 rtFbbQw7dNFS9dSmLlycojoRKyFhrdrZmdyf+7yFH+XD9Xmus0FPCLu2Fsbw6Ifk Jxhi+ZGUPEklMNrIQg0EdG9s+j5oZDk= =PZEP -----END PGP SIGNATURE----- Merge tag 'thunderbolt-for-v6.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/westeri/thunderbolt into usb-next Mika writes: thunderbolt: Changes for v6.7 merge window This includes following USB4/Thunderbolt changes for the v6.7 merge window: - Configure asymmetric link if the DisplayPort bandwidth requires so - Enable path power management packet support for USB4 v2 routers - Make the bandwidth reservations to follow the USB4 v2 connection manager guide suggestions - DisplayPort tunneling improvements - Small cleanups and improvements around the driver. All these have been in linux-next with no reported issues. * tag 'thunderbolt-for-v6.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/westeri/thunderbolt: (25 commits) thunderbolt: Fix one kernel-doc comment thunderbolt: Configure asymmetric link if needed and bandwidth allows thunderbolt: Add support for asymmetric link thunderbolt: Introduce tb_switch_depth() thunderbolt: Introduce tb_for_each_upstream_port_on_path() thunderbolt: Introduce tb_port_path_direction_downstream() thunderbolt: Set path power management packet support bit for USB4 v2 routers thunderbolt: Change bandwidth reservations to comply USB4 v2 thunderbolt: Make is_gen4_link() available to the rest of the driver thunderbolt: Use weight constants in tb_usb3_consumed_bandwidth() thunderbolt: Use constants for path weight and priority thunderbolt: Add DP IN added last in the head of the list of DP resources thunderbolt: Create multiple DisplayPort tunnels if there are more DP IN/OUT pairs thunderbolt: Log NVM version of routers and retimers thunderbolt: Use tb_tunnel_xxx() log macros in tb.c thunderbolt: Expose tb_tunnel_xxx() log macros to the rest of the driver thunderbolt: Use tb_tunnel_dbg() where possible to make logging more consistent thunderbolt: Fix typo of HPD bit for Hot Plug Detect thunderbolt: Fix typo in enum tb_link_width kernel-doc thunderbolt: Fix debug log when DisplayPort adapter not available for pairing ...
This commit is contained in:
commit
9b6db9a3a6
@ -174,6 +174,28 @@ bool tb_port_clx_is_enabled(struct tb_port *port, unsigned int clx)
|
||||
return !!(tb_port_clx(port) & clx);
|
||||
}
|
||||
|
||||
/**
|
||||
* tb_switch_clx_is_supported() - Is CLx supported on this type of router
|
||||
* @sw: The router to check CLx support for
|
||||
*/
|
||||
static bool tb_switch_clx_is_supported(const struct tb_switch *sw)
|
||||
{
|
||||
if (!clx_enabled)
|
||||
return false;
|
||||
|
||||
if (sw->quirks & QUIRK_NO_CLX)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* CLx is not enabled and validated on Intel USB4 platforms
|
||||
* before Alder Lake.
|
||||
*/
|
||||
if (tb_switch_is_tiger_lake(sw))
|
||||
return false;
|
||||
|
||||
return tb_switch_is_usb4(sw) || tb_switch_is_titan_ridge(sw);
|
||||
}
|
||||
|
||||
/**
|
||||
* tb_switch_clx_init() - Initialize router CL states
|
||||
* @sw: Router
|
||||
@ -273,28 +295,6 @@ static int tb_switch_mask_clx_objections(struct tb_switch *sw)
|
||||
sw->cap_lp + offset, ARRAY_SIZE(val));
|
||||
}
|
||||
|
||||
/**
|
||||
* tb_switch_clx_is_supported() - Is CLx supported on this type of router
|
||||
* @sw: The router to check CLx support for
|
||||
*/
|
||||
bool tb_switch_clx_is_supported(const struct tb_switch *sw)
|
||||
{
|
||||
if (!clx_enabled)
|
||||
return false;
|
||||
|
||||
if (sw->quirks & QUIRK_NO_CLX)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* CLx is not enabled and validated on Intel USB4 platforms
|
||||
* before Alder Lake.
|
||||
*/
|
||||
if (tb_switch_is_tiger_lake(sw))
|
||||
return false;
|
||||
|
||||
return tb_switch_is_usb4(sw) || tb_switch_is_titan_ridge(sw);
|
||||
}
|
||||
|
||||
static bool validate_mask(unsigned int clx)
|
||||
{
|
||||
/* Previous states need to be enabled */
|
||||
@ -405,6 +405,9 @@ int tb_switch_clx_disable(struct tb_switch *sw)
|
||||
if (!clx)
|
||||
return 0;
|
||||
|
||||
if (sw->is_unplugged)
|
||||
return clx;
|
||||
|
||||
up = tb_upstream_port(sw);
|
||||
down = tb_switch_downstream_port(sw);
|
||||
|
||||
|
@ -101,7 +101,7 @@ struct dma_test {
|
||||
unsigned int packets_sent;
|
||||
unsigned int packets_received;
|
||||
unsigned int link_speed;
|
||||
unsigned int link_width;
|
||||
enum tb_link_width link_width;
|
||||
unsigned int crc_errors;
|
||||
unsigned int buffer_overflow_errors;
|
||||
enum dma_test_result result;
|
||||
@ -465,9 +465,9 @@ DMA_TEST_DEBUGFS_ATTR(packets_to_send, packets_to_send_get,
|
||||
static int dma_test_set_bonding(struct dma_test *dt)
|
||||
{
|
||||
switch (dt->link_width) {
|
||||
case 2:
|
||||
case TB_LINK_WIDTH_DUAL:
|
||||
return tb_xdomain_lane_bonding_enable(dt->xd);
|
||||
case 1:
|
||||
case TB_LINK_WIDTH_SINGLE:
|
||||
tb_xdomain_lane_bonding_disable(dt->xd);
|
||||
fallthrough;
|
||||
default:
|
||||
@ -490,12 +490,8 @@ static void dma_test_check_errors(struct dma_test *dt, int ret)
|
||||
if (!dt->error_code) {
|
||||
if (dt->link_speed && dt->xd->link_speed != dt->link_speed) {
|
||||
dt->error_code = DMA_TEST_SPEED_ERROR;
|
||||
} else if (dt->link_width) {
|
||||
const struct tb_xdomain *xd = dt->xd;
|
||||
|
||||
if ((dt->link_width == 1 && xd->link_width != TB_LINK_WIDTH_SINGLE) ||
|
||||
(dt->link_width == 2 && xd->link_width < TB_LINK_WIDTH_DUAL))
|
||||
dt->error_code = DMA_TEST_WIDTH_ERROR;
|
||||
} else if (dt->link_width && dt->link_width != dt->xd->link_width) {
|
||||
dt->error_code = DMA_TEST_WIDTH_ERROR;
|
||||
} else if (dt->packets_to_send != dt->packets_sent ||
|
||||
dt->packets_to_receive != dt->packets_received ||
|
||||
dt->crc_errors || dt->buffer_overflow_errors) {
|
||||
|
@ -19,9 +19,9 @@ static void tb_dump_hop(const struct tb_path_hop *hop, const struct tb_regs_hop
|
||||
|
||||
tb_port_dbg(port, " In HopID: %d => Out port: %d Out HopID: %d\n",
|
||||
hop->in_hop_index, regs->out_port, regs->next_hop);
|
||||
tb_port_dbg(port, " Weight: %d Priority: %d Credits: %d Drop: %d\n",
|
||||
regs->weight, regs->priority,
|
||||
regs->initial_credits, regs->drop_packages);
|
||||
tb_port_dbg(port, " Weight: %d Priority: %d Credits: %d Drop: %d PM: %d\n",
|
||||
regs->weight, regs->priority, regs->initial_credits,
|
||||
regs->drop_packages, regs->pmps);
|
||||
tb_port_dbg(port, " Counter enabled: %d Counter index: %d\n",
|
||||
regs->counter_enable, regs->counter);
|
||||
tb_port_dbg(port, " Flow Control (In/Eg): %d/%d Shared Buffer (In/Eg): %d/%d\n",
|
||||
@ -535,6 +535,7 @@ int tb_path_activate(struct tb_path *path)
|
||||
hop.next_hop = path->hops[i].next_hop_index;
|
||||
hop.out_port = path->hops[i].out_port->port;
|
||||
hop.initial_credits = path->hops[i].initial_credits;
|
||||
hop.pmps = path->hops[i].pm_support;
|
||||
hop.unknown1 = 0;
|
||||
hop.enable = 1;
|
||||
|
||||
|
@ -31,6 +31,9 @@ static void quirk_usb3_maximum_bandwidth(struct tb_switch *sw)
|
||||
{
|
||||
struct tb_port *port;
|
||||
|
||||
if (tb_switch_is_icm(sw))
|
||||
return;
|
||||
|
||||
tb_switch_for_each_port(sw, port) {
|
||||
if (!tb_port_is_usb3_down(port))
|
||||
continue;
|
||||
|
@ -94,6 +94,7 @@ static int tb_retimer_nvm_add(struct tb_retimer *rt)
|
||||
goto err_nvm;
|
||||
|
||||
rt->nvm = nvm;
|
||||
dev_dbg(&rt->dev, "NVM version %x.%x\n", nvm->major, nvm->minor);
|
||||
return 0;
|
||||
|
||||
err_nvm:
|
||||
|
@ -372,6 +372,7 @@ static int tb_switch_nvm_add(struct tb_switch *sw)
|
||||
ret = tb_nvm_add_active(nvm, nvm_read);
|
||||
if (ret)
|
||||
goto err_nvm;
|
||||
tb_sw_dbg(sw, "NVM version %x.%x\n", nvm->major, nvm->minor);
|
||||
}
|
||||
|
||||
if (!sw->no_nvm_upgrade) {
|
||||
@ -914,6 +915,48 @@ int tb_port_get_link_speed(struct tb_port *port)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* tb_port_get_link_generation() - Returns link generation
|
||||
* @port: Lane adapter
|
||||
*
|
||||
* Returns link generation as number or negative errno in case of
|
||||
* failure. Does not distinguish between Thunderbolt 1 and Thunderbolt 2
|
||||
* links so for those always returns 2.
|
||||
*/
|
||||
int tb_port_get_link_generation(struct tb_port *port)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = tb_port_get_link_speed(port);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
switch (ret) {
|
||||
case 40:
|
||||
return 4;
|
||||
case 20:
|
||||
return 3;
|
||||
default:
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *width_name(enum tb_link_width width)
|
||||
{
|
||||
switch (width) {
|
||||
case TB_LINK_WIDTH_SINGLE:
|
||||
return "symmetric, single lane";
|
||||
case TB_LINK_WIDTH_DUAL:
|
||||
return "symmetric, dual lanes";
|
||||
case TB_LINK_WIDTH_ASYM_TX:
|
||||
return "asymmetric, 3 transmitters, 1 receiver";
|
||||
case TB_LINK_WIDTH_ASYM_RX:
|
||||
return "asymmetric, 3 receivers, 1 transmitter";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* tb_port_get_link_width() - Get current link width
|
||||
* @port: Port to check (USB4 or CIO)
|
||||
@ -939,8 +982,15 @@ int tb_port_get_link_width(struct tb_port *port)
|
||||
LANE_ADP_CS_1_CURRENT_WIDTH_SHIFT;
|
||||
}
|
||||
|
||||
static bool tb_port_is_width_supported(struct tb_port *port,
|
||||
unsigned int width_mask)
|
||||
/**
|
||||
* tb_port_width_supported() - Is the given link width supported
|
||||
* @port: Port to check
|
||||
* @width: Widths to check (bitmask)
|
||||
*
|
||||
* Can be called to any lane adapter. Checks if given @width is
|
||||
* supported by the hardware and returns %true if it is.
|
||||
*/
|
||||
bool tb_port_width_supported(struct tb_port *port, unsigned int width)
|
||||
{
|
||||
u32 phy, widths;
|
||||
int ret;
|
||||
@ -948,20 +998,23 @@ static bool tb_port_is_width_supported(struct tb_port *port,
|
||||
if (!port->cap_phy)
|
||||
return false;
|
||||
|
||||
if (width & (TB_LINK_WIDTH_ASYM_TX | TB_LINK_WIDTH_ASYM_RX)) {
|
||||
if (tb_port_get_link_generation(port) < 4 ||
|
||||
!usb4_port_asym_supported(port))
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = tb_port_read(port, &phy, TB_CFG_PORT,
|
||||
port->cap_phy + LANE_ADP_CS_0, 1);
|
||||
if (ret)
|
||||
return false;
|
||||
|
||||
widths = (phy & LANE_ADP_CS_0_SUPPORTED_WIDTH_MASK) >>
|
||||
LANE_ADP_CS_0_SUPPORTED_WIDTH_SHIFT;
|
||||
|
||||
return widths & width_mask;
|
||||
}
|
||||
|
||||
static bool is_gen4_link(struct tb_port *port)
|
||||
{
|
||||
return tb_port_get_link_speed(port) > 20;
|
||||
/*
|
||||
* The field encoding is the same as &enum tb_link_width (which is
|
||||
* passed to @width).
|
||||
*/
|
||||
widths = FIELD_GET(LANE_ADP_CS_0_SUPPORTED_WIDTH_MASK, phy);
|
||||
return widths & width;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -991,15 +1044,23 @@ int tb_port_set_link_width(struct tb_port *port, enum tb_link_width width)
|
||||
switch (width) {
|
||||
case TB_LINK_WIDTH_SINGLE:
|
||||
/* Gen 4 link cannot be single */
|
||||
if (is_gen4_link(port))
|
||||
if (tb_port_get_link_generation(port) >= 4)
|
||||
return -EOPNOTSUPP;
|
||||
val |= LANE_ADP_CS_1_TARGET_WIDTH_SINGLE <<
|
||||
LANE_ADP_CS_1_TARGET_WIDTH_SHIFT;
|
||||
break;
|
||||
|
||||
case TB_LINK_WIDTH_DUAL:
|
||||
if (tb_port_get_link_generation(port) >= 4)
|
||||
return usb4_port_asym_set_link_width(port, width);
|
||||
val |= LANE_ADP_CS_1_TARGET_WIDTH_DUAL <<
|
||||
LANE_ADP_CS_1_TARGET_WIDTH_SHIFT;
|
||||
break;
|
||||
|
||||
case TB_LINK_WIDTH_ASYM_TX:
|
||||
case TB_LINK_WIDTH_ASYM_RX:
|
||||
return usb4_port_asym_set_link_width(port, width);
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -1124,7 +1185,7 @@ void tb_port_lane_bonding_disable(struct tb_port *port)
|
||||
/**
|
||||
* tb_port_wait_for_link_width() - Wait until link reaches specific width
|
||||
* @port: Port to wait for
|
||||
* @width_mask: Expected link width mask
|
||||
* @width: Expected link width (bitmask)
|
||||
* @timeout_msec: Timeout in ms how long to wait
|
||||
*
|
||||
* Should be used after both ends of the link have been bonded (or
|
||||
@ -1133,14 +1194,15 @@ void tb_port_lane_bonding_disable(struct tb_port *port)
|
||||
* within the given timeout, %0 if it did. Can be passed a mask of
|
||||
* expected widths and succeeds if any of the widths is reached.
|
||||
*/
|
||||
int tb_port_wait_for_link_width(struct tb_port *port, unsigned int width_mask,
|
||||
int tb_port_wait_for_link_width(struct tb_port *port, unsigned int width,
|
||||
int timeout_msec)
|
||||
{
|
||||
ktime_t timeout = ktime_add_ms(ktime_get(), timeout_msec);
|
||||
int ret;
|
||||
|
||||
/* Gen 4 link does not support single lane */
|
||||
if ((width_mask & TB_LINK_WIDTH_SINGLE) && is_gen4_link(port))
|
||||
if ((width & TB_LINK_WIDTH_SINGLE) &&
|
||||
tb_port_get_link_generation(port) >= 4)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
do {
|
||||
@ -1153,7 +1215,7 @@ int tb_port_wait_for_link_width(struct tb_port *port, unsigned int width_mask,
|
||||
*/
|
||||
if (ret != -EACCES)
|
||||
return ret;
|
||||
} else if (ret & width_mask) {
|
||||
} else if (ret & width) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1332,7 +1394,7 @@ int tb_pci_port_enable(struct tb_port *port, bool enable)
|
||||
* tb_dp_port_hpd_is_active() - Is HPD already active
|
||||
* @port: DP out port to check
|
||||
*
|
||||
* Checks if the DP OUT adapter port has HDP bit already set.
|
||||
* Checks if the DP OUT adapter port has HPD bit already set.
|
||||
*/
|
||||
int tb_dp_port_hpd_is_active(struct tb_port *port)
|
||||
{
|
||||
@ -1344,14 +1406,14 @@ int tb_dp_port_hpd_is_active(struct tb_port *port)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return !!(data & ADP_DP_CS_2_HDP);
|
||||
return !!(data & ADP_DP_CS_2_HPD);
|
||||
}
|
||||
|
||||
/**
|
||||
* tb_dp_port_hpd_clear() - Clear HPD from DP IN port
|
||||
* @port: Port to clear HPD
|
||||
*
|
||||
* If the DP IN port has HDP set, this function can be used to clear it.
|
||||
* If the DP IN port has HPD set, this function can be used to clear it.
|
||||
*/
|
||||
int tb_dp_port_hpd_clear(struct tb_port *port)
|
||||
{
|
||||
@ -1363,7 +1425,7 @@ int tb_dp_port_hpd_clear(struct tb_port *port)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
data |= ADP_DP_CS_3_HDPC;
|
||||
data |= ADP_DP_CS_3_HPDC;
|
||||
return tb_port_write(port, &data, TB_CFG_PORT,
|
||||
port->cap_adap + ADP_DP_CS_3, 1);
|
||||
}
|
||||
@ -2697,6 +2759,38 @@ static int tb_switch_update_link_attributes(struct tb_switch *sw)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Must be called after tb_switch_update_link_attributes() */
|
||||
static void tb_switch_link_init(struct tb_switch *sw)
|
||||
{
|
||||
struct tb_port *up, *down;
|
||||
bool bonded;
|
||||
|
||||
if (!tb_route(sw) || tb_switch_is_icm(sw))
|
||||
return;
|
||||
|
||||
tb_sw_dbg(sw, "current link speed %u.0 Gb/s\n", sw->link_speed);
|
||||
tb_sw_dbg(sw, "current link width %s\n", width_name(sw->link_width));
|
||||
|
||||
bonded = sw->link_width >= TB_LINK_WIDTH_DUAL;
|
||||
|
||||
/*
|
||||
* Gen 4 links come up as bonded so update the port structures
|
||||
* accordingly.
|
||||
*/
|
||||
up = tb_upstream_port(sw);
|
||||
down = tb_switch_downstream_port(sw);
|
||||
|
||||
up->bonded = bonded;
|
||||
if (up->dual_link_port)
|
||||
up->dual_link_port->bonded = bonded;
|
||||
tb_port_update_credits(up);
|
||||
|
||||
down->bonded = bonded;
|
||||
if (down->dual_link_port)
|
||||
down->dual_link_port->bonded = bonded;
|
||||
tb_port_update_credits(down);
|
||||
}
|
||||
|
||||
/**
|
||||
* tb_switch_lane_bonding_enable() - Enable lane bonding
|
||||
* @sw: Switch to enable lane bonding
|
||||
@ -2705,24 +2799,20 @@ static int tb_switch_update_link_attributes(struct tb_switch *sw)
|
||||
* switch. If conditions are correct and both switches support the feature,
|
||||
* lanes are bonded. It is safe to call this to any switch.
|
||||
*/
|
||||
int tb_switch_lane_bonding_enable(struct tb_switch *sw)
|
||||
static int tb_switch_lane_bonding_enable(struct tb_switch *sw)
|
||||
{
|
||||
struct tb_port *up, *down;
|
||||
u64 route = tb_route(sw);
|
||||
unsigned int width_mask;
|
||||
unsigned int width;
|
||||
int ret;
|
||||
|
||||
if (!route)
|
||||
return 0;
|
||||
|
||||
if (!tb_switch_lane_bonding_possible(sw))
|
||||
return 0;
|
||||
|
||||
up = tb_upstream_port(sw);
|
||||
down = tb_switch_downstream_port(sw);
|
||||
|
||||
if (!tb_port_is_width_supported(up, TB_LINK_WIDTH_DUAL) ||
|
||||
!tb_port_is_width_supported(down, TB_LINK_WIDTH_DUAL))
|
||||
if (!tb_port_width_supported(up, TB_LINK_WIDTH_DUAL) ||
|
||||
!tb_port_width_supported(down, TB_LINK_WIDTH_DUAL))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
@ -2746,21 +2836,10 @@ int tb_switch_lane_bonding_enable(struct tb_switch *sw)
|
||||
}
|
||||
|
||||
/* Any of the widths are all bonded */
|
||||
width_mask = TB_LINK_WIDTH_DUAL | TB_LINK_WIDTH_ASYM_TX |
|
||||
TB_LINK_WIDTH_ASYM_RX;
|
||||
width = TB_LINK_WIDTH_DUAL | TB_LINK_WIDTH_ASYM_TX |
|
||||
TB_LINK_WIDTH_ASYM_RX;
|
||||
|
||||
ret = tb_port_wait_for_link_width(down, width_mask, 100);
|
||||
if (ret) {
|
||||
tb_port_warn(down, "timeout enabling lane bonding\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
tb_port_update_credits(down);
|
||||
tb_port_update_credits(up);
|
||||
tb_switch_update_link_attributes(sw);
|
||||
|
||||
tb_sw_dbg(sw, "lane bonding enabled\n");
|
||||
return ret;
|
||||
return tb_port_wait_for_link_width(down, width, 100);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2770,20 +2849,27 @@ int tb_switch_lane_bonding_enable(struct tb_switch *sw)
|
||||
* Disables lane bonding between @sw and parent. This can be called even
|
||||
* if lanes were not bonded originally.
|
||||
*/
|
||||
void tb_switch_lane_bonding_disable(struct tb_switch *sw)
|
||||
static int tb_switch_lane_bonding_disable(struct tb_switch *sw)
|
||||
{
|
||||
struct tb_port *up, *down;
|
||||
int ret;
|
||||
|
||||
if (!tb_route(sw))
|
||||
return;
|
||||
|
||||
up = tb_upstream_port(sw);
|
||||
if (!up->bonded)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* If the link is Gen 4 there is no way to switch the link to
|
||||
* two single lane links so avoid that here. Also don't bother
|
||||
* if the link is not up anymore (sw is unplugged).
|
||||
*/
|
||||
ret = tb_port_get_link_generation(up);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret >= 4)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
down = tb_switch_downstream_port(sw);
|
||||
|
||||
tb_port_lane_bonding_disable(up);
|
||||
tb_port_lane_bonding_disable(down);
|
||||
|
||||
@ -2791,15 +2877,160 @@ void tb_switch_lane_bonding_disable(struct tb_switch *sw)
|
||||
* It is fine if we get other errors as the router might have
|
||||
* been unplugged.
|
||||
*/
|
||||
ret = tb_port_wait_for_link_width(down, TB_LINK_WIDTH_SINGLE, 100);
|
||||
if (ret == -ETIMEDOUT)
|
||||
tb_sw_warn(sw, "timeout disabling lane bonding\n");
|
||||
return tb_port_wait_for_link_width(down, TB_LINK_WIDTH_SINGLE, 100);
|
||||
}
|
||||
|
||||
static int tb_switch_asym_enable(struct tb_switch *sw, enum tb_link_width width)
|
||||
{
|
||||
struct tb_port *up, *down, *port;
|
||||
enum tb_link_width down_width;
|
||||
int ret;
|
||||
|
||||
up = tb_upstream_port(sw);
|
||||
down = tb_switch_downstream_port(sw);
|
||||
|
||||
if (width == TB_LINK_WIDTH_ASYM_TX) {
|
||||
down_width = TB_LINK_WIDTH_ASYM_RX;
|
||||
port = down;
|
||||
} else {
|
||||
down_width = TB_LINK_WIDTH_ASYM_TX;
|
||||
port = up;
|
||||
}
|
||||
|
||||
ret = tb_port_set_link_width(up, width);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = tb_port_set_link_width(down, down_width);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Initiate the change in the router that one of its TX lanes is
|
||||
* changing to RX but do so only if there is an actual change.
|
||||
*/
|
||||
if (sw->link_width != width) {
|
||||
ret = usb4_port_asym_start(port);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = tb_port_wait_for_link_width(up, width, 100);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
sw->link_width = width;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tb_switch_asym_disable(struct tb_switch *sw)
|
||||
{
|
||||
struct tb_port *up, *down;
|
||||
int ret;
|
||||
|
||||
up = tb_upstream_port(sw);
|
||||
down = tb_switch_downstream_port(sw);
|
||||
|
||||
ret = tb_port_set_link_width(up, TB_LINK_WIDTH_DUAL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = tb_port_set_link_width(down, TB_LINK_WIDTH_DUAL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Initiate the change in the router that has three TX lanes and
|
||||
* is changing one of its TX lanes to RX but only if there is a
|
||||
* change in the link width.
|
||||
*/
|
||||
if (sw->link_width > TB_LINK_WIDTH_DUAL) {
|
||||
if (sw->link_width == TB_LINK_WIDTH_ASYM_TX)
|
||||
ret = usb4_port_asym_start(up);
|
||||
else
|
||||
ret = usb4_port_asym_start(down);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = tb_port_wait_for_link_width(up, TB_LINK_WIDTH_DUAL, 100);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
sw->link_width = TB_LINK_WIDTH_DUAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* tb_switch_set_link_width() - Configure router link width
|
||||
* @sw: Router to configure
|
||||
* @width: The new link width
|
||||
*
|
||||
* Set device router link width to @width from router upstream port
|
||||
* perspective. Supports also asymmetric links if the routers boths side
|
||||
* of the link supports it.
|
||||
*
|
||||
* Does nothing for host router.
|
||||
*
|
||||
* Returns %0 in case of success, negative errno otherwise.
|
||||
*/
|
||||
int tb_switch_set_link_width(struct tb_switch *sw, enum tb_link_width width)
|
||||
{
|
||||
struct tb_port *up, *down;
|
||||
int ret = 0;
|
||||
|
||||
if (!tb_route(sw))
|
||||
return 0;
|
||||
|
||||
up = tb_upstream_port(sw);
|
||||
down = tb_switch_downstream_port(sw);
|
||||
|
||||
switch (width) {
|
||||
case TB_LINK_WIDTH_SINGLE:
|
||||
ret = tb_switch_lane_bonding_disable(sw);
|
||||
break;
|
||||
|
||||
case TB_LINK_WIDTH_DUAL:
|
||||
if (sw->link_width == TB_LINK_WIDTH_ASYM_TX ||
|
||||
sw->link_width == TB_LINK_WIDTH_ASYM_RX) {
|
||||
ret = tb_switch_asym_disable(sw);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
ret = tb_switch_lane_bonding_enable(sw);
|
||||
break;
|
||||
|
||||
case TB_LINK_WIDTH_ASYM_TX:
|
||||
case TB_LINK_WIDTH_ASYM_RX:
|
||||
ret = tb_switch_asym_enable(sw, width);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (ret) {
|
||||
case 0:
|
||||
break;
|
||||
|
||||
case -ETIMEDOUT:
|
||||
tb_sw_warn(sw, "timeout changing link width\n");
|
||||
return ret;
|
||||
|
||||
case -ENOTCONN:
|
||||
case -EOPNOTSUPP:
|
||||
case -ENODEV:
|
||||
return ret;
|
||||
|
||||
default:
|
||||
tb_sw_dbg(sw, "failed to change link width: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
tb_port_update_credits(down);
|
||||
tb_port_update_credits(up);
|
||||
|
||||
tb_switch_update_link_attributes(sw);
|
||||
|
||||
tb_sw_dbg(sw, "lane bonding disabled\n");
|
||||
tb_sw_dbg(sw, "link width set to %s\n", width_name(width));
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2959,6 +3190,8 @@ int tb_switch_add(struct tb_switch *sw)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
tb_switch_link_init(sw);
|
||||
|
||||
ret = tb_switch_clx_init(sw);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -162,11 +162,6 @@ struct tb_switch_tmu {
|
||||
* switches) you need to have domain lock held.
|
||||
*
|
||||
* In USB4 terminology this structure represents a router.
|
||||
*
|
||||
* Note @link_width is not the same as whether link is bonded or not.
|
||||
* For Gen 4 links the link is also bonded when it is asymmetric. The
|
||||
* correct way to find out whether the link is bonded or not is to look
|
||||
* @bonded field of the upstream port.
|
||||
*/
|
||||
struct tb_switch {
|
||||
struct device dev;
|
||||
@ -348,6 +343,7 @@ struct tb_retimer {
|
||||
* the path
|
||||
* @nfc_credits: Number of non-flow controlled buffers allocated for the
|
||||
* @in_port.
|
||||
* @pm_support: Set path PM packet support bit to 1 (for USB4 v2 routers)
|
||||
*
|
||||
* Hop configuration is always done on the IN port of a switch.
|
||||
* in_port and out_port have to be on the same switch. Packets arriving on
|
||||
@ -368,6 +364,7 @@ struct tb_path_hop {
|
||||
int next_hop_index;
|
||||
unsigned int initial_credits;
|
||||
unsigned int nfc_credits;
|
||||
bool pm_support;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -864,6 +861,15 @@ static inline struct tb_port *tb_switch_downstream_port(struct tb_switch *sw)
|
||||
return tb_port_at(tb_route(sw), tb_switch_parent(sw));
|
||||
}
|
||||
|
||||
/**
|
||||
* tb_switch_depth() - Returns depth of the connected router
|
||||
* @sw: Router
|
||||
*/
|
||||
static inline int tb_switch_depth(const struct tb_switch *sw)
|
||||
{
|
||||
return sw->config.depth;
|
||||
}
|
||||
|
||||
static inline bool tb_switch_is_light_ridge(const struct tb_switch *sw)
|
||||
{
|
||||
return sw->config.vendor_id == PCI_VENDOR_ID_INTEL &&
|
||||
@ -956,8 +962,7 @@ static inline bool tb_switch_is_icm(const struct tb_switch *sw)
|
||||
return !sw->config.enabled;
|
||||
}
|
||||
|
||||
int tb_switch_lane_bonding_enable(struct tb_switch *sw);
|
||||
void tb_switch_lane_bonding_disable(struct tb_switch *sw);
|
||||
int tb_switch_set_link_width(struct tb_switch *sw, enum tb_link_width width);
|
||||
int tb_switch_configure_link(struct tb_switch *sw);
|
||||
void tb_switch_unconfigure_link(struct tb_switch *sw);
|
||||
|
||||
@ -1001,7 +1006,6 @@ static inline bool tb_switch_tmu_is_enabled(const struct tb_switch *sw)
|
||||
bool tb_port_clx_is_enabled(struct tb_port *port, unsigned int clx);
|
||||
|
||||
int tb_switch_clx_init(struct tb_switch *sw);
|
||||
bool tb_switch_clx_is_supported(const struct tb_switch *sw);
|
||||
int tb_switch_clx_enable(struct tb_switch *sw, unsigned int clx);
|
||||
int tb_switch_clx_disable(struct tb_switch *sw);
|
||||
|
||||
@ -1040,6 +1044,21 @@ void tb_port_release_out_hopid(struct tb_port *port, int hopid);
|
||||
struct tb_port *tb_next_port_on_path(struct tb_port *start, struct tb_port *end,
|
||||
struct tb_port *prev);
|
||||
|
||||
/**
|
||||
* tb_port_path_direction_downstream() - Checks if path directed downstream
|
||||
* @src: Source adapter
|
||||
* @dst: Destination adapter
|
||||
*
|
||||
* Returns %true only if the specified path from source adapter (@src)
|
||||
* to destination adapter (@dst) is directed downstream.
|
||||
*/
|
||||
static inline bool
|
||||
tb_port_path_direction_downstream(const struct tb_port *src,
|
||||
const struct tb_port *dst)
|
||||
{
|
||||
return src->sw->config.depth < dst->sw->config.depth;
|
||||
}
|
||||
|
||||
static inline bool tb_port_use_credit_allocation(const struct tb_port *port)
|
||||
{
|
||||
return tb_port_is_null(port) && port->sw->credit_allocation;
|
||||
@ -1057,12 +1076,29 @@ static inline bool tb_port_use_credit_allocation(const struct tb_port *port)
|
||||
for ((p) = tb_next_port_on_path((src), (dst), NULL); (p); \
|
||||
(p) = tb_next_port_on_path((src), (dst), (p)))
|
||||
|
||||
/**
|
||||
* tb_for_each_upstream_port_on_path() - Iterate over each upstreamm port on path
|
||||
* @src: Source port
|
||||
* @dst: Destination port
|
||||
* @p: Port used as iterator
|
||||
*
|
||||
* Walks over each upstream lane adapter on path from @src to @dst.
|
||||
*/
|
||||
#define tb_for_each_upstream_port_on_path(src, dst, p) \
|
||||
for ((p) = tb_next_port_on_path((src), (dst), NULL); (p); \
|
||||
(p) = tb_next_port_on_path((src), (dst), (p))) \
|
||||
if (!tb_port_is_null((p)) || !tb_is_upstream_port((p))) {\
|
||||
continue; \
|
||||
} else
|
||||
|
||||
int tb_port_get_link_speed(struct tb_port *port);
|
||||
int tb_port_get_link_generation(struct tb_port *port);
|
||||
int tb_port_get_link_width(struct tb_port *port);
|
||||
bool tb_port_width_supported(struct tb_port *port, unsigned int width);
|
||||
int tb_port_set_link_width(struct tb_port *port, enum tb_link_width width);
|
||||
int tb_port_lane_bonding_enable(struct tb_port *port);
|
||||
void tb_port_lane_bonding_disable(struct tb_port *port);
|
||||
int tb_port_wait_for_link_width(struct tb_port *port, unsigned int width_mask,
|
||||
int tb_port_wait_for_link_width(struct tb_port *port, unsigned int width,
|
||||
int timeout_msec);
|
||||
int tb_port_update_credits(struct tb_port *port);
|
||||
|
||||
@ -1256,6 +1292,11 @@ int usb4_port_router_online(struct tb_port *port);
|
||||
int usb4_port_enumerate_retimers(struct tb_port *port);
|
||||
bool usb4_port_clx_supported(struct tb_port *port);
|
||||
int usb4_port_margining_caps(struct tb_port *port, u32 *caps);
|
||||
|
||||
bool usb4_port_asym_supported(struct tb_port *port);
|
||||
int usb4_port_asym_set_link_width(struct tb_port *port, enum tb_link_width width);
|
||||
int usb4_port_asym_start(struct tb_port *port);
|
||||
|
||||
int usb4_port_hw_margin(struct tb_port *port, unsigned int lanes,
|
||||
unsigned int ber_level, bool timing, bool right_high,
|
||||
u32 *results);
|
||||
@ -1283,7 +1324,6 @@ int usb4_port_retimer_nvm_read(struct tb_port *port, u8 index,
|
||||
unsigned int address, void *buf, size_t size);
|
||||
|
||||
int usb4_usb3_port_max_link_rate(struct tb_port *port);
|
||||
int usb4_usb3_port_actual_link_rate(struct tb_port *port);
|
||||
int usb4_usb3_port_allocated_bandwidth(struct tb_port *port, int *upstream_bw,
|
||||
int *downstream_bw);
|
||||
int usb4_usb3_port_allocate_bandwidth(struct tb_port *port, int *upstream_bw,
|
||||
|
@ -346,10 +346,14 @@ struct tb_regs_port_header {
|
||||
#define LANE_ADP_CS_1 0x01
|
||||
#define LANE_ADP_CS_1_TARGET_SPEED_MASK GENMASK(3, 0)
|
||||
#define LANE_ADP_CS_1_TARGET_SPEED_GEN3 0xc
|
||||
#define LANE_ADP_CS_1_TARGET_WIDTH_MASK GENMASK(9, 4)
|
||||
#define LANE_ADP_CS_1_TARGET_WIDTH_MASK GENMASK(5, 4)
|
||||
#define LANE_ADP_CS_1_TARGET_WIDTH_SHIFT 4
|
||||
#define LANE_ADP_CS_1_TARGET_WIDTH_SINGLE 0x1
|
||||
#define LANE_ADP_CS_1_TARGET_WIDTH_DUAL 0x3
|
||||
#define LANE_ADP_CS_1_TARGET_WIDTH_ASYM_MASK GENMASK(7, 6)
|
||||
#define LANE_ADP_CS_1_TARGET_WIDTH_ASYM_TX 0x1
|
||||
#define LANE_ADP_CS_1_TARGET_WIDTH_ASYM_RX 0x2
|
||||
#define LANE_ADP_CS_1_TARGET_WIDTH_ASYM_DUAL 0x0
|
||||
#define LANE_ADP_CS_1_CL0S_ENABLE BIT(10)
|
||||
#define LANE_ADP_CS_1_CL1_ENABLE BIT(11)
|
||||
#define LANE_ADP_CS_1_CL2_ENABLE BIT(12)
|
||||
@ -382,12 +386,15 @@ struct tb_regs_port_header {
|
||||
#define PORT_CS_18_WOCS BIT(16)
|
||||
#define PORT_CS_18_WODS BIT(17)
|
||||
#define PORT_CS_18_WOU4S BIT(18)
|
||||
#define PORT_CS_18_CSA BIT(22)
|
||||
#define PORT_CS_18_TIP BIT(24)
|
||||
#define PORT_CS_19 0x13
|
||||
#define PORT_CS_19_PC BIT(3)
|
||||
#define PORT_CS_19_PID BIT(4)
|
||||
#define PORT_CS_19_WOC BIT(16)
|
||||
#define PORT_CS_19_WOD BIT(17)
|
||||
#define PORT_CS_19_WOU4 BIT(18)
|
||||
#define PORT_CS_19_START_ASYM BIT(24)
|
||||
|
||||
/* Display Port adapter registers */
|
||||
#define ADP_DP_CS_0 0x00
|
||||
@ -400,7 +407,7 @@ struct tb_regs_port_header {
|
||||
#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_HPD 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)
|
||||
@ -417,7 +424,7 @@ struct tb_regs_port_header {
|
||||
#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 ADP_DP_CS_3_HPDC BIT(9)
|
||||
#define DP_LOCAL_CAP 0x04
|
||||
#define DP_REMOTE_CAP 0x05
|
||||
/* For DP IN adapter */
|
||||
@ -484,9 +491,6 @@ struct tb_regs_port_header {
|
||||
#define ADP_USB3_CS_3 0x03
|
||||
#define ADP_USB3_CS_3_SCALE_MASK GENMASK(5, 0)
|
||||
#define ADP_USB3_CS_4 0x04
|
||||
#define ADP_USB3_CS_4_ALR_MASK GENMASK(6, 0)
|
||||
#define ADP_USB3_CS_4_ALR_20G 0x1
|
||||
#define ADP_USB3_CS_4_ULV BIT(7)
|
||||
#define ADP_USB3_CS_4_MSLR_MASK GENMASK(18, 12)
|
||||
#define ADP_USB3_CS_4_MSLR_SHIFT 12
|
||||
#define ADP_USB3_CS_4_MSLR_20G 0x1
|
||||
@ -499,7 +503,8 @@ struct tb_regs_hop {
|
||||
* out_port (on the incoming port of the next switch)
|
||||
*/
|
||||
u32 out_port:6; /* next port of the path (on the same switch) */
|
||||
u32 initial_credits:8;
|
||||
u32 initial_credits:7;
|
||||
u32 pmps:1;
|
||||
u32 unknown1:6; /* set to zero */
|
||||
bool enable:1;
|
||||
|
||||
|
@ -21,12 +21,18 @@
|
||||
#define TB_PCI_PATH_DOWN 0
|
||||
#define TB_PCI_PATH_UP 1
|
||||
|
||||
#define TB_PCI_PRIORITY 3
|
||||
#define TB_PCI_WEIGHT 1
|
||||
|
||||
/* USB3 adapters use always HopID of 8 for both directions */
|
||||
#define TB_USB3_HOPID 8
|
||||
|
||||
#define TB_USB3_PATH_DOWN 0
|
||||
#define TB_USB3_PATH_UP 1
|
||||
|
||||
#define TB_USB3_PRIORITY 3
|
||||
#define TB_USB3_WEIGHT 2
|
||||
|
||||
/* DP adapters use HopID 8 for AUX and 9 for Video */
|
||||
#define TB_DP_AUX_TX_HOPID 8
|
||||
#define TB_DP_AUX_RX_HOPID 8
|
||||
@ -36,6 +42,12 @@
|
||||
#define TB_DP_AUX_PATH_OUT 1
|
||||
#define TB_DP_AUX_PATH_IN 2
|
||||
|
||||
#define TB_DP_VIDEO_PRIORITY 1
|
||||
#define TB_DP_VIDEO_WEIGHT 1
|
||||
|
||||
#define TB_DP_AUX_PRIORITY 2
|
||||
#define TB_DP_AUX_WEIGHT 1
|
||||
|
||||
/* Minimum number of credits needed for PCIe path */
|
||||
#define TB_MIN_PCIE_CREDITS 6U
|
||||
/*
|
||||
@ -46,6 +58,18 @@
|
||||
/* Minimum number of credits for DMA path */
|
||||
#define TB_MIN_DMA_CREDITS 1
|
||||
|
||||
#define TB_DMA_PRIORITY 5
|
||||
#define TB_DMA_WEIGHT 1
|
||||
|
||||
/*
|
||||
* Reserve additional bandwidth for USB 3.x and PCIe bulk traffic
|
||||
* according to USB4 v2 Connection Manager guide. This ends up reserving
|
||||
* 1500 Mb/s for PCIe and 3000 Mb/s for USB 3.x taking weights into
|
||||
* account.
|
||||
*/
|
||||
#define USB4_V2_PCI_MIN_BANDWIDTH (1500 * TB_PCI_WEIGHT)
|
||||
#define USB4_V2_USB3_MIN_BANDWIDTH (1500 * TB_USB3_WEIGHT)
|
||||
|
||||
static unsigned int dma_credits = TB_DMA_CREDITS;
|
||||
module_param(dma_credits, uint, 0444);
|
||||
MODULE_PARM_DESC(dma_credits, "specify custom credits for DMA tunnels (default: "
|
||||
@ -58,27 +82,6 @@ MODULE_PARM_DESC(bw_alloc_mode,
|
||||
|
||||
static const char * const tb_tunnel_names[] = { "PCI", "DP", "DMA", "USB3" };
|
||||
|
||||
#define __TB_TUNNEL_PRINT(level, tunnel, fmt, arg...) \
|
||||
do { \
|
||||
struct tb_tunnel *__tunnel = (tunnel); \
|
||||
level(__tunnel->tb, "%llx:%u <-> %llx:%u (%s): " fmt, \
|
||||
tb_route(__tunnel->src_port->sw), \
|
||||
__tunnel->src_port->port, \
|
||||
tb_route(__tunnel->dst_port->sw), \
|
||||
__tunnel->dst_port->port, \
|
||||
tb_tunnel_names[__tunnel->type], \
|
||||
## arg); \
|
||||
} while (0)
|
||||
|
||||
#define tb_tunnel_WARN(tunnel, fmt, arg...) \
|
||||
__TB_TUNNEL_PRINT(tb_WARN, tunnel, fmt, ##arg)
|
||||
#define tb_tunnel_warn(tunnel, fmt, arg...) \
|
||||
__TB_TUNNEL_PRINT(tb_warn, tunnel, fmt, ##arg)
|
||||
#define tb_tunnel_info(tunnel, fmt, arg...) \
|
||||
__TB_TUNNEL_PRINT(tb_info, tunnel, fmt, ##arg)
|
||||
#define tb_tunnel_dbg(tunnel, fmt, arg...) \
|
||||
__TB_TUNNEL_PRINT(tb_dbg, tunnel, fmt, ##arg)
|
||||
|
||||
static inline unsigned int tb_usable_credits(const struct tb_port *port)
|
||||
{
|
||||
return port->total_credits - port->ctl_credits;
|
||||
@ -131,6 +134,16 @@ static unsigned int tb_available_credits(const struct tb_port *port,
|
||||
return credits > 0 ? credits : 0;
|
||||
}
|
||||
|
||||
static void tb_init_pm_support(struct tb_path_hop *hop)
|
||||
{
|
||||
struct tb_port *out_port = hop->out_port;
|
||||
struct tb_port *in_port = hop->in_port;
|
||||
|
||||
if (tb_port_is_null(in_port) && tb_port_is_null(out_port) &&
|
||||
usb4_switch_version(in_port->sw) >= 2)
|
||||
hop->pm_support = true;
|
||||
}
|
||||
|
||||
static struct tb_tunnel *tb_tunnel_alloc(struct tb *tb, size_t npaths,
|
||||
enum tb_tunnel_type type)
|
||||
{
|
||||
@ -156,11 +169,11 @@ static struct tb_tunnel *tb_tunnel_alloc(struct tb *tb, size_t npaths,
|
||||
|
||||
static int tb_pci_set_ext_encapsulation(struct tb_tunnel *tunnel, bool enable)
|
||||
{
|
||||
struct tb_port *port = tb_upstream_port(tunnel->dst_port->sw);
|
||||
int ret;
|
||||
|
||||
/* Only supported of both routers are at least USB4 v2 */
|
||||
if (usb4_switch_version(tunnel->src_port->sw) < 2 ||
|
||||
usb4_switch_version(tunnel->dst_port->sw) < 2)
|
||||
if (tb_port_get_link_generation(port) < 4)
|
||||
return 0;
|
||||
|
||||
ret = usb4_pci_port_set_ext_encapsulation(tunnel->src_port, enable);
|
||||
@ -234,8 +247,8 @@ static int tb_pci_init_path(struct tb_path *path)
|
||||
path->egress_shared_buffer = TB_PATH_NONE;
|
||||
path->ingress_fc_enable = TB_PATH_ALL;
|
||||
path->ingress_shared_buffer = TB_PATH_NONE;
|
||||
path->priority = 3;
|
||||
path->weight = 1;
|
||||
path->priority = TB_PCI_PRIORITY;
|
||||
path->weight = TB_PCI_WEIGHT;
|
||||
path->drop_packages = 0;
|
||||
|
||||
tb_path_for_each_hop(path, hop) {
|
||||
@ -376,6 +389,51 @@ err_free:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* tb_tunnel_reserved_pci() - Amount of bandwidth to reserve for PCIe
|
||||
* @port: Lane 0 adapter
|
||||
* @reserved_up: Upstream bandwidth in Mb/s to reserve
|
||||
* @reserved_down: Downstream bandwidth in Mb/s to reserve
|
||||
*
|
||||
* Can be called to any connected lane 0 adapter to find out how much
|
||||
* bandwidth needs to be left in reserve for possible PCIe bulk traffic.
|
||||
* Returns true if there is something to be reserved and writes the
|
||||
* amount to @reserved_down/@reserved_up. Otherwise returns false and
|
||||
* does not touch the parameters.
|
||||
*/
|
||||
bool tb_tunnel_reserved_pci(struct tb_port *port, int *reserved_up,
|
||||
int *reserved_down)
|
||||
{
|
||||
if (WARN_ON_ONCE(!port->remote))
|
||||
return false;
|
||||
|
||||
if (!tb_acpi_may_tunnel_pcie())
|
||||
return false;
|
||||
|
||||
if (tb_port_get_link_generation(port) < 4)
|
||||
return false;
|
||||
|
||||
/* Must have PCIe adapters */
|
||||
if (tb_is_upstream_port(port)) {
|
||||
if (!tb_switch_find_port(port->sw, TB_TYPE_PCIE_UP))
|
||||
return false;
|
||||
if (!tb_switch_find_port(port->remote->sw, TB_TYPE_PCIE_DOWN))
|
||||
return false;
|
||||
} else {
|
||||
if (!tb_switch_find_port(port->sw, TB_TYPE_PCIE_DOWN))
|
||||
return false;
|
||||
if (!tb_switch_find_port(port->remote->sw, TB_TYPE_PCIE_UP))
|
||||
return false;
|
||||
}
|
||||
|
||||
*reserved_up = USB4_V2_PCI_MIN_BANDWIDTH;
|
||||
*reserved_down = USB4_V2_PCI_MIN_BANDWIDTH;
|
||||
|
||||
tb_port_dbg(port, "reserving %u/%u Mb/s for PCIe\n", *reserved_up,
|
||||
*reserved_down);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool tb_dp_is_usb4(const struct tb_switch *sw)
|
||||
{
|
||||
/* Titan Ridge DP adapters need the same treatment as USB4 */
|
||||
@ -614,8 +672,9 @@ static int tb_dp_xchg_caps(struct tb_tunnel *tunnel)
|
||||
|
||||
in_rate = tb_dp_cap_get_rate(in_dp_cap);
|
||||
in_lanes = tb_dp_cap_get_lanes(in_dp_cap);
|
||||
tb_port_dbg(in, "maximum supported bandwidth %u Mb/s x%u = %u Mb/s\n",
|
||||
in_rate, in_lanes, tb_dp_bandwidth(in_rate, in_lanes));
|
||||
tb_tunnel_dbg(tunnel,
|
||||
"DP IN maximum supported bandwidth %u Mb/s x%u = %u Mb/s\n",
|
||||
in_rate, in_lanes, tb_dp_bandwidth(in_rate, in_lanes));
|
||||
|
||||
/*
|
||||
* If the tunnel bandwidth is limited (max_bw is set) then see
|
||||
@ -624,10 +683,11 @@ static int tb_dp_xchg_caps(struct tb_tunnel *tunnel)
|
||||
out_rate = tb_dp_cap_get_rate(out_dp_cap);
|
||||
out_lanes = tb_dp_cap_get_lanes(out_dp_cap);
|
||||
bw = tb_dp_bandwidth(out_rate, out_lanes);
|
||||
tb_port_dbg(out, "maximum supported bandwidth %u Mb/s x%u = %u Mb/s\n",
|
||||
out_rate, out_lanes, bw);
|
||||
tb_tunnel_dbg(tunnel,
|
||||
"DP OUT maximum supported bandwidth %u Mb/s x%u = %u Mb/s\n",
|
||||
out_rate, out_lanes, bw);
|
||||
|
||||
if (in->sw->config.depth < out->sw->config.depth)
|
||||
if (tb_port_path_direction_downstream(in, out))
|
||||
max_bw = tunnel->max_down;
|
||||
else
|
||||
max_bw = tunnel->max_up;
|
||||
@ -639,13 +699,14 @@ static int tb_dp_xchg_caps(struct tb_tunnel *tunnel)
|
||||
out_rate, out_lanes, &new_rate,
|
||||
&new_lanes);
|
||||
if (ret) {
|
||||
tb_port_info(out, "not enough bandwidth for DP tunnel\n");
|
||||
tb_tunnel_info(tunnel, "not enough bandwidth\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
new_bw = tb_dp_bandwidth(new_rate, new_lanes);
|
||||
tb_port_dbg(out, "bandwidth reduced to %u Mb/s x%u = %u Mb/s\n",
|
||||
new_rate, new_lanes, new_bw);
|
||||
tb_tunnel_dbg(tunnel,
|
||||
"bandwidth reduced to %u Mb/s x%u = %u Mb/s\n",
|
||||
new_rate, new_lanes, new_bw);
|
||||
|
||||
/*
|
||||
* Set new rate and number of lanes before writing it to
|
||||
@ -662,7 +723,7 @@ static int tb_dp_xchg_caps(struct tb_tunnel *tunnel)
|
||||
*/
|
||||
if (tb_route(out->sw) && tb_switch_is_titan_ridge(out->sw)) {
|
||||
out_dp_cap |= DP_COMMON_CAP_LTTPR_NS;
|
||||
tb_port_dbg(out, "disabling LTTPR\n");
|
||||
tb_tunnel_dbg(tunnel, "disabling LTTPR\n");
|
||||
}
|
||||
|
||||
return tb_port_write(in, &out_dp_cap, TB_CFG_PORT,
|
||||
@ -712,8 +773,8 @@ static int tb_dp_bandwidth_alloc_mode_enable(struct tb_tunnel *tunnel)
|
||||
lanes = min(in_lanes, out_lanes);
|
||||
tmp = tb_dp_bandwidth(rate, lanes);
|
||||
|
||||
tb_port_dbg(in, "non-reduced bandwidth %u Mb/s x%u = %u Mb/s\n", rate,
|
||||
lanes, tmp);
|
||||
tb_tunnel_dbg(tunnel, "non-reduced bandwidth %u Mb/s x%u = %u Mb/s\n",
|
||||
rate, lanes, tmp);
|
||||
|
||||
ret = usb4_dp_port_set_nrd(in, rate, lanes);
|
||||
if (ret)
|
||||
@ -728,15 +789,15 @@ static int tb_dp_bandwidth_alloc_mode_enable(struct tb_tunnel *tunnel)
|
||||
rate = min(in_rate, out_rate);
|
||||
tmp = tb_dp_bandwidth(rate, lanes);
|
||||
|
||||
tb_port_dbg(in,
|
||||
"maximum bandwidth through allocation mode %u Mb/s x%u = %u Mb/s\n",
|
||||
rate, lanes, tmp);
|
||||
tb_tunnel_dbg(tunnel,
|
||||
"maximum bandwidth through allocation mode %u Mb/s x%u = %u Mb/s\n",
|
||||
rate, lanes, tmp);
|
||||
|
||||
for (granularity = 250; tmp / granularity > 255 && granularity <= 1000;
|
||||
granularity *= 2)
|
||||
;
|
||||
|
||||
tb_port_dbg(in, "granularity %d Mb/s\n", granularity);
|
||||
tb_tunnel_dbg(tunnel, "granularity %d Mb/s\n", granularity);
|
||||
|
||||
/*
|
||||
* Returns -EINVAL if granularity above is outside of the
|
||||
@ -751,12 +812,12 @@ static int tb_dp_bandwidth_alloc_mode_enable(struct tb_tunnel *tunnel)
|
||||
* max_up/down fields. For discovery we just read what the
|
||||
* estimation was set to.
|
||||
*/
|
||||
if (in->sw->config.depth < out->sw->config.depth)
|
||||
if (tb_port_path_direction_downstream(in, out))
|
||||
estimated_bw = tunnel->max_down;
|
||||
else
|
||||
estimated_bw = tunnel->max_up;
|
||||
|
||||
tb_port_dbg(in, "estimated bandwidth %d Mb/s\n", estimated_bw);
|
||||
tb_tunnel_dbg(tunnel, "estimated bandwidth %d Mb/s\n", estimated_bw);
|
||||
|
||||
ret = usb4_dp_port_set_estimated_bandwidth(in, estimated_bw);
|
||||
if (ret)
|
||||
@ -767,7 +828,7 @@ static int tb_dp_bandwidth_alloc_mode_enable(struct tb_tunnel *tunnel)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
tb_port_dbg(in, "bandwidth allocation mode enabled\n");
|
||||
tb_tunnel_dbg(tunnel, "bandwidth allocation mode enabled\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -788,7 +849,7 @@ static int tb_dp_init(struct tb_tunnel *tunnel)
|
||||
if (!usb4_dp_port_bandwidth_mode_supported(in))
|
||||
return 0;
|
||||
|
||||
tb_port_dbg(in, "bandwidth allocation mode supported\n");
|
||||
tb_tunnel_dbg(tunnel, "bandwidth allocation mode supported\n");
|
||||
|
||||
ret = usb4_dp_port_set_cm_id(in, tb->index);
|
||||
if (ret)
|
||||
@ -805,7 +866,7 @@ static void tb_dp_deinit(struct tb_tunnel *tunnel)
|
||||
return;
|
||||
if (usb4_dp_port_bandwidth_mode_enabled(in)) {
|
||||
usb4_dp_port_set_cm_bandwidth_mode_supported(in, false);
|
||||
tb_port_dbg(in, "bandwidth allocation mode disabled\n");
|
||||
tb_tunnel_dbg(tunnel, "bandwidth allocation mode disabled\n");
|
||||
}
|
||||
}
|
||||
|
||||
@ -921,10 +982,7 @@ static int tb_dp_bandwidth_mode_consumed_bandwidth(struct tb_tunnel *tunnel,
|
||||
if (allocated_bw == max_bw)
|
||||
allocated_bw = ret;
|
||||
|
||||
tb_port_dbg(in, "consumed bandwidth through allocation mode %d Mb/s\n",
|
||||
allocated_bw);
|
||||
|
||||
if (in->sw->config.depth < out->sw->config.depth) {
|
||||
if (tb_port_path_direction_downstream(in, out)) {
|
||||
*consumed_up = 0;
|
||||
*consumed_down = allocated_bw;
|
||||
} else {
|
||||
@ -959,7 +1017,7 @@ static int tb_dp_allocated_bandwidth(struct tb_tunnel *tunnel, int *allocated_up
|
||||
if (allocated_bw == max_bw)
|
||||
allocated_bw = ret;
|
||||
|
||||
if (in->sw->config.depth < out->sw->config.depth) {
|
||||
if (tb_port_path_direction_downstream(in, out)) {
|
||||
*allocated_up = 0;
|
||||
*allocated_down = allocated_bw;
|
||||
} else {
|
||||
@ -987,7 +1045,7 @@ static int tb_dp_alloc_bandwidth(struct tb_tunnel *tunnel, int *alloc_up,
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (in->sw->config.depth < out->sw->config.depth) {
|
||||
if (tb_port_path_direction_downstream(in, out)) {
|
||||
tmp = min(*alloc_down, max_bw);
|
||||
ret = usb4_dp_port_allocate_bandwidth(in, tmp);
|
||||
if (ret)
|
||||
@ -1006,9 +1064,6 @@ static int tb_dp_alloc_bandwidth(struct tb_tunnel *tunnel, int *alloc_up,
|
||||
/* Now we can use BW mode registers to figure out the bandwidth */
|
||||
/* TODO: need to handle discovery too */
|
||||
tunnel->bw_mode = true;
|
||||
|
||||
tb_port_dbg(in, "allocated bandwidth through allocation mode %d Mb/s\n",
|
||||
tmp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1035,8 +1090,7 @@ static int tb_dp_read_dprx(struct tb_tunnel *tunnel, u32 *rate, u32 *lanes,
|
||||
*rate = tb_dp_cap_get_rate(val);
|
||||
*lanes = tb_dp_cap_get_lanes(val);
|
||||
|
||||
tb_port_dbg(in, "consumed bandwidth through DPRX %d Mb/s\n",
|
||||
tb_dp_bandwidth(*rate, *lanes));
|
||||
tb_tunnel_dbg(tunnel, "DPRX read done\n");
|
||||
return 0;
|
||||
}
|
||||
usleep_range(100, 150);
|
||||
@ -1073,9 +1127,6 @@ static int tb_dp_read_cap(struct tb_tunnel *tunnel, unsigned int cap, u32 *rate,
|
||||
|
||||
*rate = tb_dp_cap_get_rate(val);
|
||||
*lanes = tb_dp_cap_get_lanes(val);
|
||||
|
||||
tb_port_dbg(in, "bandwidth from %#x capability %d Mb/s\n", cap,
|
||||
tb_dp_bandwidth(*rate, *lanes));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1092,7 +1143,7 @@ static int tb_dp_maximum_bandwidth(struct tb_tunnel *tunnel, int *max_up,
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (in->sw->config.depth < tunnel->dst_port->sw->config.depth) {
|
||||
if (tb_port_path_direction_downstream(in, tunnel->dst_port)) {
|
||||
*max_up = 0;
|
||||
*max_down = ret;
|
||||
} else {
|
||||
@ -1150,7 +1201,7 @@ static int tb_dp_consumed_bandwidth(struct tb_tunnel *tunnel, int *consumed_up,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (in->sw->config.depth < tunnel->dst_port->sw->config.depth) {
|
||||
if (tb_port_path_direction_downstream(in, tunnel->dst_port)) {
|
||||
*consumed_up = 0;
|
||||
*consumed_down = tb_dp_bandwidth(rate, lanes);
|
||||
} else {
|
||||
@ -1172,7 +1223,7 @@ static void tb_dp_init_aux_credits(struct tb_path_hop *hop)
|
||||
hop->initial_credits = 1;
|
||||
}
|
||||
|
||||
static void tb_dp_init_aux_path(struct tb_path *path)
|
||||
static void tb_dp_init_aux_path(struct tb_path *path, bool pm_support)
|
||||
{
|
||||
struct tb_path_hop *hop;
|
||||
|
||||
@ -1180,11 +1231,14 @@ static void tb_dp_init_aux_path(struct tb_path *path)
|
||||
path->egress_shared_buffer = TB_PATH_NONE;
|
||||
path->ingress_fc_enable = TB_PATH_ALL;
|
||||
path->ingress_shared_buffer = TB_PATH_NONE;
|
||||
path->priority = 2;
|
||||
path->weight = 1;
|
||||
path->priority = TB_DP_AUX_PRIORITY;
|
||||
path->weight = TB_DP_AUX_WEIGHT;
|
||||
|
||||
tb_path_for_each_hop(path, hop)
|
||||
tb_path_for_each_hop(path, hop) {
|
||||
tb_dp_init_aux_credits(hop);
|
||||
if (pm_support)
|
||||
tb_init_pm_support(hop);
|
||||
}
|
||||
}
|
||||
|
||||
static int tb_dp_init_video_credits(struct tb_path_hop *hop)
|
||||
@ -1216,7 +1270,7 @@ static int tb_dp_init_video_credits(struct tb_path_hop *hop)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tb_dp_init_video_path(struct tb_path *path)
|
||||
static int tb_dp_init_video_path(struct tb_path *path, bool pm_support)
|
||||
{
|
||||
struct tb_path_hop *hop;
|
||||
|
||||
@ -1224,8 +1278,8 @@ static int tb_dp_init_video_path(struct tb_path *path)
|
||||
path->egress_shared_buffer = TB_PATH_NONE;
|
||||
path->ingress_fc_enable = TB_PATH_NONE;
|
||||
path->ingress_shared_buffer = TB_PATH_NONE;
|
||||
path->priority = 1;
|
||||
path->weight = 1;
|
||||
path->priority = TB_DP_VIDEO_PRIORITY;
|
||||
path->weight = TB_DP_VIDEO_WEIGHT;
|
||||
|
||||
tb_path_for_each_hop(path, hop) {
|
||||
int ret;
|
||||
@ -1233,6 +1287,8 @@ static int tb_dp_init_video_path(struct tb_path *path)
|
||||
ret = tb_dp_init_video_credits(hop);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (pm_support)
|
||||
tb_init_pm_support(hop);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -1253,8 +1309,9 @@ static void tb_dp_dump(struct tb_tunnel *tunnel)
|
||||
rate = tb_dp_cap_get_rate(dp_cap);
|
||||
lanes = tb_dp_cap_get_lanes(dp_cap);
|
||||
|
||||
tb_port_dbg(in, "maximum supported bandwidth %u Mb/s x%u = %u Mb/s\n",
|
||||
rate, lanes, tb_dp_bandwidth(rate, lanes));
|
||||
tb_tunnel_dbg(tunnel,
|
||||
"DP IN maximum supported bandwidth %u Mb/s x%u = %u Mb/s\n",
|
||||
rate, lanes, tb_dp_bandwidth(rate, lanes));
|
||||
|
||||
out = tunnel->dst_port;
|
||||
|
||||
@ -1265,8 +1322,9 @@ static void tb_dp_dump(struct tb_tunnel *tunnel)
|
||||
rate = tb_dp_cap_get_rate(dp_cap);
|
||||
lanes = tb_dp_cap_get_lanes(dp_cap);
|
||||
|
||||
tb_port_dbg(out, "maximum supported bandwidth %u Mb/s x%u = %u Mb/s\n",
|
||||
rate, lanes, tb_dp_bandwidth(rate, lanes));
|
||||
tb_tunnel_dbg(tunnel,
|
||||
"DP OUT maximum supported bandwidth %u Mb/s x%u = %u Mb/s\n",
|
||||
rate, lanes, tb_dp_bandwidth(rate, lanes));
|
||||
|
||||
if (tb_port_read(in, &dp_cap, TB_CFG_PORT,
|
||||
in->cap_adap + DP_REMOTE_CAP, 1))
|
||||
@ -1275,8 +1333,8 @@ static void tb_dp_dump(struct tb_tunnel *tunnel)
|
||||
rate = tb_dp_cap_get_rate(dp_cap);
|
||||
lanes = tb_dp_cap_get_lanes(dp_cap);
|
||||
|
||||
tb_port_dbg(in, "reduced bandwidth %u Mb/s x%u = %u Mb/s\n",
|
||||
rate, lanes, tb_dp_bandwidth(rate, lanes));
|
||||
tb_tunnel_dbg(tunnel, "reduced bandwidth %u Mb/s x%u = %u Mb/s\n",
|
||||
rate, lanes, tb_dp_bandwidth(rate, lanes));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1322,7 +1380,7 @@ struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in,
|
||||
goto err_free;
|
||||
}
|
||||
tunnel->paths[TB_DP_VIDEO_PATH_OUT] = path;
|
||||
if (tb_dp_init_video_path(tunnel->paths[TB_DP_VIDEO_PATH_OUT]))
|
||||
if (tb_dp_init_video_path(tunnel->paths[TB_DP_VIDEO_PATH_OUT], false))
|
||||
goto err_free;
|
||||
|
||||
path = tb_path_discover(in, TB_DP_AUX_TX_HOPID, NULL, -1, NULL, "AUX TX",
|
||||
@ -1330,14 +1388,14 @@ struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in,
|
||||
if (!path)
|
||||
goto err_deactivate;
|
||||
tunnel->paths[TB_DP_AUX_PATH_OUT] = path;
|
||||
tb_dp_init_aux_path(tunnel->paths[TB_DP_AUX_PATH_OUT]);
|
||||
tb_dp_init_aux_path(tunnel->paths[TB_DP_AUX_PATH_OUT], false);
|
||||
|
||||
path = tb_path_discover(tunnel->dst_port, -1, in, TB_DP_AUX_RX_HOPID,
|
||||
&port, "AUX RX", alloc_hopid);
|
||||
if (!path)
|
||||
goto err_deactivate;
|
||||
tunnel->paths[TB_DP_AUX_PATH_IN] = path;
|
||||
tb_dp_init_aux_path(tunnel->paths[TB_DP_AUX_PATH_IN]);
|
||||
tb_dp_init_aux_path(tunnel->paths[TB_DP_AUX_PATH_IN], false);
|
||||
|
||||
/* Validate that the tunnel is complete */
|
||||
if (!tb_port_is_dpout(tunnel->dst_port)) {
|
||||
@ -1392,6 +1450,7 @@ struct tb_tunnel *tb_tunnel_alloc_dp(struct tb *tb, struct tb_port *in,
|
||||
struct tb_tunnel *tunnel;
|
||||
struct tb_path **paths;
|
||||
struct tb_path *path;
|
||||
bool pm_support;
|
||||
|
||||
if (WARN_ON(!in->cap_adap || !out->cap_adap))
|
||||
return NULL;
|
||||
@ -1413,26 +1472,27 @@ struct tb_tunnel *tb_tunnel_alloc_dp(struct tb *tb, struct tb_port *in,
|
||||
tunnel->max_down = max_down;
|
||||
|
||||
paths = tunnel->paths;
|
||||
pm_support = usb4_switch_version(in->sw) >= 2;
|
||||
|
||||
path = tb_path_alloc(tb, in, TB_DP_VIDEO_HOPID, out, TB_DP_VIDEO_HOPID,
|
||||
link_nr, "Video");
|
||||
if (!path)
|
||||
goto err_free;
|
||||
tb_dp_init_video_path(path);
|
||||
tb_dp_init_video_path(path, pm_support);
|
||||
paths[TB_DP_VIDEO_PATH_OUT] = path;
|
||||
|
||||
path = tb_path_alloc(tb, in, TB_DP_AUX_TX_HOPID, out,
|
||||
TB_DP_AUX_TX_HOPID, link_nr, "AUX TX");
|
||||
if (!path)
|
||||
goto err_free;
|
||||
tb_dp_init_aux_path(path);
|
||||
tb_dp_init_aux_path(path, pm_support);
|
||||
paths[TB_DP_AUX_PATH_OUT] = path;
|
||||
|
||||
path = tb_path_alloc(tb, out, TB_DP_AUX_RX_HOPID, in,
|
||||
TB_DP_AUX_RX_HOPID, link_nr, "AUX RX");
|
||||
if (!path)
|
||||
goto err_free;
|
||||
tb_dp_init_aux_path(path);
|
||||
tb_dp_init_aux_path(path, pm_support);
|
||||
paths[TB_DP_AUX_PATH_IN] = path;
|
||||
|
||||
return tunnel;
|
||||
@ -1497,8 +1557,8 @@ static int tb_dma_init_rx_path(struct tb_path *path, unsigned int credits)
|
||||
path->ingress_fc_enable = TB_PATH_ALL;
|
||||
path->egress_shared_buffer = TB_PATH_NONE;
|
||||
path->ingress_shared_buffer = TB_PATH_NONE;
|
||||
path->priority = 5;
|
||||
path->weight = 1;
|
||||
path->priority = TB_DMA_PRIORITY;
|
||||
path->weight = TB_DMA_WEIGHT;
|
||||
path->clear_fc = true;
|
||||
|
||||
/*
|
||||
@ -1531,8 +1591,8 @@ static int tb_dma_init_tx_path(struct tb_path *path, unsigned int credits)
|
||||
path->ingress_fc_enable = TB_PATH_ALL;
|
||||
path->egress_shared_buffer = TB_PATH_NONE;
|
||||
path->ingress_shared_buffer = TB_PATH_NONE;
|
||||
path->priority = 5;
|
||||
path->weight = 1;
|
||||
path->priority = TB_DMA_PRIORITY;
|
||||
path->weight = TB_DMA_WEIGHT;
|
||||
path->clear_fc = true;
|
||||
|
||||
tb_path_for_each_hop(path, hop) {
|
||||
@ -1758,14 +1818,23 @@ static int tb_usb3_activate(struct tb_tunnel *tunnel, bool activate)
|
||||
static int tb_usb3_consumed_bandwidth(struct tb_tunnel *tunnel,
|
||||
int *consumed_up, int *consumed_down)
|
||||
{
|
||||
int pcie_enabled = tb_acpi_may_tunnel_pcie();
|
||||
struct tb_port *port = tb_upstream_port(tunnel->dst_port->sw);
|
||||
int pcie_weight = tb_acpi_may_tunnel_pcie() ? TB_PCI_WEIGHT : 0;
|
||||
|
||||
/*
|
||||
* PCIe tunneling, if enabled, affects the USB3 bandwidth so
|
||||
* take that it into account here.
|
||||
*/
|
||||
*consumed_up = tunnel->allocated_up * (3 + pcie_enabled) / 3;
|
||||
*consumed_down = tunnel->allocated_down * (3 + pcie_enabled) / 3;
|
||||
*consumed_up = tunnel->allocated_up *
|
||||
(TB_USB3_WEIGHT + pcie_weight) / TB_USB3_WEIGHT;
|
||||
*consumed_down = tunnel->allocated_down *
|
||||
(TB_USB3_WEIGHT + pcie_weight) / TB_USB3_WEIGHT;
|
||||
|
||||
if (tb_port_get_link_generation(port) >= 4) {
|
||||
*consumed_up = max(*consumed_up, USB4_V2_USB3_MIN_BANDWIDTH);
|
||||
*consumed_down = max(*consumed_down, USB4_V2_USB3_MIN_BANDWIDTH);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1790,17 +1859,10 @@ static void tb_usb3_reclaim_available_bandwidth(struct tb_tunnel *tunnel,
|
||||
{
|
||||
int ret, max_rate, allocate_up, allocate_down;
|
||||
|
||||
ret = usb4_usb3_port_actual_link_rate(tunnel->src_port);
|
||||
ret = tb_usb3_max_link_rate(tunnel->dst_port, tunnel->src_port);
|
||||
if (ret < 0) {
|
||||
tb_tunnel_warn(tunnel, "failed to read actual link rate\n");
|
||||
tb_tunnel_warn(tunnel, "failed to read maximum link rate\n");
|
||||
return;
|
||||
} else if (!ret) {
|
||||
/* Use maximum link rate if the link valid is not set */
|
||||
ret = tb_usb3_max_link_rate(tunnel->dst_port, tunnel->src_port);
|
||||
if (ret < 0) {
|
||||
tb_tunnel_warn(tunnel, "failed to read maximum link rate\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1871,8 +1933,8 @@ static void tb_usb3_init_path(struct tb_path *path)
|
||||
path->egress_shared_buffer = TB_PATH_NONE;
|
||||
path->ingress_fc_enable = TB_PATH_ALL;
|
||||
path->ingress_shared_buffer = TB_PATH_NONE;
|
||||
path->priority = 3;
|
||||
path->weight = 3;
|
||||
path->priority = TB_USB3_PRIORITY;
|
||||
path->weight = TB_USB3_WEIGHT;
|
||||
path->drop_packages = 0;
|
||||
|
||||
tb_path_for_each_hop(path, hop)
|
||||
@ -2387,3 +2449,8 @@ void tb_tunnel_reclaim_available_bandwidth(struct tb_tunnel *tunnel,
|
||||
tunnel->reclaim_available_bandwidth(tunnel, available_up,
|
||||
available_down);
|
||||
}
|
||||
|
||||
const char *tb_tunnel_type_name(const struct tb_tunnel *tunnel)
|
||||
{
|
||||
return tb_tunnel_names[tunnel->type];
|
||||
}
|
||||
|
@ -80,6 +80,8 @@ struct tb_tunnel *tb_tunnel_discover_pci(struct tb *tb, struct tb_port *down,
|
||||
bool alloc_hopid);
|
||||
struct tb_tunnel *tb_tunnel_alloc_pci(struct tb *tb, struct tb_port *up,
|
||||
struct tb_port *down);
|
||||
bool tb_tunnel_reserved_pci(struct tb_port *port, int *reserved_up,
|
||||
int *reserved_down);
|
||||
struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in,
|
||||
bool alloc_hopid);
|
||||
struct tb_tunnel *tb_tunnel_alloc_dp(struct tb *tb, struct tb_port *in,
|
||||
@ -137,5 +139,27 @@ static inline bool tb_tunnel_is_usb3(const struct tb_tunnel *tunnel)
|
||||
return tunnel->type == TB_TUNNEL_USB3;
|
||||
}
|
||||
|
||||
#endif
|
||||
const char *tb_tunnel_type_name(const struct tb_tunnel *tunnel);
|
||||
|
||||
#define __TB_TUNNEL_PRINT(level, tunnel, fmt, arg...) \
|
||||
do { \
|
||||
struct tb_tunnel *__tunnel = (tunnel); \
|
||||
level(__tunnel->tb, "%llx:%u <-> %llx:%u (%s): " fmt, \
|
||||
tb_route(__tunnel->src_port->sw), \
|
||||
__tunnel->src_port->port, \
|
||||
tb_route(__tunnel->dst_port->sw), \
|
||||
__tunnel->dst_port->port, \
|
||||
tb_tunnel_type_name(__tunnel), \
|
||||
## arg); \
|
||||
} while (0)
|
||||
|
||||
#define tb_tunnel_WARN(tunnel, fmt, arg...) \
|
||||
__TB_TUNNEL_PRINT(tb_WARN, tunnel, fmt, ##arg)
|
||||
#define tb_tunnel_warn(tunnel, fmt, arg...) \
|
||||
__TB_TUNNEL_PRINT(tb_warn, tunnel, fmt, ##arg)
|
||||
#define tb_tunnel_info(tunnel, fmt, arg...) \
|
||||
__TB_TUNNEL_PRINT(tb_info, tunnel, fmt, ##arg)
|
||||
#define tb_tunnel_dbg(tunnel, fmt, arg...) \
|
||||
__TB_TUNNEL_PRINT(tb_dbg, tunnel, fmt, ##arg)
|
||||
|
||||
#endif
|
||||
|
@ -1454,6 +1454,112 @@ bool usb4_port_clx_supported(struct tb_port *port)
|
||||
return !!(val & PORT_CS_18_CPS);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb4_port_asym_supported() - If the port supports asymmetric link
|
||||
* @port: USB4 port
|
||||
*
|
||||
* Checks if the port and the cable supports asymmetric link and returns
|
||||
* %true in that case.
|
||||
*/
|
||||
bool usb4_port_asym_supported(struct tb_port *port)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
if (!port->cap_usb4)
|
||||
return false;
|
||||
|
||||
if (tb_port_read(port, &val, TB_CFG_PORT, port->cap_usb4 + PORT_CS_18, 1))
|
||||
return false;
|
||||
|
||||
return !!(val & PORT_CS_18_CSA);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb4_port_asym_set_link_width() - Set link width to asymmetric or symmetric
|
||||
* @port: USB4 port
|
||||
* @width: Asymmetric width to configure
|
||||
*
|
||||
* Sets USB4 port link width to @width. Can be called for widths where
|
||||
* usb4_port_asym_width_supported() returned @true.
|
||||
*/
|
||||
int usb4_port_asym_set_link_width(struct tb_port *port, enum tb_link_width width)
|
||||
{
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
if (!port->cap_phy)
|
||||
return -EINVAL;
|
||||
|
||||
ret = tb_port_read(port, &val, TB_CFG_PORT,
|
||||
port->cap_phy + LANE_ADP_CS_1, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val &= ~LANE_ADP_CS_1_TARGET_WIDTH_ASYM_MASK;
|
||||
switch (width) {
|
||||
case TB_LINK_WIDTH_DUAL:
|
||||
val |= FIELD_PREP(LANE_ADP_CS_1_TARGET_WIDTH_ASYM_MASK,
|
||||
LANE_ADP_CS_1_TARGET_WIDTH_ASYM_DUAL);
|
||||
break;
|
||||
case TB_LINK_WIDTH_ASYM_TX:
|
||||
val |= FIELD_PREP(LANE_ADP_CS_1_TARGET_WIDTH_ASYM_MASK,
|
||||
LANE_ADP_CS_1_TARGET_WIDTH_ASYM_TX);
|
||||
break;
|
||||
case TB_LINK_WIDTH_ASYM_RX:
|
||||
val |= FIELD_PREP(LANE_ADP_CS_1_TARGET_WIDTH_ASYM_MASK,
|
||||
LANE_ADP_CS_1_TARGET_WIDTH_ASYM_RX);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return tb_port_write(port, &val, TB_CFG_PORT,
|
||||
port->cap_phy + LANE_ADP_CS_1, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb4_port_asym_start() - Start symmetry change and wait for completion
|
||||
* @port: USB4 port
|
||||
*
|
||||
* Start symmetry change of the link to asymmetric or symmetric
|
||||
* (according to what was previously set in tb_port_set_link_width().
|
||||
* Wait for completion of the change.
|
||||
*
|
||||
* Returns %0 in case of success, %-ETIMEDOUT if case of timeout or
|
||||
* a negative errno in case of a failure.
|
||||
*/
|
||||
int usb4_port_asym_start(struct tb_port *port)
|
||||
{
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
ret = tb_port_read(port, &val, TB_CFG_PORT,
|
||||
port->cap_usb4 + PORT_CS_19, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val &= ~PORT_CS_19_START_ASYM;
|
||||
val |= FIELD_PREP(PORT_CS_19_START_ASYM, 1);
|
||||
|
||||
ret = tb_port_write(port, &val, TB_CFG_PORT,
|
||||
port->cap_usb4 + PORT_CS_19, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Wait for PORT_CS_19_START_ASYM to be 0. This means the USB4
|
||||
* port started the symmetry transition.
|
||||
*/
|
||||
ret = usb4_port_wait_for_bit(port, port->cap_usb4 + PORT_CS_19,
|
||||
PORT_CS_19_START_ASYM, 0, 1000);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Then wait for the transtion to be completed */
|
||||
return usb4_port_wait_for_bit(port, port->cap_usb4 + PORT_CS_18,
|
||||
PORT_CS_18_TIP, 0, 5000);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb4_port_margining_caps() - Read USB4 port marginig capabilities
|
||||
* @port: USB4 port
|
||||
@ -1946,35 +2052,6 @@ int usb4_usb3_port_max_link_rate(struct tb_port *port)
|
||||
return usb4_usb3_port_max_bandwidth(port, ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb4_usb3_port_actual_link_rate() - Established USB3 link rate
|
||||
* @port: USB3 adapter port
|
||||
*
|
||||
* Return actual established link rate of a USB3 adapter in Mb/s. If the
|
||||
* link is not up returns %0 and negative errno in case of failure.
|
||||
*/
|
||||
int usb4_usb3_port_actual_link_rate(struct tb_port *port)
|
||||
{
|
||||
int ret, lr;
|
||||
u32 val;
|
||||
|
||||
if (!tb_port_is_usb3_down(port) && !tb_port_is_usb3_up(port))
|
||||
return -EINVAL;
|
||||
|
||||
ret = tb_port_read(port, &val, TB_CFG_PORT,
|
||||
port->cap_adap + ADP_USB3_CS_4, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!(val & ADP_USB3_CS_4_ULV))
|
||||
return 0;
|
||||
|
||||
lr = val & ADP_USB3_CS_4_ALR_MASK;
|
||||
ret = lr == ADP_USB3_CS_4_ALR_20G ? 20000 : 10000;
|
||||
|
||||
return usb4_usb3_port_max_bandwidth(port, ret);
|
||||
}
|
||||
|
||||
static int usb4_usb3_port_cm_request(struct tb_port *port, bool request)
|
||||
{
|
||||
int ret;
|
||||
|
@ -175,7 +175,7 @@ void tb_unregister_property_dir(const char *key, struct tb_property_dir *dir);
|
||||
* enum tb_link_width - Thunderbolt/USB4 link width
|
||||
* @TB_LINK_WIDTH_SINGLE: Single lane link
|
||||
* @TB_LINK_WIDTH_DUAL: Dual lane symmetric link
|
||||
* @TB_LINK_WIDTH_ASYM_TX: Dual lane asymmetric Gen 4 link with 3 trasmitters
|
||||
* @TB_LINK_WIDTH_ASYM_TX: Dual lane asymmetric Gen 4 link with 3 transmitters
|
||||
* @TB_LINK_WIDTH_ASYM_RX: Dual lane asymmetric Gen 4 link with 3 receivers
|
||||
*/
|
||||
enum tb_link_width {
|
||||
|
Loading…
Reference in New Issue
Block a user