提交 e0d07ba7 编写于 作者: G Greg Kroah-Hartman

Merge tag 'thunderbolt-for-v5.17-rc1' of...

Merge tag 'thunderbolt-for-v5.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/westeri/thunderbolt into usb-next

Mika writes:

thunderbolt: Changes for v5.17 merge window

This includes following Thunderbolt/USB4 changes for the v5.17 merge
window:

  * Enable low-power link state (CL0s) for USB4 and Intel Titan Ridge
    devices
  * Add support for TMU (Time Management Unit) uni-directional mode
  * Power management improvements (suspend-to-disk, runtime PM)
  * USB4 compatibility fixes
  * Minor fixes and cleanups.

All these have been in linux-next with no reported issues.

* tag 'thunderbolt-for-v5.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/westeri/thunderbolt:
  thunderbolt: Add module parameter for CLx disabling
  thunderbolt: Enable CL0s for Intel Titan Ridge
  thunderbolt: Rename Intel TB_VSE_CAP_IECS capability
  thunderbolt: Implement TMU time disruption for Intel Titan Ridge
  thunderbolt: Move usb4_switch_wait_for_bit() to switch.c
  thunderbolt: Add CL0s support for USB4 routers
  thunderbolt: Add TMU uni-directional mode
  thunderbolt: Check return value of kmemdup() in icm_handle_event()
  thunderbolt: Do not dereference fwnode in struct device
  thunderbolt: Add debug logging of DisplayPort resource allocation
  thunderbolt: Do not program path HopIDs for USB4 routers
  thunderbolt: Do not allow subtracting more NFC credits than configured
  thunderbolt: Runtime resume USB4 port when retimers are scanned
  thunderbolt: Tear down existing tunnels when resuming from hibernate
  thunderbolt: Runtime PM activate both ends of the device link
  thunderbolt: xdomain: Avoid potential stack OOB read
......@@ -7,6 +7,7 @@
*/
#include <linux/acpi.h>
#include <linux/pm_runtime.h>
#include "tb.h"
......@@ -31,7 +32,7 @@ static acpi_status tb_acpi_add_link(acpi_handle handle, u32 level, void *data,
return AE_OK;
/* It needs to reference this NHI */
if (nhi->pdev->dev.fwnode != args.fwnode)
if (dev_fwnode(&nhi->pdev->dev) != args.fwnode)
goto out_put;
/*
......@@ -74,8 +75,18 @@ static acpi_status tb_acpi_add_link(acpi_handle handle, u32 level, void *data,
pci_pcie_type(pdev) == PCI_EXP_TYPE_DOWNSTREAM))) {
const struct device_link *link;
/*
* Make them both active first to make sure the NHI does
* not runtime suspend before the consumer. The
* pm_runtime_put() below then allows the consumer to
* runtime suspend again (which then allows NHI runtime
* suspend too now that the device link is established).
*/
pm_runtime_get_sync(&pdev->dev);
link = device_link_add(&pdev->dev, &nhi->pdev->dev,
DL_FLAG_AUTOREMOVE_SUPPLIER |
DL_FLAG_RPM_ACTIVE |
DL_FLAG_PM_RUNTIME);
if (link) {
dev_dbg(&nhi->pdev->dev, "created link from %s\n",
......@@ -84,6 +95,8 @@ static acpi_status tb_acpi_add_link(acpi_handle handle, u32 level, void *data,
dev_warn(&nhi->pdev->dev, "device link creation from %s failed\n",
dev_name(&pdev->dev));
}
pm_runtime_put(&pdev->dev);
}
out_put:
......
......@@ -1741,8 +1741,13 @@ static void icm_handle_event(struct tb *tb, enum tb_cfg_pkg_type type,
if (!n)
return;
INIT_WORK(&n->work, icm_handle_notification);
n->pkg = kmemdup(buf, size, GFP_KERNEL);
if (!n->pkg) {
kfree(n);
return;
}
INIT_WORK(&n->work, icm_handle_notification);
n->tb = tb;
queue_work(tb->wq, &n->work);
......
......@@ -193,6 +193,30 @@ int tb_lc_start_lane_initialization(struct tb_port *port)
return tb_sw_write(sw, &ctrl, TB_CFG_SWITCH, cap + TB_LC_SX_CTRL, 1);
}
/**
* tb_lc_is_clx_supported() - Check whether CLx is supported by the lane adapter
* @port: Lane adapter
*
* TB_LC_LINK_ATTR_CPS bit reflects if the link supports CLx including
* active cables (if connected on the link).
*/
bool tb_lc_is_clx_supported(struct tb_port *port)
{
struct tb_switch *sw = port->sw;
int cap, ret;
u32 val;
cap = find_port_lc_cap(port);
if (cap < 0)
return false;
ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, cap + TB_LC_LINK_ATTR, 1);
if (ret)
return false;
return !!(val & TB_LC_LINK_ATTR_CPS);
}
static int tb_lc_set_wake_one(struct tb_switch *sw, unsigned int offset,
unsigned int flags)
{
......
......@@ -85,11 +85,12 @@ static int tb_path_find_src_hopid(struct tb_port *src,
* @dst_hopid: HopID to the @dst (%-1 if don't care)
* @last: Last port is filled here if not %NULL
* @name: Name of the path
* @alloc_hopid: Allocate HopIDs for the ports
*
* Follows a path starting from @src and @src_hopid to the last output
* port of the path. Allocates HopIDs for the visited ports. Call
* tb_path_free() to release the path and allocated HopIDs when the path
* is not needed anymore.
* port of the path. Allocates HopIDs for the visited ports (if
* @alloc_hopid is true). Call tb_path_free() to release the path and
* allocated HopIDs when the path is not needed anymore.
*
* Note function discovers also incomplete paths so caller should check
* that the @dst port is the expected one. If it is not, the path can be
......@@ -99,7 +100,8 @@ static int tb_path_find_src_hopid(struct tb_port *src,
*/
struct tb_path *tb_path_discover(struct tb_port *src, int src_hopid,
struct tb_port *dst, int dst_hopid,
struct tb_port **last, const char *name)
struct tb_port **last, const char *name,
bool alloc_hopid)
{
struct tb_port *out_port;
struct tb_regs_hop hop;
......@@ -156,6 +158,7 @@ struct tb_path *tb_path_discover(struct tb_port *src, int src_hopid,
path->tb = src->sw->tb;
path->path_length = num_hops;
path->activated = true;
path->alloc_hopid = alloc_hopid;
path->hops = kcalloc(num_hops, sizeof(*path->hops), GFP_KERNEL);
if (!path->hops) {
......@@ -177,13 +180,14 @@ struct tb_path *tb_path_discover(struct tb_port *src, int src_hopid,
goto err;
}
if (tb_port_alloc_in_hopid(p, h, h) < 0)
if (alloc_hopid && tb_port_alloc_in_hopid(p, h, h) < 0)
goto err;
out_port = &sw->ports[hop.out_port];
next_hop = hop.next_hop;
if (tb_port_alloc_out_hopid(out_port, next_hop, next_hop) < 0) {
if (alloc_hopid &&
tb_port_alloc_out_hopid(out_port, next_hop, next_hop) < 0) {
tb_port_release_in_hopid(p, h);
goto err;
}
......@@ -263,6 +267,8 @@ struct tb_path *tb_path_alloc(struct tb *tb, struct tb_port *src, int src_hopid,
return NULL;
}
path->alloc_hopid = true;
in_hopid = src_hopid;
out_port = NULL;
......@@ -345,17 +351,19 @@ struct tb_path *tb_path_alloc(struct tb *tb, struct tb_port *src, int src_hopid,
*/
void tb_path_free(struct tb_path *path)
{
int i;
for (i = 0; i < path->path_length; i++) {
const struct tb_path_hop *hop = &path->hops[i];
if (hop->in_port)
tb_port_release_in_hopid(hop->in_port,
hop->in_hop_index);
if (hop->out_port)
tb_port_release_out_hopid(hop->out_port,
hop->next_hop_index);
if (path->alloc_hopid) {
int i;
for (i = 0; i < path->path_length; i++) {
const struct tb_path_hop *hop = &path->hops[i];
if (hop->in_port)
tb_port_release_in_hopid(hop->in_port,
hop->in_hop_index);
if (hop->out_port)
tb_port_release_out_hopid(hop->out_port,
hop->next_hop_index);
}
}
kfree(path->hops);
......
......@@ -324,15 +324,10 @@ struct device_type tb_retimer_type = {
static int tb_retimer_add(struct tb_port *port, u8 index, u32 auth_status)
{
struct usb4_port *usb4;
struct tb_retimer *rt;
u32 vendor, device;
int ret;
usb4 = port->usb4;
if (!usb4)
return -EINVAL;
ret = usb4_port_retimer_read(port, index, USB4_SB_VENDOR_ID, &vendor,
sizeof(vendor));
if (ret) {
......@@ -374,7 +369,7 @@ static int tb_retimer_add(struct tb_port *port, u8 index, u32 auth_status)
rt->port = port;
rt->tb = port->sw->tb;
rt->dev.parent = &usb4->dev;
rt->dev.parent = &port->usb4->dev;
rt->dev.bus = &tb_bus_type;
rt->dev.type = &tb_retimer_type;
dev_set_name(&rt->dev, "%s:%u.%u", dev_name(&port->sw->dev),
......@@ -453,6 +448,13 @@ int tb_retimer_scan(struct tb_port *port, bool add)
{
u32 status[TB_MAX_RETIMER_INDEX + 1] = {};
int ret, i, last_idx = 0;
struct usb4_port *usb4;
usb4 = port->usb4;
if (!usb4)
return 0;
pm_runtime_get_sync(&usb4->dev);
/*
* Send broadcast RT to make sure retimer indices facing this
......@@ -460,7 +462,7 @@ int tb_retimer_scan(struct tb_port *port, bool add)
*/
ret = usb4_port_enumerate_retimers(port);
if (ret)
return ret;
goto out;
/*
* Enable sideband channel for each retimer. We can do this
......@@ -490,8 +492,10 @@ int tb_retimer_scan(struct tb_port *port, bool add)
break;
}
if (!last_idx)
return 0;
if (!last_idx) {
ret = 0;
goto out;
}
/* Add on-board retimers if they do not exist already */
for (i = 1; i <= last_idx; i++) {
......@@ -507,7 +511,11 @@ int tb_retimer_scan(struct tb_port *port, bool add)
}
}
return 0;
out:
pm_runtime_mark_last_busy(&usb4->dev);
pm_runtime_put_autosuspend(&usb4->dev);
return ret;
}
static int remove_retimer(struct device *dev, void *data)
......
......@@ -13,6 +13,7 @@
#include <linux/sched/signal.h>
#include <linux/sizes.h>
#include <linux/slab.h>
#include <linux/module.h>
#include "tb.h"
......@@ -26,6 +27,10 @@ struct nvm_auth_status {
u32 status;
};
static bool clx_enabled = true;
module_param_named(clx, clx_enabled, bool, 0444);
MODULE_PARM_DESC(clx, "allow low power states on the high-speed lanes (default: true)");
/*
* Hold NVM authentication failure status per switch This information
* needs to stay around even when the switch gets power cycled so we
......@@ -623,6 +628,9 @@ int tb_port_add_nfc_credits(struct tb_port *port, int credits)
return 0;
nfc_credits = port->config.nfc_credits & ADP_CS_4_NFC_BUFFERS_MASK;
if (credits < 0)
credits = max_t(int, -nfc_credits, credits);
nfc_credits += credits;
tb_port_dbg(port, "adding %d NFC credits to %lu", credits,
......@@ -1319,7 +1327,9 @@ int tb_dp_port_hpd_clear(struct tb_port *port)
* @aux_tx: AUX TX Hop ID
* @aux_rx: AUX RX Hop ID
*
* Programs specified Hop IDs for DP IN/OUT port.
* Programs specified Hop IDs for DP IN/OUT port. Can be called for USB4
* router DP adapters too but does not program the values as the fields
* are read-only.
*/
int tb_dp_port_set_hops(struct tb_port *port, unsigned int video,
unsigned int aux_tx, unsigned int aux_rx)
......@@ -1327,6 +1337,9 @@ int tb_dp_port_set_hops(struct tb_port *port, unsigned int video,
u32 data[2];
int ret;
if (tb_switch_is_usb4(port->sw))
return 0;
ret = tb_port_read(port, data, TB_CFG_PORT,
port->cap_adap + ADP_DP_CS_0, ARRAY_SIZE(data));
if (ret)
......@@ -1449,6 +1462,40 @@ int tb_switch_reset(struct tb_switch *sw)
return res.err;
}
/**
* tb_switch_wait_for_bit() - Wait for specified value of bits in offset
* @sw: Router to read the offset value from
* @offset: Offset in the router config space to read from
* @bit: Bit mask in the offset to wait for
* @value: Value of the bits to wait for
* @timeout_msec: Timeout in ms how long to wait
*
* Wait till the specified bits in specified offset reach specified value.
* Returns %0 in case of success, %-ETIMEDOUT if the @value was not reached
* within the given timeout or a negative errno in case of failure.
*/
int tb_switch_wait_for_bit(struct tb_switch *sw, u32 offset, u32 bit,
u32 value, int timeout_msec)
{
ktime_t timeout = ktime_add_ms(ktime_get(), timeout_msec);
do {
u32 val;
int ret;
ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, offset, 1);
if (ret)
return ret;
if ((val & bit) == value)
return 0;
usleep_range(50, 100);
} while (ktime_before(ktime_get(), timeout));
return -ETIMEDOUT;
}
/*
* tb_plug_events_active() - enable/disable plug events on a switch
*
......@@ -2186,10 +2233,18 @@ struct tb_switch *tb_switch_alloc(struct tb *tb, struct device *parent,
if (ret > 0)
sw->cap_plug_events = ret;
ret = tb_switch_find_vse_cap(sw, TB_VSE_CAP_TIME2);
if (ret > 0)
sw->cap_vsec_tmu = ret;
ret = tb_switch_find_vse_cap(sw, TB_VSE_CAP_LINK_CONTROLLER);
if (ret > 0)
sw->cap_lc = ret;
ret = tb_switch_find_vse_cap(sw, TB_VSE_CAP_CP_LP);
if (ret > 0)
sw->cap_lp = ret;
/* Root switch is always authorized */
if (!route)
sw->authorized = true;
......@@ -2996,6 +3051,13 @@ void tb_switch_suspend(struct tb_switch *sw, bool runtime)
tb_sw_dbg(sw, "suspending switch\n");
/*
* Actually only needed for Titan Ridge but for simplicity can be
* done for USB4 device too as CLx is re-enabled at resume.
*/
if (tb_switch_disable_clx(sw, TB_CL0S))
tb_sw_warn(sw, "failed to disable CLx on upstream port\n");
err = tb_plug_events_active(sw, false);
if (err)
return;
......@@ -3048,9 +3110,20 @@ bool tb_switch_query_dp_resource(struct tb_switch *sw, struct tb_port *in)
*/
int tb_switch_alloc_dp_resource(struct tb_switch *sw, struct tb_port *in)
{
int ret;
if (tb_switch_is_usb4(sw))
return usb4_switch_alloc_dp_resource(sw, in);
return tb_lc_dp_sink_alloc(sw, in);
ret = usb4_switch_alloc_dp_resource(sw, in);
else
ret = tb_lc_dp_sink_alloc(sw, in);
if (ret)
tb_sw_warn(sw, "failed to allocate DP resource for port %d\n",
in->port);
else
tb_sw_dbg(sw, "allocated DP resource for port %d\n", in->port);
return ret;
}
/**
......@@ -3073,6 +3146,8 @@ void tb_switch_dealloc_dp_resource(struct tb_switch *sw, struct tb_port *in)
if (ret)
tb_sw_warn(sw, "failed to de-allocate DP resource for port %d\n",
in->port);
else
tb_sw_dbg(sw, "released DP resource for port %d\n", in->port);
}
struct tb_sw_lookup {
......@@ -3202,3 +3277,415 @@ struct tb_port *tb_switch_find_port(struct tb_switch *sw,
return NULL;
}
static int __tb_port_pm_secondary_set(struct tb_port *port, bool secondary)
{
u32 phy;
int ret;
ret = tb_port_read(port, &phy, TB_CFG_PORT,
port->cap_phy + LANE_ADP_CS_1, 1);
if (ret)
return ret;
if (secondary)
phy |= LANE_ADP_CS_1_PMS;
else
phy &= ~LANE_ADP_CS_1_PMS;
return tb_port_write(port, &phy, TB_CFG_PORT,
port->cap_phy + LANE_ADP_CS_1, 1);
}
static int tb_port_pm_secondary_enable(struct tb_port *port)
{
return __tb_port_pm_secondary_set(port, true);
}
static int tb_port_pm_secondary_disable(struct tb_port *port)
{
return __tb_port_pm_secondary_set(port, false);
}
static int tb_switch_pm_secondary_resolve(struct tb_switch *sw)
{
struct tb_switch *parent = tb_switch_parent(sw);
struct tb_port *up, *down;
int ret;
if (!tb_route(sw))
return 0;
up = tb_upstream_port(sw);
down = tb_port_at(tb_route(sw), parent);
ret = tb_port_pm_secondary_enable(up);
if (ret)
return ret;
return tb_port_pm_secondary_disable(down);
}
/* Called for USB4 or Titan Ridge routers only */
static bool tb_port_clx_supported(struct tb_port *port, enum tb_clx clx)
{
u32 mask, val;
bool ret;
/* Don't enable CLx in case of two single-lane links */
if (!port->bonded && port->dual_link_port)
return false;
/* Don't enable CLx in case of inter-domain link */
if (port->xdomain)
return false;
if (tb_switch_is_usb4(port->sw)) {
if (!usb4_port_clx_supported(port))
return false;
} else if (!tb_lc_is_clx_supported(port)) {
return false;
}
switch (clx) {
case TB_CL0S:
/* CL0s support requires also CL1 support */
mask = LANE_ADP_CS_0_CL0S_SUPPORT | LANE_ADP_CS_0_CL1_SUPPORT;
break;
/* For now we support only CL0s. Not CL1, CL2 */
case TB_CL1:
case TB_CL2:
default:
return false;
}
ret = tb_port_read(port, &val, TB_CFG_PORT,
port->cap_phy + LANE_ADP_CS_0, 1);
if (ret)
return false;
return !!(val & mask);
}
static inline bool tb_port_cl0s_supported(struct tb_port *port)
{
return tb_port_clx_supported(port, TB_CL0S);
}
static int __tb_port_cl0s_set(struct tb_port *port, bool enable)
{
u32 phy, mask;
int ret;
/* To enable CL0s also required to enable CL1 */
mask = LANE_ADP_CS_1_CL0S_ENABLE | LANE_ADP_CS_1_CL1_ENABLE;
ret = tb_port_read(port, &phy, TB_CFG_PORT,
port->cap_phy + LANE_ADP_CS_1, 1);
if (ret)
return ret;
if (enable)
phy |= mask;
else
phy &= ~mask;
return tb_port_write(port, &phy, TB_CFG_PORT,
port->cap_phy + LANE_ADP_CS_1, 1);
}
static int tb_port_cl0s_disable(struct tb_port *port)
{
return __tb_port_cl0s_set(port, false);
}
static int tb_port_cl0s_enable(struct tb_port *port)
{
return __tb_port_cl0s_set(port, true);
}
static int tb_switch_enable_cl0s(struct tb_switch *sw)
{
struct tb_switch *parent = tb_switch_parent(sw);
bool up_cl0s_support, down_cl0s_support;
struct tb_port *up, *down;
int ret;
if (!tb_switch_is_clx_supported(sw))
return 0;
/*
* Enable CLx for host router's downstream port as part of the
* downstream router enabling procedure.
*/
if (!tb_route(sw))
return 0;
/* Enable CLx only for first hop router (depth = 1) */
if (tb_route(parent))
return 0;
ret = tb_switch_pm_secondary_resolve(sw);
if (ret)
return ret;
up = tb_upstream_port(sw);
down = tb_port_at(tb_route(sw), parent);
up_cl0s_support = tb_port_cl0s_supported(up);
down_cl0s_support = tb_port_cl0s_supported(down);
tb_port_dbg(up, "CL0s %ssupported\n",
up_cl0s_support ? "" : "not ");
tb_port_dbg(down, "CL0s %ssupported\n",
down_cl0s_support ? "" : "not ");
if (!up_cl0s_support || !down_cl0s_support)
return -EOPNOTSUPP;
ret = tb_port_cl0s_enable(up);
if (ret)
return ret;
ret = tb_port_cl0s_enable(down);
if (ret) {
tb_port_cl0s_disable(up);
return ret;
}
ret = tb_switch_mask_clx_objections(sw);
if (ret) {
tb_port_cl0s_disable(up);
tb_port_cl0s_disable(down);
return ret;
}
sw->clx = TB_CL0S;
tb_port_dbg(up, "CL0s enabled\n");
return 0;
}
/**
* tb_switch_enable_clx() - Enable CLx on upstream port of specified router
* @sw: Router to enable CLx for
* @clx: The CLx state to enable
*
* Enable CLx state only for first hop router. That is the most common
* use-case, that is intended for better thermal management, and so helps
* to improve performance. CLx is enabled only if both sides of the link
* support CLx, and if both sides of the link are not configured as two
* single lane links and only if the link is not inter-domain link. The
* complete set of conditions is descibed in CM Guide 1.0 section 8.1.
*
* Return: Returns 0 on success or an error code on failure.
*/
int tb_switch_enable_clx(struct tb_switch *sw, enum tb_clx clx)
{
struct tb_switch *root_sw = sw->tb->root_switch;
if (!clx_enabled)
return 0;
/*
* CLx is not enabled and validated on Intel USB4 platforms before
* Alder Lake.
*/
if (root_sw->generation < 4 || tb_switch_is_tiger_lake(root_sw))
return 0;
switch (clx) {
case TB_CL0S:
return tb_switch_enable_cl0s(sw);
default:
return -EOPNOTSUPP;
}
}
static int tb_switch_disable_cl0s(struct tb_switch *sw)
{
struct tb_switch *parent = tb_switch_parent(sw);
struct tb_port *up, *down;
int ret;
if (!tb_switch_is_clx_supported(sw))
return 0;
/*
* Disable CLx for host router's downstream port as part of the
* downstream router enabling procedure.
*/
if (!tb_route(sw))
return 0;
/* Disable CLx only for first hop router (depth = 1) */
if (tb_route(parent))
return 0;
up = tb_upstream_port(sw);
down = tb_port_at(tb_route(sw), parent);
ret = tb_port_cl0s_disable(up);
if (ret)
return ret;
ret = tb_port_cl0s_disable(down);
if (ret)
return ret;
sw->clx = TB_CLX_DISABLE;
tb_port_dbg(up, "CL0s disabled\n");
return 0;
}
/**
* tb_switch_disable_clx() - Disable CLx on upstream port of specified router
* @sw: Router to disable CLx for
* @clx: The CLx state to disable
*
* Return: Returns 0 on success or an error code on failure.
*/
int tb_switch_disable_clx(struct tb_switch *sw, enum tb_clx clx)
{
if (!clx_enabled)
return 0;
switch (clx) {
case TB_CL0S:
return tb_switch_disable_cl0s(sw);
default:
return -EOPNOTSUPP;
}
}
/**
* tb_switch_mask_clx_objections() - Mask CLx objections for a router
* @sw: Router to mask objections for
*
* Mask the objections coming from the second depth routers in order to
* stop these objections from interfering with the CLx states of the first
* depth link.
*/
int tb_switch_mask_clx_objections(struct tb_switch *sw)
{
int up_port = sw->config.upstream_port_number;
u32 offset, val[2], mask_obj, unmask_obj;
int ret, i;
/* Only Titan Ridge of pre-USB4 devices support CLx states */
if (!tb_switch_is_titan_ridge(sw))
return 0;
if (!tb_route(sw))
return 0;
/*
* In Titan Ridge there are only 2 dual-lane Thunderbolt ports:
* Port A consists of lane adapters 1,2 and
* Port B consists of lane adapters 3,4
* If upstream port is A, (lanes are 1,2), we mask objections from
* port B (lanes 3,4) and unmask objections from Port A and vice-versa.
*/
if (up_port == 1) {
mask_obj = TB_LOW_PWR_C0_PORT_B_MASK;
unmask_obj = TB_LOW_PWR_C1_PORT_A_MASK;
offset = TB_LOW_PWR_C1_CL1;
} else {
mask_obj = TB_LOW_PWR_C1_PORT_A_MASK;
unmask_obj = TB_LOW_PWR_C0_PORT_B_MASK;
offset = TB_LOW_PWR_C3_CL1;
}
ret = tb_sw_read(sw, &val, TB_CFG_SWITCH,
sw->cap_lp + offset, ARRAY_SIZE(val));
if (ret)
return ret;
for (i = 0; i < ARRAY_SIZE(val); i++) {
val[i] |= mask_obj;
val[i] &= ~unmask_obj;
}
return tb_sw_write(sw, &val, TB_CFG_SWITCH,
sw->cap_lp + offset, ARRAY_SIZE(val));
}
/*
* Can be used for read/write a specified PCIe bridge for any Thunderbolt 3
* device. For now used only for Titan Ridge.
*/
static int tb_switch_pcie_bridge_write(struct tb_switch *sw, unsigned int bridge,
unsigned int pcie_offset, u32 value)
{
u32 offset, command, val;
int ret;
if (sw->generation != 3)
return -EOPNOTSUPP;
offset = sw->cap_plug_events + TB_PLUG_EVENTS_PCIE_WR_DATA;
ret = tb_sw_write(sw, &value, TB_CFG_SWITCH, offset, 1);
if (ret)
return ret;
command = pcie_offset & TB_PLUG_EVENTS_PCIE_CMD_DW_OFFSET_MASK;
command |= BIT(bridge + TB_PLUG_EVENTS_PCIE_CMD_BR_SHIFT);
command |= TB_PLUG_EVENTS_PCIE_CMD_RD_WR_MASK;
command |= TB_PLUG_EVENTS_PCIE_CMD_COMMAND_VAL
<< TB_PLUG_EVENTS_PCIE_CMD_COMMAND_SHIFT;
command |= TB_PLUG_EVENTS_PCIE_CMD_REQ_ACK_MASK;
offset = sw->cap_plug_events + TB_PLUG_EVENTS_PCIE_CMD;
ret = tb_sw_write(sw, &command, TB_CFG_SWITCH, offset, 1);
if (ret)
return ret;
ret = tb_switch_wait_for_bit(sw, offset,
TB_PLUG_EVENTS_PCIE_CMD_REQ_ACK_MASK, 0, 100);
if (ret)
return ret;
ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, offset, 1);
if (ret)
return ret;
if (val & TB_PLUG_EVENTS_PCIE_CMD_TIMEOUT_MASK)
return -ETIMEDOUT;
return 0;
}
/**
* tb_switch_pcie_l1_enable() - Enable PCIe link to enter L1 state
* @sw: Router to enable PCIe L1
*
* For Titan Ridge switch to enter CLx state, its PCIe bridges shall enable
* entry to PCIe L1 state. Shall be called after the upstream PCIe tunnel
* was configured. Due to Intel platforms limitation, shall be called only
* for first hop switch.
*/
int tb_switch_pcie_l1_enable(struct tb_switch *sw)
{
struct tb_switch *parent = tb_switch_parent(sw);
int ret;
if (!tb_route(sw))
return 0;
if (!tb_switch_is_titan_ridge(sw))
return 0;
/* Enable PCIe L1 enable only for first hop router (depth = 1) */
if (tb_route(parent))
return 0;
/* Write to downstream PCIe bridge #5 aka Dn4 */
ret = tb_switch_pcie_bridge_write(sw, 5, 0x143, 0x0c7806b1);
if (ret)
return ret;
/* Write to Upstream PCIe bridge #0 aka Up0 */
return tb_switch_pcie_bridge_write(sw, 0, 0x143, 0x0c5806b1);
}
......@@ -105,10 +105,11 @@ static void tb_remove_dp_resources(struct tb_switch *sw)
}
}
static void tb_discover_tunnels(struct tb_switch *sw)
static void tb_switch_discover_tunnels(struct tb_switch *sw,
struct list_head *list,
bool alloc_hopids)
{
struct tb *tb = sw->tb;
struct tb_cm *tcm = tb_priv(tb);
struct tb_port *port;
tb_switch_for_each_port(sw, port) {
......@@ -116,24 +117,41 @@ static void tb_discover_tunnels(struct tb_switch *sw)
switch (port->config.type) {
case TB_TYPE_DP_HDMI_IN:
tunnel = tb_tunnel_discover_dp(tb, port);
tunnel = tb_tunnel_discover_dp(tb, port, alloc_hopids);
break;
case TB_TYPE_PCIE_DOWN:
tunnel = tb_tunnel_discover_pci(tb, port);
tunnel = tb_tunnel_discover_pci(tb, port, alloc_hopids);
break;
case TB_TYPE_USB3_DOWN:
tunnel = tb_tunnel_discover_usb3(tb, port);
tunnel = tb_tunnel_discover_usb3(tb, port, alloc_hopids);
break;
default:
break;
}
if (!tunnel)
continue;
if (tunnel)
list_add_tail(&tunnel->list, list);
}
tb_switch_for_each_port(sw, port) {
if (tb_port_has_remote(port)) {
tb_switch_discover_tunnels(port->remote->sw, list,
alloc_hopids);
}
}
}
static void tb_discover_tunnels(struct tb *tb)
{
struct tb_cm *tcm = tb_priv(tb);
struct tb_tunnel *tunnel;
tb_switch_discover_tunnels(tb->root_switch, &tcm->tunnel_list, true);
list_for_each_entry(tunnel, &tcm->tunnel_list, list) {
if (tb_tunnel_is_pci(tunnel)) {
struct tb_switch *parent = tunnel->dst_port->sw;
......@@ -146,13 +164,6 @@ static void tb_discover_tunnels(struct tb_switch *sw)
pm_runtime_get_sync(&tunnel->src_port->sw->dev);
pm_runtime_get_sync(&tunnel->dst_port->sw->dev);
}
list_add_tail(&tunnel->list, &tcm->tunnel_list);
}
tb_switch_for_each_port(sw, port) {
if (tb_port_has_remote(port))
tb_discover_tunnels(port->remote->sw);
}
}
......@@ -210,7 +221,7 @@ static int tb_enable_tmu(struct tb_switch *sw)
int ret;
/* If it is already enabled in correct mode, don't touch it */
if (tb_switch_tmu_is_enabled(sw))
if (tb_switch_tmu_hifi_is_enabled(sw, sw->tmu.unidirectional_request))
return 0;
ret = tb_switch_tmu_disable(sw);
......@@ -658,6 +669,11 @@ static void tb_scan_port(struct tb_port *port)
tb_switch_lane_bonding_enable(sw);
/* Set the link configured */
tb_switch_configure_link(sw);
if (tb_switch_enable_clx(sw, TB_CL0S))
tb_sw_warn(sw, "failed to enable CLx on upstream port\n");
tb_switch_tmu_configure(sw, TB_SWITCH_TMU_RATE_HIFI,
tb_switch_is_clx_enabled(sw));
if (tb_enable_tmu(sw))
tb_sw_warn(sw, "failed to enable TMU\n");
......@@ -1076,6 +1092,13 @@ static int tb_tunnel_pci(struct tb *tb, struct tb_switch *sw)
return -EIO;
}
/*
* PCIe L1 is needed to enable CL0s for Titan Ridge so enable it
* here.
*/
if (tb_switch_pcie_l1_enable(sw))
tb_sw_warn(sw, "failed to enable PCIe L1 for Titan Ridge\n");
list_add_tail(&tunnel->list, &tcm->tunnel_list);
return 0;
}
......@@ -1364,12 +1387,13 @@ static int tb_start(struct tb *tb)
return ret;
}
tb_switch_tmu_configure(tb->root_switch, TB_SWITCH_TMU_RATE_HIFI, false);
/* Enable TMU if it is off */
tb_switch_tmu_enable(tb->root_switch);
/* Full scan to discover devices added before the driver was loaded. */
tb_scan_switch(tb->root_switch);
/* Find out tunnels created by the boot firmware */
tb_discover_tunnels(tb->root_switch);
tb_discover_tunnels(tb);
/*
* If the boot firmware did not create USB 3.x tunnels create them
* now for the whole topology.
......@@ -1407,6 +1431,14 @@ static void tb_restore_children(struct tb_switch *sw)
if (sw->is_unplugged)
return;
if (tb_switch_enable_clx(sw, TB_CL0S))
tb_sw_warn(sw, "failed to re-enable CLx on upstream port\n");
/*
* tb_switch_tmu_configure() was already called when the switch was
* added before entering system sleep or runtime suspend,
* so no need to call it again before enabling TMU.
*/
if (tb_enable_tmu(sw))
tb_sw_warn(sw, "failed to restore TMU configuration\n");
......@@ -1429,6 +1461,8 @@ static int tb_resume_noirq(struct tb *tb)
{
struct tb_cm *tcm = tb_priv(tb);
struct tb_tunnel *tunnel, *n;
unsigned int usb3_delay = 0;
LIST_HEAD(tunnels);
tb_dbg(tb, "resuming...\n");
......@@ -1439,8 +1473,31 @@ static int tb_resume_noirq(struct tb *tb)
tb_free_invalid_tunnels(tb);
tb_free_unplugged_children(tb->root_switch);
tb_restore_children(tb->root_switch);
list_for_each_entry_safe(tunnel, n, &tcm->tunnel_list, list)
/*
* If we get here from suspend to disk the boot firmware or the
* restore kernel might have created tunnels of its own. Since
* we cannot be sure they are usable for us we find and tear
* them down.
*/
tb_switch_discover_tunnels(tb->root_switch, &tunnels, false);
list_for_each_entry_safe_reverse(tunnel, n, &tunnels, list) {
if (tb_tunnel_is_usb3(tunnel))
usb3_delay = 500;
tb_tunnel_deactivate(tunnel);
tb_tunnel_free(tunnel);
}
/* Re-create our tunnels now */
list_for_each_entry_safe(tunnel, n, &tcm->tunnel_list, list) {
/* USB3 requires delay before it can be re-activated */
if (tb_tunnel_is_usb3(tunnel)) {
msleep(usb3_delay);
/* Only need to do it once */
usb3_delay = 0;
}
tb_tunnel_restart(tunnel);
}
if (!list_empty(&tcm->tunnel_list)) {
/*
* the pcie links need some time to get going.
......
......@@ -89,15 +89,31 @@ enum tb_switch_tmu_rate {
* @cap: Offset to the TMU capability (%0 if not found)
* @has_ucap: Does the switch support uni-directional mode
* @rate: TMU refresh rate related to upstream switch. In case of root
* switch this holds the domain rate.
* switch this holds the domain rate. Reflects the HW setting.
* @unidirectional: Is the TMU in uni-directional or bi-directional mode
* related to upstream switch. Don't case for root switch.
* related to upstream switch. Don't care for root switch.
* Reflects the HW setting.
* @unidirectional_request: Is the new TMU mode: uni-directional or bi-directional
* that is requested to be set. Related to upstream switch.
* Don't care for root switch.
* @rate_request: TMU new refresh rate related to upstream switch that is
* requested to be set. In case of root switch, this holds
* the new domain rate that is requested to be set.
*/
struct tb_switch_tmu {
int cap;
bool has_ucap;
enum tb_switch_tmu_rate rate;
bool unidirectional;
bool unidirectional_request;
enum tb_switch_tmu_rate rate_request;
};
enum tb_clx {
TB_CLX_DISABLE,
TB_CL0S,
TB_CL1,
TB_CL2,
};
/**
......@@ -122,7 +138,9 @@ struct tb_switch_tmu {
* @link_usb4: Upstream link is USB4
* @generation: Switch Thunderbolt generation
* @cap_plug_events: Offset to the plug events capability (%0 if not found)
* @cap_vsec_tmu: Offset to the TMU vendor specific capability (%0 if not found)
* @cap_lc: Offset to the link controller capability (%0 if not found)
* @cap_lp: Offset to the low power (CLx for TBT) capability (%0 if not found)
* @is_unplugged: The switch is going away
* @drom: DROM of the switch (%NULL if not found)
* @nvm: Pointer to the NVM if the switch has one (%NULL otherwise)
......@@ -148,6 +166,7 @@ struct tb_switch_tmu {
* @min_dp_main_credits: Router preferred minimum number of buffers for DP MAIN
* @max_pcie_credits: Router preferred number of buffers for PCIe
* @max_dma_credits: Router preferred number of buffers for DMA/P2P
* @clx: CLx state on the upstream link of the router
*
* When the switch is being added or removed to the domain (other
* switches) you need to have domain lock held.
......@@ -172,7 +191,9 @@ struct tb_switch {
bool link_usb4;
unsigned int generation;
int cap_plug_events;
int cap_vsec_tmu;
int cap_lc;
int cap_lp;
bool is_unplugged;
u8 *drom;
struct tb_nvm *nvm;
......@@ -196,6 +217,7 @@ struct tb_switch {
unsigned int min_dp_main_credits;
unsigned int max_pcie_credits;
unsigned int max_dma_credits;
enum tb_clx clx;
};
/**
......@@ -354,6 +376,7 @@ enum tb_path_port {
* when deactivating this path
* @hops: Path hops
* @path_length: How many hops the path uses
* @alloc_hopid: Does this path consume port HopID
*
* A path consists of a number of hops (see &struct tb_path_hop). To
* establish a PCIe tunnel two paths have to be created between the two
......@@ -374,6 +397,7 @@ struct tb_path {
bool clear_fc;
struct tb_path_hop *hops;
int path_length;
bool alloc_hopid;
};
/* HopIDs 0-7 are reserved by the Thunderbolt protocol */
......@@ -740,6 +764,8 @@ void tb_switch_remove(struct tb_switch *sw);
void tb_switch_suspend(struct tb_switch *sw, bool runtime);
int tb_switch_resume(struct tb_switch *sw);
int tb_switch_reset(struct tb_switch *sw);
int tb_switch_wait_for_bit(struct tb_switch *sw, u32 offset, u32 bit,
u32 value, int timeout_msec);
void tb_sw_set_unplugged(struct tb_switch *sw);
struct tb_port *tb_switch_find_port(struct tb_switch *sw,
enum tb_port_type type);
......@@ -851,6 +877,20 @@ static inline bool tb_switch_is_titan_ridge(const struct tb_switch *sw)
return false;
}
static inline bool tb_switch_is_tiger_lake(const struct tb_switch *sw)
{
if (sw->config.vendor_id == PCI_VENDOR_ID_INTEL) {
switch (sw->config.device_id) {
case PCI_DEVICE_ID_INTEL_TGL_NHI0:
case PCI_DEVICE_ID_INTEL_TGL_NHI1:
case PCI_DEVICE_ID_INTEL_TGL_H_NHI0:
case PCI_DEVICE_ID_INTEL_TGL_H_NHI1:
return true;
}
}
return false;
}
/**
* tb_switch_is_usb4() - Is the switch USB4 compliant
* @sw: Switch to check
......@@ -889,13 +929,64 @@ int tb_switch_tmu_init(struct tb_switch *sw);
int tb_switch_tmu_post_time(struct tb_switch *sw);
int tb_switch_tmu_disable(struct tb_switch *sw);
int tb_switch_tmu_enable(struct tb_switch *sw);
static inline bool tb_switch_tmu_is_enabled(const struct tb_switch *sw)
void tb_switch_tmu_configure(struct tb_switch *sw,
enum tb_switch_tmu_rate rate,
bool unidirectional);
/**
* tb_switch_tmu_hifi_is_enabled() - Checks if the specified TMU mode is enabled
* @sw: Router whose TMU mode to check
* @unidirectional: If uni-directional (bi-directional otherwise)
*
* Return true if hardware TMU configuration matches the one passed in
* as parameter. That is HiFi and either uni-directional or bi-directional.
*/
static inline bool tb_switch_tmu_hifi_is_enabled(const struct tb_switch *sw,
bool unidirectional)
{
return sw->tmu.rate == TB_SWITCH_TMU_RATE_HIFI &&
!sw->tmu.unidirectional;
sw->tmu.unidirectional == unidirectional;
}
int tb_switch_enable_clx(struct tb_switch *sw, enum tb_clx clx);
int tb_switch_disable_clx(struct tb_switch *sw, enum tb_clx clx);
/**
* tb_switch_is_clx_enabled() - Checks if the CLx is enabled
* @sw: Router to check the CLx state for
*
* Checks if the CLx is enabled on the router upstream link.
* Not applicable for a host router.
*/
static inline bool tb_switch_is_clx_enabled(const struct tb_switch *sw)
{
return sw->clx != TB_CLX_DISABLE;
}
/**
* tb_switch_is_cl0s_enabled() - Checks if the CL0s is enabled
* @sw: Router to check for the CL0s
*
* Checks if the CL0s is enabled on the router upstream link.
* Not applicable for a host router.
*/
static inline bool tb_switch_is_cl0s_enabled(const struct tb_switch *sw)
{
return sw->clx == TB_CL0S;
}
/**
* tb_switch_is_clx_supported() - Is CLx supported on this type of router
* @sw: The router to check CLx support for
*/
static inline bool tb_switch_is_clx_supported(const struct tb_switch *sw)
{
return tb_switch_is_usb4(sw) || tb_switch_is_titan_ridge(sw);
}
int tb_switch_mask_clx_objections(struct tb_switch *sw);
int tb_switch_pcie_l1_enable(struct tb_switch *sw);
int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged);
int tb_port_add_nfc_credits(struct tb_port *port, int credits);
int tb_port_clear_counter(struct tb_port *port, int counter);
......@@ -957,7 +1048,8 @@ int tb_dp_port_enable(struct tb_port *port, bool enable);
struct tb_path *tb_path_discover(struct tb_port *src, int src_hopid,
struct tb_port *dst, int dst_hopid,
struct tb_port **last, const char *name);
struct tb_port **last, const char *name,
bool alloc_hopid);
struct tb_path *tb_path_alloc(struct tb *tb, struct tb_port *src, int src_hopid,
struct tb_port *dst, int dst_hopid, int link_nr,
const char *name);
......@@ -988,6 +1080,7 @@ void tb_lc_unconfigure_port(struct tb_port *port);
int tb_lc_configure_xdomain(struct tb_port *port);
void tb_lc_unconfigure_xdomain(struct tb_port *port);
int tb_lc_start_lane_initialization(struct tb_port *port);
bool tb_lc_is_clx_supported(struct tb_port *port);
int tb_lc_set_wake(struct tb_switch *sw, unsigned int flags);
int tb_lc_set_sleep(struct tb_switch *sw);
bool tb_lc_lane_bonding_possible(struct tb_switch *sw);
......@@ -1074,6 +1167,7 @@ void usb4_port_unconfigure_xdomain(struct tb_port *port);
int usb4_port_router_offline(struct tb_port *port);
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_retimer_set_inbound_sbtx(struct tb_port *port, u8 index);
int usb4_port_retimer_read(struct tb_port *port, u8 index, u8 reg, void *buf,
......
......@@ -535,15 +535,25 @@ struct tb_xdp_header {
u32 type;
};
struct tb_xdp_error_response {
struct tb_xdp_header hdr;
u32 error;
};
struct tb_xdp_uuid {
struct tb_xdp_header hdr;
};
struct tb_xdp_uuid_response {
struct tb_xdp_header hdr;
uuid_t src_uuid;
u32 src_route_hi;
u32 src_route_lo;
union {
struct tb_xdp_error_response err;
struct {
struct tb_xdp_header hdr;
uuid_t src_uuid;
u32 src_route_hi;
u32 src_route_lo;
};
};
};
struct tb_xdp_properties {
......@@ -555,13 +565,18 @@ struct tb_xdp_properties {
};
struct tb_xdp_properties_response {
struct tb_xdp_header hdr;
uuid_t src_uuid;
uuid_t dst_uuid;
u16 offset;
u16 data_length;
u32 generation;
u32 data[0];
union {
struct tb_xdp_error_response err;
struct {
struct tb_xdp_header hdr;
uuid_t src_uuid;
uuid_t dst_uuid;
u16 offset;
u16 data_length;
u32 generation;
u32 data[];
};
};
};
/*
......@@ -580,7 +595,10 @@ struct tb_xdp_properties_changed {
};
struct tb_xdp_properties_changed_response {
struct tb_xdp_header hdr;
union {
struct tb_xdp_error_response err;
struct tb_xdp_header hdr;
};
};
enum tb_xdp_error {
......@@ -591,9 +609,4 @@ enum tb_xdp_error {
ERROR_NOT_READY,
};
struct tb_xdp_error_response {
struct tb_xdp_header hdr;
u32 error;
};
#endif
......@@ -33,7 +33,7 @@ enum tb_switch_cap {
enum tb_switch_vse_cap {
TB_VSE_CAP_PLUG_EVENTS = 0x01, /* also EEPROM */
TB_VSE_CAP_TIME2 = 0x03,
TB_VSE_CAP_IECS = 0x04,
TB_VSE_CAP_CP_LP = 0x04,
TB_VSE_CAP_LINK_CONTROLLER = 0x06, /* also IECS */
};
......@@ -246,6 +246,7 @@ enum usb4_switch_op {
#define TMU_RTR_CS_3_TS_PACKET_INTERVAL_SHIFT 16
#define TMU_RTR_CS_22 0x16
#define TMU_RTR_CS_24 0x18
#define TMU_RTR_CS_25 0x19
enum tb_port_type {
TB_TYPE_INACTIVE = 0x000000,
......@@ -305,16 +306,22 @@ struct tb_regs_port_header {
/* TMU adapter registers */
#define TMU_ADP_CS_3 0x03
#define TMU_ADP_CS_3_UDM BIT(29)
#define TMU_ADP_CS_6 0x06
#define TMU_ADP_CS_6_DTS BIT(1)
/* Lane adapter registers */
#define LANE_ADP_CS_0 0x00
#define LANE_ADP_CS_0_SUPPORTED_WIDTH_MASK GENMASK(25, 20)
#define LANE_ADP_CS_0_SUPPORTED_WIDTH_SHIFT 20
#define LANE_ADP_CS_0_CL0S_SUPPORT BIT(26)
#define LANE_ADP_CS_0_CL1_SUPPORT BIT(27)
#define LANE_ADP_CS_1 0x01
#define LANE_ADP_CS_1_TARGET_WIDTH_MASK GENMASK(9, 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_CL0S_ENABLE BIT(10)
#define LANE_ADP_CS_1_CL1_ENABLE BIT(11)
#define LANE_ADP_CS_1_LD BIT(14)
#define LANE_ADP_CS_1_LB BIT(15)
#define LANE_ADP_CS_1_CURRENT_SPEED_MASK GENMASK(19, 16)
......@@ -323,6 +330,7 @@ struct tb_regs_port_header {
#define LANE_ADP_CS_1_CURRENT_SPEED_GEN3 0x4
#define LANE_ADP_CS_1_CURRENT_WIDTH_MASK GENMASK(25, 20)
#define LANE_ADP_CS_1_CURRENT_WIDTH_SHIFT 20
#define LANE_ADP_CS_1_PMS BIT(30)
/* USB4 port registers */
#define PORT_CS_1 0x01
......@@ -338,6 +346,7 @@ struct tb_regs_port_header {
#define PORT_CS_18 0x12
#define PORT_CS_18_BE BIT(8)
#define PORT_CS_18_TCM BIT(9)
#define PORT_CS_18_CPS BIT(10)
#define PORT_CS_18_WOU4S BIT(18)
#define PORT_CS_19 0x13
#define PORT_CS_19_PC BIT(3)
......@@ -437,39 +446,79 @@ struct tb_regs_hop {
u32 unknown3:3; /* set to zero */
} __packed;
/* TMU Thunderbolt 3 registers */
#define TB_TIME_VSEC_3_CS_9 0x9
#define TB_TIME_VSEC_3_CS_9_TMU_OBJ_MASK GENMASK(17, 16)
#define TB_TIME_VSEC_3_CS_26 0x1a
#define TB_TIME_VSEC_3_CS_26_TD BIT(22)
/*
* Used for Titan Ridge only. Bits are part of the same register: TMU_ADP_CS_6
* (see above) as in USB4 spec, but these specific bits used for Titan Ridge
* only and reserved in USB4 spec.
*/
#define TMU_ADP_CS_6_DISABLE_TMU_OBJ_MASK GENMASK(3, 2)
#define TMU_ADP_CS_6_DISABLE_TMU_OBJ_CL1 BIT(2)
#define TMU_ADP_CS_6_DISABLE_TMU_OBJ_CL2 BIT(3)
/* Plug Events registers */
#define TB_PLUG_EVENTS_PCIE_WR_DATA 0x1b
#define TB_PLUG_EVENTS_PCIE_CMD 0x1c
#define TB_PLUG_EVENTS_PCIE_CMD_DW_OFFSET_MASK GENMASK(9, 0)
#define TB_PLUG_EVENTS_PCIE_CMD_BR_SHIFT 10
#define TB_PLUG_EVENTS_PCIE_CMD_BR_MASK GENMASK(17, 10)
#define TB_PLUG_EVENTS_PCIE_CMD_RD_WR_MASK BIT(21)
#define TB_PLUG_EVENTS_PCIE_CMD_WR 0x1
#define TB_PLUG_EVENTS_PCIE_CMD_COMMAND_SHIFT 22
#define TB_PLUG_EVENTS_PCIE_CMD_COMMAND_MASK GENMASK(24, 22)
#define TB_PLUG_EVENTS_PCIE_CMD_COMMAND_VAL 0x2
#define TB_PLUG_EVENTS_PCIE_CMD_REQ_ACK_MASK BIT(30)
#define TB_PLUG_EVENTS_PCIE_CMD_TIMEOUT_MASK BIT(31)
#define TB_PLUG_EVENTS_PCIE_CMD_RD_DATA 0x1d
/* CP Low Power registers */
#define TB_LOW_PWR_C1_CL1 0x1
#define TB_LOW_PWR_C1_CL1_OBJ_MASK GENMASK(4, 1)
#define TB_LOW_PWR_C1_CL2_OBJ_MASK GENMASK(4, 1)
#define TB_LOW_PWR_C1_PORT_A_MASK GENMASK(2, 1)
#define TB_LOW_PWR_C0_PORT_B_MASK GENMASK(4, 3)
#define TB_LOW_PWR_C3_CL1 0x3
/* Common link controller registers */
#define TB_LC_DESC 0x02
#define TB_LC_DESC_NLC_MASK GENMASK(3, 0)
#define TB_LC_DESC_SIZE_SHIFT 8
#define TB_LC_DESC_SIZE_MASK GENMASK(15, 8)
#define TB_LC_DESC_PORT_SIZE_SHIFT 16
#define TB_LC_DESC_PORT_SIZE_MASK GENMASK(27, 16)
#define TB_LC_FUSE 0x03
#define TB_LC_SNK_ALLOCATION 0x10
#define TB_LC_SNK_ALLOCATION_SNK0_MASK GENMASK(3, 0)
#define TB_LC_SNK_ALLOCATION_SNK0_CM 0x1
#define TB_LC_SNK_ALLOCATION_SNK1_SHIFT 4
#define TB_LC_SNK_ALLOCATION_SNK1_MASK GENMASK(7, 4)
#define TB_LC_SNK_ALLOCATION_SNK1_CM 0x1
#define TB_LC_POWER 0x740
#define TB_LC_DESC 0x02
#define TB_LC_DESC_NLC_MASK GENMASK(3, 0)
#define TB_LC_DESC_SIZE_SHIFT 8
#define TB_LC_DESC_SIZE_MASK GENMASK(15, 8)
#define TB_LC_DESC_PORT_SIZE_SHIFT 16
#define TB_LC_DESC_PORT_SIZE_MASK GENMASK(27, 16)
#define TB_LC_FUSE 0x03
#define TB_LC_SNK_ALLOCATION 0x10
#define TB_LC_SNK_ALLOCATION_SNK0_MASK GENMASK(3, 0)
#define TB_LC_SNK_ALLOCATION_SNK0_CM 0x1
#define TB_LC_SNK_ALLOCATION_SNK1_SHIFT 4
#define TB_LC_SNK_ALLOCATION_SNK1_MASK GENMASK(7, 4)
#define TB_LC_SNK_ALLOCATION_SNK1_CM 0x1
#define TB_LC_POWER 0x740
/* Link controller registers */
#define TB_LC_PORT_ATTR 0x8d
#define TB_LC_PORT_ATTR_BE BIT(12)
#define TB_LC_SX_CTRL 0x96
#define TB_LC_SX_CTRL_WOC BIT(1)
#define TB_LC_SX_CTRL_WOD BIT(2)
#define TB_LC_SX_CTRL_WODPC BIT(3)
#define TB_LC_SX_CTRL_WODPD BIT(4)
#define TB_LC_SX_CTRL_WOU4 BIT(5)
#define TB_LC_SX_CTRL_WOP BIT(6)
#define TB_LC_SX_CTRL_L1C BIT(16)
#define TB_LC_SX_CTRL_L1D BIT(17)
#define TB_LC_SX_CTRL_L2C BIT(20)
#define TB_LC_SX_CTRL_L2D BIT(21)
#define TB_LC_SX_CTRL_SLI BIT(29)
#define TB_LC_SX_CTRL_UPSTREAM BIT(30)
#define TB_LC_SX_CTRL_SLP BIT(31)
#define TB_LC_PORT_ATTR 0x8d
#define TB_LC_PORT_ATTR_BE BIT(12)
#define TB_LC_SX_CTRL 0x96
#define TB_LC_SX_CTRL_WOC BIT(1)
#define TB_LC_SX_CTRL_WOD BIT(2)
#define TB_LC_SX_CTRL_WODPC BIT(3)
#define TB_LC_SX_CTRL_WODPD BIT(4)
#define TB_LC_SX_CTRL_WOU4 BIT(5)
#define TB_LC_SX_CTRL_WOP BIT(6)
#define TB_LC_SX_CTRL_L1C BIT(16)
#define TB_LC_SX_CTRL_L1D BIT(17)
#define TB_LC_SX_CTRL_L2C BIT(20)
#define TB_LC_SX_CTRL_L2D BIT(21)
#define TB_LC_SX_CTRL_SLI BIT(29)
#define TB_LC_SX_CTRL_UPSTREAM BIT(30)
#define TB_LC_SX_CTRL_SLP BIT(31)
#define TB_LC_LINK_ATTR 0x97
#define TB_LC_LINK_ATTR_CPS BIT(18)
#endif
......@@ -115,6 +115,11 @@ static inline int tb_port_tmu_unidirectional_disable(struct tb_port *port)
return tb_port_tmu_set_unidirectional(port, false);
}
static inline int tb_port_tmu_unidirectional_enable(struct tb_port *port)
{
return tb_port_tmu_set_unidirectional(port, true);
}
static bool tb_port_tmu_is_unidirectional(struct tb_port *port)
{
int ret;
......@@ -128,23 +133,46 @@ static bool tb_port_tmu_is_unidirectional(struct tb_port *port)
return val & TMU_ADP_CS_3_UDM;
}
static int tb_port_tmu_time_sync(struct tb_port *port, bool time_sync)
{
u32 val = time_sync ? TMU_ADP_CS_6_DTS : 0;
return tb_port_tmu_write(port, TMU_ADP_CS_6, TMU_ADP_CS_6_DTS, val);
}
static int tb_port_tmu_time_sync_disable(struct tb_port *port)
{
return tb_port_tmu_time_sync(port, true);
}
static int tb_port_tmu_time_sync_enable(struct tb_port *port)
{
return tb_port_tmu_time_sync(port, false);
}
static int tb_switch_tmu_set_time_disruption(struct tb_switch *sw, bool set)
{
u32 val, offset, bit;
int ret;
u32 val;
ret = tb_sw_read(sw, &val, TB_CFG_SWITCH,
sw->tmu.cap + TMU_RTR_CS_0, 1);
if (tb_switch_is_usb4(sw)) {
offset = sw->tmu.cap + TMU_RTR_CS_0;
bit = TMU_RTR_CS_0_TD;
} else {
offset = sw->cap_vsec_tmu + TB_TIME_VSEC_3_CS_26;
bit = TB_TIME_VSEC_3_CS_26_TD;
}
ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, offset, 1);
if (ret)
return ret;
if (set)
val |= TMU_RTR_CS_0_TD;
val |= bit;
else
val &= ~TMU_RTR_CS_0_TD;
val &= ~bit;
return tb_sw_write(sw, &val, TB_CFG_SWITCH,
sw->tmu.cap + TMU_RTR_CS_0, 1);
return tb_sw_write(sw, &val, TB_CFG_SWITCH, offset, 1);
}
/**
......@@ -207,7 +235,8 @@ int tb_switch_tmu_init(struct tb_switch *sw)
*/
int tb_switch_tmu_post_time(struct tb_switch *sw)
{
unsigned int post_local_time_offset, post_time_offset;
unsigned int post_time_high_offset, post_time_high = 0;
unsigned int post_local_time_offset, post_time_offset;
struct tb_switch *root_switch = sw->tb->root_switch;
u64 hi, mid, lo, local_time, post_time;
int i, ret, retries = 100;
......@@ -247,6 +276,7 @@ int tb_switch_tmu_post_time(struct tb_switch *sw)
post_local_time_offset = sw->tmu.cap + TMU_RTR_CS_22;
post_time_offset = sw->tmu.cap + TMU_RTR_CS_24;
post_time_high_offset = sw->tmu.cap + TMU_RTR_CS_25;
/*
* Write the Grandmaster time to the Post Local Time registers
......@@ -258,17 +288,24 @@ int tb_switch_tmu_post_time(struct tb_switch *sw)
goto out;
/*
* Have the new switch update its local time (by writing 1 to
* the post_time registers) and wait for the completion of the
* same (post_time register becomes 0). This means the time has
* been converged properly.
* Have the new switch update its local time by:
* 1) writing 0x1 to the Post Time Low register and 0xffffffff to
* Post Time High register.
* 2) write 0 to Post Time High register and then wait for
* the completion of the post_time register becomes 0.
* This means the time has been converged properly.
*/
post_time = 1;
post_time = 0xffffffff00000001ULL;
ret = tb_sw_write(sw, &post_time, TB_CFG_SWITCH, post_time_offset, 2);
if (ret)
goto out;
ret = tb_sw_write(sw, &post_time_high, TB_CFG_SWITCH,
post_time_high_offset, 1);
if (ret)
goto out;
do {
usleep_range(5, 10);
ret = tb_sw_read(sw, &post_time, TB_CFG_SWITCH,
......@@ -297,30 +334,54 @@ int tb_switch_tmu_post_time(struct tb_switch *sw)
*/
int tb_switch_tmu_disable(struct tb_switch *sw)
{
int ret;
if (!tb_switch_is_usb4(sw))
/*
* No need to disable TMU on devices that don't support CLx since
* on these devices e.g. Alpine Ridge and earlier, the TMU mode
* HiFi bi-directional is enabled by default and we don't change it.
*/
if (!tb_switch_is_clx_supported(sw))
return 0;
/* Already disabled? */
if (sw->tmu.rate == TB_SWITCH_TMU_RATE_OFF)
return 0;
if (sw->tmu.unidirectional) {
if (tb_route(sw)) {
bool unidirectional = tb_switch_tmu_hifi_is_enabled(sw, true);
struct tb_switch *parent = tb_switch_parent(sw);
struct tb_port *up, *down;
struct tb_port *down, *up;
int ret;
up = tb_upstream_port(sw);
down = tb_port_at(tb_route(sw), parent);
/* The switch may be unplugged so ignore any errors */
tb_port_tmu_unidirectional_disable(up);
ret = tb_port_tmu_unidirectional_disable(down);
up = tb_upstream_port(sw);
/*
* In case of uni-directional time sync, TMU handshake is
* initiated by upstream router. In case of bi-directional
* time sync, TMU handshake is initiated by downstream router.
* Therefore, we change the rate to off in the respective
* router.
*/
if (unidirectional)
tb_switch_tmu_rate_write(parent, TB_SWITCH_TMU_RATE_OFF);
else
tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_OFF);
tb_port_tmu_time_sync_disable(up);
ret = tb_port_tmu_time_sync_disable(down);
if (ret)
return ret;
}
tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_OFF);
if (unidirectional) {
/* The switch may be unplugged so ignore any errors */
tb_port_tmu_unidirectional_disable(up);
ret = tb_port_tmu_unidirectional_disable(down);
if (ret)
return ret;
}
} else {
tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_OFF);
}
sw->tmu.unidirectional = false;
sw->tmu.rate = TB_SWITCH_TMU_RATE_OFF;
......@@ -329,55 +390,231 @@ int tb_switch_tmu_disable(struct tb_switch *sw)
return 0;
}
/**
* tb_switch_tmu_enable() - Enable TMU on a switch
* @sw: Switch whose TMU to enable
*
* Enables TMU of a switch to be in bi-directional, HiFi mode. In this mode
* all tunneling should work.
static void __tb_switch_tmu_off(struct tb_switch *sw, bool unidirectional)
{
struct tb_switch *parent = tb_switch_parent(sw);
struct tb_port *down, *up;
down = tb_port_at(tb_route(sw), parent);
up = tb_upstream_port(sw);
/*
* In case of any failure in one of the steps when setting
* bi-directional or uni-directional TMU mode, get back to the TMU
* configurations in off mode. In case of additional failures in
* the functions below, ignore them since the caller shall already
* report a failure.
*/
tb_port_tmu_time_sync_disable(down);
tb_port_tmu_time_sync_disable(up);
if (unidirectional)
tb_switch_tmu_rate_write(parent, TB_SWITCH_TMU_RATE_OFF);
else
tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_OFF);
tb_port_tmu_unidirectional_disable(down);
tb_port_tmu_unidirectional_disable(up);
}
/*
* This function is called when the previous TMU mode was
* TB_SWITCH_TMU_RATE_OFF.
*/
int tb_switch_tmu_enable(struct tb_switch *sw)
static int __tb_switch_tmu_enable_bidirectional(struct tb_switch *sw)
{
struct tb_switch *parent = tb_switch_parent(sw);
struct tb_port *up, *down;
int ret;
if (!tb_switch_is_usb4(sw))
return 0;
up = tb_upstream_port(sw);
down = tb_port_at(tb_route(sw), parent);
if (tb_switch_tmu_is_enabled(sw))
return 0;
ret = tb_port_tmu_unidirectional_disable(up);
if (ret)
return ret;
ret = tb_switch_tmu_set_time_disruption(sw, true);
ret = tb_port_tmu_unidirectional_disable(down);
if (ret)
goto out;
ret = tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_HIFI);
if (ret)
goto out;
ret = tb_port_tmu_time_sync_enable(up);
if (ret)
goto out;
ret = tb_port_tmu_time_sync_enable(down);
if (ret)
goto out;
return 0;
out:
__tb_switch_tmu_off(sw, false);
return ret;
}
static int tb_switch_tmu_objection_mask(struct tb_switch *sw)
{
u32 val;
int ret;
ret = tb_sw_read(sw, &val, TB_CFG_SWITCH,
sw->cap_vsec_tmu + TB_TIME_VSEC_3_CS_9, 1);
if (ret)
return ret;
/* Change mode to bi-directional */
if (tb_route(sw) && sw->tmu.unidirectional) {
struct tb_switch *parent = tb_switch_parent(sw);
struct tb_port *up, *down;
val &= ~TB_TIME_VSEC_3_CS_9_TMU_OBJ_MASK;
up = tb_upstream_port(sw);
down = tb_port_at(tb_route(sw), parent);
return tb_sw_write(sw, &val, TB_CFG_SWITCH,
sw->cap_vsec_tmu + TB_TIME_VSEC_3_CS_9, 1);
}
ret = tb_port_tmu_unidirectional_disable(down);
if (ret)
return ret;
static int tb_switch_tmu_unidirectional_enable(struct tb_switch *sw)
{
struct tb_port *up = tb_upstream_port(sw);
ret = tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_HIFI);
return tb_port_tmu_write(up, TMU_ADP_CS_6,
TMU_ADP_CS_6_DISABLE_TMU_OBJ_MASK,
TMU_ADP_CS_6_DISABLE_TMU_OBJ_MASK);
}
/*
* This function is called when the previous TMU mode was
* TB_SWITCH_TMU_RATE_OFF.
*/
static int __tb_switch_tmu_enable_unidirectional(struct tb_switch *sw)
{
struct tb_switch *parent = tb_switch_parent(sw);
struct tb_port *up, *down;
int ret;
up = tb_upstream_port(sw);
down = tb_port_at(tb_route(sw), parent);
ret = tb_switch_tmu_rate_write(parent, TB_SWITCH_TMU_RATE_HIFI);
if (ret)
return ret;
ret = tb_port_tmu_unidirectional_enable(up);
if (ret)
goto out;
ret = tb_port_tmu_time_sync_enable(up);
if (ret)
goto out;
ret = tb_port_tmu_unidirectional_enable(down);
if (ret)
goto out;
ret = tb_port_tmu_time_sync_enable(down);
if (ret)
goto out;
return 0;
out:
__tb_switch_tmu_off(sw, true);
return ret;
}
static int tb_switch_tmu_hifi_enable(struct tb_switch *sw)
{
bool unidirectional = sw->tmu.unidirectional_request;
int ret;
if (unidirectional && !sw->tmu.has_ucap)
return -EOPNOTSUPP;
/*
* No need to enable TMU on devices that don't support CLx since on
* these devices e.g. Alpine Ridge and earlier, the TMU mode HiFi
* bi-directional is enabled by default.
*/
if (!tb_switch_is_clx_supported(sw))
return 0;
if (tb_switch_tmu_hifi_is_enabled(sw, sw->tmu.unidirectional_request))
return 0;
if (tb_switch_is_titan_ridge(sw) && unidirectional) {
/* Titan Ridge supports only CL0s */
if (!tb_switch_is_cl0s_enabled(sw))
return -EOPNOTSUPP;
ret = tb_switch_tmu_objection_mask(sw);
if (ret)
return ret;
ret = tb_port_tmu_unidirectional_disable(up);
ret = tb_switch_tmu_unidirectional_enable(sw);
if (ret)
return ret;
}
ret = tb_switch_tmu_set_time_disruption(sw, true);
if (ret)
return ret;
if (tb_route(sw)) {
/* The used mode changes are from OFF to HiFi-Uni/HiFi-BiDir */
if (sw->tmu.rate == TB_SWITCH_TMU_RATE_OFF) {
if (unidirectional)
ret = __tb_switch_tmu_enable_unidirectional(sw);
else
ret = __tb_switch_tmu_enable_bidirectional(sw);
if (ret)
return ret;
}
sw->tmu.unidirectional = unidirectional;
} else {
/*
* Host router port configurations are written as
* part of configurations for downstream port of the parent
* of the child node - see above.
* Here only the host router' rate configuration is written.
*/
ret = tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_HIFI);
if (ret)
return ret;
}
sw->tmu.unidirectional = false;
sw->tmu.rate = TB_SWITCH_TMU_RATE_HIFI;
tb_sw_dbg(sw, "TMU: mode set to: %s\n", tb_switch_tmu_mode_name(sw));
tb_sw_dbg(sw, "TMU: mode set to: %s\n", tb_switch_tmu_mode_name(sw));
return tb_switch_tmu_set_time_disruption(sw, false);
}
/**
* tb_switch_tmu_enable() - Enable TMU on a router
* @sw: Router whose TMU to enable
*
* Enables TMU of a router to be in uni-directional or bi-directional HiFi mode.
* Calling tb_switch_tmu_configure() is required before calling this function,
* to select the mode HiFi and directionality (uni-directional/bi-directional).
* In both modes all tunneling should work. Uni-directional mode is required for
* CLx (Link Low-Power) to work.
*/
int tb_switch_tmu_enable(struct tb_switch *sw)
{
if (sw->tmu.rate_request == TB_SWITCH_TMU_RATE_NORMAL)
return -EOPNOTSUPP;
return tb_switch_tmu_hifi_enable(sw);
}
/**
* tb_switch_tmu_configure() - Configure the TMU rate and directionality
* @sw: Router whose mode to change
* @rate: Rate to configure Off/LowRes/HiFi
* @unidirectional: If uni-directional (bi-directional otherwise)
*
* Selects the rate of the TMU and directionality (uni-directional or
* bi-directional). Must be called before tb_switch_tmu_enable().
*/
void tb_switch_tmu_configure(struct tb_switch *sw,
enum tb_switch_tmu_rate rate, bool unidirectional)
{
sw->tmu.unidirectional_request = unidirectional;
sw->tmu.rate_request = rate;
}
......@@ -207,12 +207,14 @@ static int tb_pci_init_path(struct tb_path *path)
* tb_tunnel_discover_pci() - Discover existing PCIe tunnels
* @tb: Pointer to the domain structure
* @down: PCIe downstream adapter
* @alloc_hopid: Allocate HopIDs from visited ports
*
* If @down adapter is active, follows the tunnel to the PCIe upstream
* adapter and back. Returns the discovered tunnel or %NULL if there was
* no tunnel.
*/
struct tb_tunnel *tb_tunnel_discover_pci(struct tb *tb, struct tb_port *down)
struct tb_tunnel *tb_tunnel_discover_pci(struct tb *tb, struct tb_port *down,
bool alloc_hopid)
{
struct tb_tunnel *tunnel;
struct tb_path *path;
......@@ -233,7 +235,7 @@ struct tb_tunnel *tb_tunnel_discover_pci(struct tb *tb, struct tb_port *down)
* case.
*/
path = tb_path_discover(down, TB_PCI_HOPID, NULL, -1,
&tunnel->dst_port, "PCIe Up");
&tunnel->dst_port, "PCIe Up", alloc_hopid);
if (!path) {
/* Just disable the downstream port */
tb_pci_port_enable(down, false);
......@@ -244,7 +246,7 @@ struct tb_tunnel *tb_tunnel_discover_pci(struct tb *tb, struct tb_port *down)
goto err_free;
path = tb_path_discover(tunnel->dst_port, -1, down, TB_PCI_HOPID, NULL,
"PCIe Down");
"PCIe Down", alloc_hopid);
if (!path)
goto err_deactivate;
tunnel->paths[TB_PCI_PATH_DOWN] = path;
......@@ -761,6 +763,7 @@ static int tb_dp_init_video_path(struct tb_path *path)
* tb_tunnel_discover_dp() - Discover existing Display Port tunnels
* @tb: Pointer to the domain structure
* @in: DP in adapter
* @alloc_hopid: Allocate HopIDs from visited ports
*
* If @in adapter is active, follows the tunnel to the DP out adapter
* and back. Returns the discovered tunnel or %NULL if there was no
......@@ -768,7 +771,8 @@ static int tb_dp_init_video_path(struct tb_path *path)
*
* Return: DP tunnel or %NULL if no tunnel found.
*/
struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in)
struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in,
bool alloc_hopid)
{
struct tb_tunnel *tunnel;
struct tb_port *port;
......@@ -787,7 +791,7 @@ struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in)
tunnel->src_port = in;
path = tb_path_discover(in, TB_DP_VIDEO_HOPID, NULL, -1,
&tunnel->dst_port, "Video");
&tunnel->dst_port, "Video", alloc_hopid);
if (!path) {
/* Just disable the DP IN port */
tb_dp_port_enable(in, false);
......@@ -797,14 +801,15 @@ struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in)
if (tb_dp_init_video_path(tunnel->paths[TB_DP_VIDEO_PATH_OUT]))
goto err_free;
path = tb_path_discover(in, TB_DP_AUX_TX_HOPID, NULL, -1, NULL, "AUX TX");
path = tb_path_discover(in, TB_DP_AUX_TX_HOPID, NULL, -1, NULL, "AUX TX",
alloc_hopid);
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]);
path = tb_path_discover(tunnel->dst_port, -1, in, TB_DP_AUX_RX_HOPID,
&port, "AUX RX");
&port, "AUX RX", alloc_hopid);
if (!path)
goto err_deactivate;
tunnel->paths[TB_DP_AUX_PATH_IN] = path;
......@@ -1343,12 +1348,14 @@ static void tb_usb3_init_path(struct tb_path *path)
* tb_tunnel_discover_usb3() - Discover existing USB3 tunnels
* @tb: Pointer to the domain structure
* @down: USB3 downstream adapter
* @alloc_hopid: Allocate HopIDs from visited ports
*
* If @down adapter is active, follows the tunnel to the USB3 upstream
* adapter and back. Returns the discovered tunnel or %NULL if there was
* no tunnel.
*/
struct tb_tunnel *tb_tunnel_discover_usb3(struct tb *tb, struct tb_port *down)
struct tb_tunnel *tb_tunnel_discover_usb3(struct tb *tb, struct tb_port *down,
bool alloc_hopid)
{
struct tb_tunnel *tunnel;
struct tb_path *path;
......@@ -1369,7 +1376,7 @@ struct tb_tunnel *tb_tunnel_discover_usb3(struct tb *tb, struct tb_port *down)
* case.
*/
path = tb_path_discover(down, TB_USB3_HOPID, NULL, -1,
&tunnel->dst_port, "USB3 Down");
&tunnel->dst_port, "USB3 Down", alloc_hopid);
if (!path) {
/* Just disable the downstream port */
tb_usb3_port_enable(down, false);
......@@ -1379,7 +1386,7 @@ struct tb_tunnel *tb_tunnel_discover_usb3(struct tb *tb, struct tb_port *down)
tb_usb3_init_path(tunnel->paths[TB_USB3_PATH_DOWN]);
path = tb_path_discover(tunnel->dst_port, -1, down, TB_USB3_HOPID, NULL,
"USB3 Up");
"USB3 Up", alloc_hopid);
if (!path)
goto err_deactivate;
tunnel->paths[TB_USB3_PATH_UP] = path;
......
......@@ -64,10 +64,12 @@ struct tb_tunnel {
int allocated_down;
};
struct tb_tunnel *tb_tunnel_discover_pci(struct tb *tb, struct tb_port *down);
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);
struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in);
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,
struct tb_port *out, int max_up,
int max_down);
......@@ -77,7 +79,8 @@ struct tb_tunnel *tb_tunnel_alloc_dma(struct tb *tb, struct tb_port *nhi,
int receive_ring);
bool tb_tunnel_match_dma(const struct tb_tunnel *tunnel, int transmit_path,
int transmit_ring, int receive_path, int receive_ring);
struct tb_tunnel *tb_tunnel_discover_usb3(struct tb *tb, struct tb_port *down);
struct tb_tunnel *tb_tunnel_discover_usb3(struct tb *tb, struct tb_port *down,
bool alloc_hopid);
struct tb_tunnel *tb_tunnel_alloc_usb3(struct tb *tb, struct tb_port *up,
struct tb_port *down, int max_up,
int max_down);
......
......@@ -50,28 +50,6 @@ enum usb4_ba_index {
#define USB4_BA_VALUE_MASK GENMASK(31, 16)
#define USB4_BA_VALUE_SHIFT 16
static int usb4_switch_wait_for_bit(struct tb_switch *sw, u32 offset, u32 bit,
u32 value, int timeout_msec)
{
ktime_t timeout = ktime_add_ms(ktime_get(), timeout_msec);
do {
u32 val;
int ret;
ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, offset, 1);
if (ret)
return ret;
if ((val & bit) == value)
return 0;
usleep_range(50, 100);
} while (ktime_before(ktime_get(), timeout));
return -ETIMEDOUT;
}
static int usb4_native_switch_op(struct tb_switch *sw, u16 opcode,
u32 *metadata, u8 *status,
const void *tx_data, size_t tx_dwords,
......@@ -97,7 +75,7 @@ static int usb4_native_switch_op(struct tb_switch *sw, u16 opcode,
if (ret)
return ret;
ret = usb4_switch_wait_for_bit(sw, ROUTER_CS_26, ROUTER_CS_26_OV, 0, 500);
ret = tb_switch_wait_for_bit(sw, ROUTER_CS_26, ROUTER_CS_26_OV, 0, 500);
if (ret)
return ret;
......@@ -303,8 +281,8 @@ int usb4_switch_setup(struct tb_switch *sw)
if (ret)
return ret;
return usb4_switch_wait_for_bit(sw, ROUTER_CS_6, ROUTER_CS_6_CR,
ROUTER_CS_6_CR, 50);
return tb_switch_wait_for_bit(sw, ROUTER_CS_6, ROUTER_CS_6_CR,
ROUTER_CS_6_CR, 50);
}
/**
......@@ -480,8 +458,8 @@ int usb4_switch_set_sleep(struct tb_switch *sw)
if (ret)
return ret;
return usb4_switch_wait_for_bit(sw, ROUTER_CS_6, ROUTER_CS_6_SLPR,
ROUTER_CS_6_SLPR, 500);
return tb_switch_wait_for_bit(sw, ROUTER_CS_6, ROUTER_CS_6_SLPR,
ROUTER_CS_6_SLPR, 500);
}
/**
......@@ -1386,6 +1364,26 @@ int usb4_port_enumerate_retimers(struct tb_port *port)
USB4_SB_OPCODE, &val, sizeof(val));
}
/**
* usb4_port_clx_supported() - Check if CLx is supported by the link
* @port: Port to check for CLx support for
*
* PORT_CS_18_CPS bit reflects if the link supports CLx including
* active cables (if connected on the link).
*/
bool usb4_port_clx_supported(struct tb_port *port)
{
int ret;
u32 val;
ret = tb_port_read(port, &val, TB_CFG_PORT,
port->cap_usb4 + PORT_CS_18, 1);
if (ret)
return false;
return !!(val & PORT_CS_18_CPS);
}
static inline int usb4_port_retimer_op(struct tb_port *port, u8 index,
enum usb4_sb_opcode opcode,
int timeout_msec)
......
......@@ -214,16 +214,12 @@ static inline void tb_xdp_fill_header(struct tb_xdp_header *hdr, u64 route,
memcpy(&hdr->uuid, &tb_xdp_uuid, sizeof(tb_xdp_uuid));
}
static int tb_xdp_handle_error(const struct tb_xdp_header *hdr)
static int tb_xdp_handle_error(const struct tb_xdp_error_response *res)
{
const struct tb_xdp_error_response *error;
if (hdr->type != ERROR_RESPONSE)
if (res->hdr.type != ERROR_RESPONSE)
return 0;
error = (const struct tb_xdp_error_response *)hdr;
switch (error->error) {
switch (res->error) {
case ERROR_UNKNOWN_PACKET:
case ERROR_UNKNOWN_DOMAIN:
return -EIO;
......@@ -257,7 +253,7 @@ static int tb_xdp_uuid_request(struct tb_ctl *ctl, u64 route, int retry,
if (ret)
return ret;
ret = tb_xdp_handle_error(&res.hdr);
ret = tb_xdp_handle_error(&res.err);
if (ret)
return ret;
......@@ -329,7 +325,7 @@ static int tb_xdp_properties_request(struct tb_ctl *ctl, u64 route,
if (ret)
goto err;
ret = tb_xdp_handle_error(&res->hdr);
ret = tb_xdp_handle_error(&res->err);
if (ret)
goto err;
......@@ -462,7 +458,7 @@ static int tb_xdp_properties_changed_request(struct tb_ctl *ctl, u64 route,
if (ret)
return ret;
return tb_xdp_handle_error(&res.hdr);
return tb_xdp_handle_error(&res.err);
}
static int
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册