root/drivers/thunderbolt/tb.c
// SPDX-License-Identifier: GPL-2.0
/*
 * Thunderbolt driver - bus logic (NHI independent)
 *
 * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com>
 * Copyright (C) 2019, Intel Corporation
 */

#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/pm_runtime.h>
#include <linux/platform_data/x86/apple.h>

#include "tb.h"
#include "tb_regs.h"
#include "tunnel.h"

#define TB_TIMEOUT              100     /* ms */
#define TB_RELEASE_BW_TIMEOUT   10000   /* ms */

/*
 * How many time bandwidth allocation request from graphics driver is
 * retried if the DP tunnel is still activating.
 */
#define TB_BW_ALLOC_RETRIES     3

/*
 * Minimum bandwidth (in Mb/s) that is needed in the single transmitter/receiver
 * direction. This is 40G - 10% guard band bandwidth.
 */
#define TB_ASYM_MIN             (40000 * 90 / 100)

/*
 * Threshold bandwidth (in Mb/s) that is used to switch the links to
 * asymmetric and back. This is selected as 45G which means when the
 * request is higher than this, we switch the link to asymmetric, and
 * when it is less than this we switch it back. The 45G is selected so
 * that we still have 27G (of the total 72G) for bulk PCIe traffic when
 * switching back to symmetric.
 */
#define TB_ASYM_THRESHOLD       45000

#define MAX_GROUPS              7       /* max Group_ID is 7 */

static unsigned int asym_threshold = TB_ASYM_THRESHOLD;
module_param_named(asym_threshold, asym_threshold, uint, 0444);
MODULE_PARM_DESC(asym_threshold,
                "threshold (Mb/s) when to Gen 4 switch link symmetry. 0 disables. (default: "
                __MODULE_STRING(TB_ASYM_THRESHOLD) ")");

/**
 * struct tb_cm - Simple Thunderbolt connection manager
 * @tunnel_list: List of active tunnels
 * @dp_resources: List of available DP resources for DP tunneling
 * @hotplug_active: tb_handle_hotplug will stop progressing plug
 *                  events and exit if this is not set (it needs to
 *                  acquire the lock one more time). Used to drain wq
 *                  after cfg has been paused.
 * @remove_work: Work used to remove any unplugged routers after
 *               runtime resume
 * @groups: Bandwidth groups used in this domain.
 */
struct tb_cm {
        struct list_head tunnel_list;
        struct list_head dp_resources;
        bool hotplug_active;
        struct delayed_work remove_work;
        struct tb_bandwidth_group groups[MAX_GROUPS];
};

static inline struct tb *tcm_to_tb(struct tb_cm *tcm)
{
        return ((void *)tcm - sizeof(struct tb));
}

struct tb_hotplug_event {
        struct delayed_work work;
        struct tb *tb;
        u64 route;
        u8 port;
        bool unplug;
        int retry;
};

static void tb_scan_port(struct tb_port *port);
static void tb_handle_hotplug(struct work_struct *work);
static void tb_dp_resource_unavailable(struct tb *tb, struct tb_port *port,
                                       const char *reason);
static void tb_queue_dp_bandwidth_request(struct tb *tb, u64 route, u8 port,
                                          int retry, unsigned long delay);

static void tb_queue_hotplug(struct tb *tb, u64 route, u8 port, bool unplug)
{
        struct tb_hotplug_event *ev;

        ev = kmalloc_obj(*ev);
        if (!ev)
                return;

        ev->tb = tb;
        ev->route = route;
        ev->port = port;
        ev->unplug = unplug;
        INIT_DELAYED_WORK(&ev->work, tb_handle_hotplug);
        queue_delayed_work(tb->wq, &ev->work, 0);
}

/* enumeration & hot plug handling */

static void tb_add_dp_resources(struct tb_switch *sw)
{
        struct tb_cm *tcm = tb_priv(sw->tb);
        struct tb_port *port;

        tb_switch_for_each_port(sw, port) {
                if (!tb_port_is_dpin(port))
                        continue;

                if (!tb_switch_query_dp_resource(sw, port))
                        continue;

                /*
                 * If DP IN on device router exist, position it at the
                 * beginning of the DP resources list, so that it is used
                 * before DP IN of the host router. This way external GPU(s)
                 * will be prioritized when pairing DP IN to a DP OUT.
                 */
                if (tb_route(sw))
                        list_add(&port->list, &tcm->dp_resources);
                else
                        list_add_tail(&port->list, &tcm->dp_resources);

                tb_port_dbg(port, "DP IN resource available\n");
        }
}

static void tb_remove_dp_resources(struct tb_switch *sw)
{
        struct tb_cm *tcm = tb_priv(sw->tb);
        struct tb_port *port, *tmp;

        /* Clear children resources first */
        tb_switch_for_each_port(sw, port) {
                if (tb_port_has_remote(port))
                        tb_remove_dp_resources(port->remote->sw);
        }

        list_for_each_entry_safe(port, tmp, &tcm->dp_resources, list) {
                if (port->sw == sw) {
                        tb_port_dbg(port, "DP OUT resource unavailable\n");
                        list_del_init(&port->list);
                }
        }
}

static void tb_discover_dp_resource(struct tb *tb, struct tb_port *port)
{
        struct tb_cm *tcm = tb_priv(tb);
        struct tb_port *p;

        list_for_each_entry(p, &tcm->dp_resources, list) {
                if (p == port)
                        return;
        }

        tb_port_dbg(port, "DP %s resource available discovered\n",
                    tb_port_is_dpin(port) ? "IN" : "OUT");
        list_add_tail(&port->list, &tcm->dp_resources);
}

static void tb_discover_dp_resources(struct tb *tb)
{
        struct tb_cm *tcm = tb_priv(tb);
        struct tb_tunnel *tunnel;

        list_for_each_entry(tunnel, &tcm->tunnel_list, list) {
                if (tb_tunnel_is_dp(tunnel))
                        tb_discover_dp_resource(tb, tunnel->dst_port);
        }
}

/* Enables CL states up to host router */
static int tb_enable_clx(struct tb_switch *sw)
{
        struct tb_cm *tcm = tb_priv(sw->tb);
        unsigned int clx = TB_CL0S | TB_CL1;
        const struct tb_tunnel *tunnel;
        int ret;

        /*
         * Currently only enable CLx for the first link. This is enough
         * to allow the CPU to save energy at least on Intel hardware
         * and makes it slightly simpler to implement. We may change
         * this in the future to cover the whole topology if it turns
         * out to be beneficial.
         */
        while (sw && tb_switch_depth(sw) > 1)
                sw = tb_switch_parent(sw);

        if (!sw)
                return 0;

        if (tb_switch_depth(sw) != 1)
                return 0;

        /*
         * If we are re-enabling then check if there is an active DMA
         * tunnel and in that case bail out.
         */
        list_for_each_entry(tunnel, &tcm->tunnel_list, list) {
                if (tb_tunnel_is_dma(tunnel)) {
                        if (tb_tunnel_port_on_path(tunnel, tb_upstream_port(sw)))
                                return 0;
                }
        }

        /*
         * Initially try with CL2. If that's not supported by the
         * topology try with CL0s and CL1 and then give up.
         */
        ret = tb_switch_clx_enable(sw, clx | TB_CL2);
        if (ret == -EOPNOTSUPP)
                ret = tb_switch_clx_enable(sw, clx);
        return ret == -EOPNOTSUPP ? 0 : ret;
}

/*
 * Disables CL states from @sw up to the host router.
 *
 * This can be used to figure out whether the link was setup by us or the
 * boot firmware so we don't accidentally enable them if they were not
 * enabled during discovery.
 */
static bool tb_disable_clx(struct tb_switch *sw)
{
        bool disabled = false;

        do {
                int ret;

                ret = tb_switch_clx_disable(sw);
                if (ret > 0)
                        disabled = true;
                else if (ret < 0)
                        tb_sw_warn(sw, "failed to disable CL states\n");

                sw = tb_switch_parent(sw);
        } while (sw);

        return disabled;
}

static int tb_increase_switch_tmu_accuracy(struct device *dev, void *data)
{
        struct tb_switch *sw;

        sw = tb_to_switch(dev);
        if (!sw)
                return 0;

        if (tb_switch_tmu_is_configured(sw, TB_SWITCH_TMU_MODE_LOWRES)) {
                enum tb_switch_tmu_mode mode;
                int ret;

                if (tb_switch_clx_is_enabled(sw, TB_CL1))
                        mode = TB_SWITCH_TMU_MODE_HIFI_UNI;
                else
                        mode = TB_SWITCH_TMU_MODE_HIFI_BI;

                ret = tb_switch_tmu_configure(sw, mode);
                if (ret)
                        return ret;

                return tb_switch_tmu_enable(sw);
        }

        return 0;
}

static void tb_increase_tmu_accuracy(struct tb_tunnel *tunnel)
{
        struct tb_switch *sw;

        if (!tunnel)
                return;

        /*
         * Once first DP tunnel is established we change the TMU
         * accuracy of first depth child routers (and the host router)
         * to the highest. This is needed for the DP tunneling to work
         * but also allows CL0s.
         *
         * If both routers are v2 then we don't need to do anything as
         * they are using enhanced TMU mode that allows all CLx.
         */
        sw = tunnel->tb->root_switch;
        device_for_each_child(&sw->dev, NULL, tb_increase_switch_tmu_accuracy);
}

static int tb_switch_tmu_hifi_uni_required(struct device *dev, void *not_used)
{
        struct tb_switch *sw = tb_to_switch(dev);

        if (sw && tb_switch_tmu_is_enabled(sw) &&
            tb_switch_tmu_is_configured(sw, TB_SWITCH_TMU_MODE_HIFI_UNI))
                return 1;

        return device_for_each_child(dev, NULL,
                                     tb_switch_tmu_hifi_uni_required);
}

static bool tb_tmu_hifi_uni_required(struct tb *tb)
{
        return device_for_each_child(&tb->dev, NULL,
                                     tb_switch_tmu_hifi_uni_required) == 1;
}

static int tb_enable_tmu(struct tb_switch *sw)
{
        int ret;

        /*
         * If both routers at the end of the link are v2 we simply
         * enable the enhanced uni-directional mode. That covers all
         * the CL states. For v1 and before we need to use the normal
         * rate to allow CL1 (when supported). Otherwise we keep the TMU
         * running at the highest accuracy.
         */
        ret = tb_switch_tmu_configure(sw,
                        TB_SWITCH_TMU_MODE_MEDRES_ENHANCED_UNI);
        if (ret == -EOPNOTSUPP) {
                if (tb_switch_clx_is_enabled(sw, TB_CL1)) {
                        /*
                         * Figure out uni-directional HiFi TMU requirements
                         * currently in the domain. If there are no
                         * uni-directional HiFi requirements we can put the TMU
                         * into LowRes mode.
                         *
                         * Deliberately skip bi-directional HiFi links
                         * as these work independently of other links
                         * (and they do not allow any CL states anyway).
                         */
                        if (tb_tmu_hifi_uni_required(sw->tb))
                                ret = tb_switch_tmu_configure(sw,
                                                TB_SWITCH_TMU_MODE_HIFI_UNI);
                        else
                                ret = tb_switch_tmu_configure(sw,
                                                TB_SWITCH_TMU_MODE_LOWRES);
                } else {
                        ret = tb_switch_tmu_configure(sw, TB_SWITCH_TMU_MODE_HIFI_BI);
                }

                /* If not supported, fallback to bi-directional HiFi */
                if (ret == -EOPNOTSUPP)
                        ret = tb_switch_tmu_configure(sw, TB_SWITCH_TMU_MODE_HIFI_BI);
        }
        if (ret)
                return ret;

        /* If it is already enabled in correct mode, don't touch it */
        if (tb_switch_tmu_is_enabled(sw))
                return 0;

        ret = tb_switch_tmu_disable(sw);
        if (ret)
                return ret;

        ret = tb_switch_tmu_post_time(sw);
        if (ret)
                return ret;

        return tb_switch_tmu_enable(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_port *port;

        tb_switch_for_each_port(sw, port) {
                struct tb_tunnel *tunnel = NULL;

                switch (port->config.type) {
                case TB_TYPE_DP_HDMI_IN:
                        tunnel = tb_tunnel_discover_dp(tb, port, alloc_hopids);
                        tb_increase_tmu_accuracy(tunnel);
                        break;

                case TB_TYPE_PCIE_DOWN:
                        tunnel = tb_tunnel_discover_pci(tb, port, alloc_hopids);
                        break;

                case TB_TYPE_USB3_DOWN:
                        tunnel = tb_tunnel_discover_usb3(tb, port, alloc_hopids);
                        break;

                default:
                        break;
                }

                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 int tb_port_configure_xdomain(struct tb_port *port, struct tb_xdomain *xd)
{
        if (tb_switch_is_usb4(port->sw))
                return usb4_port_configure_xdomain(port, xd);
        return tb_lc_configure_xdomain(port);
}

static void tb_port_unconfigure_xdomain(struct tb_port *port)
{
        if (tb_switch_is_usb4(port->sw))
                usb4_port_unconfigure_xdomain(port);
        else
                tb_lc_unconfigure_xdomain(port);
}

static void tb_scan_xdomain(struct tb_port *port)
{
        struct tb_switch *sw = port->sw;
        struct tb *tb = sw->tb;
        struct tb_xdomain *xd;
        u64 route;

        if (!tb_is_xdomain_enabled())
                return;

        route = tb_downstream_route(port);
        xd = tb_xdomain_find_by_route(tb, route);
        if (xd) {
                tb_xdomain_put(xd);
                return;
        }

        xd = tb_xdomain_alloc(tb, &sw->dev, route, tb->root_switch->uuid,
                              NULL);
        if (xd) {
                tb_port_at(route, sw)->xdomain = xd;
                tb_port_configure_xdomain(port, xd);
                tb_xdomain_add(xd);
        }
}

/*
 * Returns the first inactive port on @sw.
 */
static struct tb_port *tb_find_unused_port(struct tb_switch *sw,
                                           enum tb_port_type type)
{
        struct tb_port *port;

        tb_switch_for_each_port(sw, port) {
                if (tb_is_upstream_port(port))
                        continue;
                if (port->config.type != type)
                        continue;
                if (!port->cap_adap)
                        continue;
                if (tb_port_is_enabled(port))
                        continue;
                return port;
        }
        return NULL;
}

static struct tb_port *tb_find_usb3_down(struct tb_switch *sw,
                                         const struct tb_port *port)
{
        struct tb_port *down;

        down = usb4_switch_map_usb3_down(sw, port);
        if (down && !tb_usb3_port_is_enabled(down))
                return down;
        return NULL;
}

static struct tb_tunnel *tb_find_tunnel(struct tb *tb, enum tb_tunnel_type type,
                                        struct tb_port *src_port,
                                        struct tb_port *dst_port)
{
        struct tb_cm *tcm = tb_priv(tb);
        struct tb_tunnel *tunnel;

        list_for_each_entry(tunnel, &tcm->tunnel_list, list) {
                if (tunnel->type == type &&
                    ((src_port && src_port == tunnel->src_port) ||
                     (dst_port && dst_port == tunnel->dst_port))) {
                        return tunnel;
                }
        }

        return NULL;
}

static struct tb_tunnel *tb_find_first_usb3_tunnel(struct tb *tb,
                                                   struct tb_port *src_port,
                                                   struct tb_port *dst_port)
{
        struct tb_port *port, *usb3_down;
        struct tb_switch *sw;

        /* Pick the router that is deepest in the topology */
        if (tb_port_path_direction_downstream(src_port, dst_port))
                sw = dst_port->sw;
        else
                sw = src_port->sw;

        /* Can't be the host router */
        if (sw == tb->root_switch)
                return NULL;

        /* Find the downstream USB4 port that leads to this router */
        port = tb_port_at(tb_route(sw), tb->root_switch);
        /* Find the corresponding host router USB3 downstream port */
        usb3_down = usb4_switch_map_usb3_down(tb->root_switch, port);
        if (!usb3_down)
                return NULL;

        return tb_find_tunnel(tb, TB_TUNNEL_USB3, usb3_down, NULL);
}

/**
 * tb_consumed_usb3_pcie_bandwidth() - Consumed USB3/PCIe bandwidth over a single link
 * @tb: Domain structure
 * @src_port: Source protocol adapter
 * @dst_port: Destination protocol adapter
 * @port: USB4 port the consumed bandwidth is calculated
 * @consumed_up: Consumed upstream bandwidth (Mb/s)
 * @consumed_down: Consumed downstream bandwidth (Mb/s)
 *
 * Calculates consumed USB3 and PCIe bandwidth at @port between path
 * from @src_port to @dst_port. Does not take USB3 tunnel starting from
 * @src_port and ending on @src_port into account because that bandwidth is
 * already included in as part of the "first hop" USB3 tunnel.
 *
 * Return: %0 on success, negative errno otherwise.
 */
static int tb_consumed_usb3_pcie_bandwidth(struct tb *tb,
                                           struct tb_port *src_port,
                                           struct tb_port *dst_port,
                                           struct tb_port *port,
                                           int *consumed_up,
                                           int *consumed_down)
{
        int pci_consumed_up, pci_consumed_down;
        struct tb_tunnel *tunnel;

        *consumed_up = *consumed_down = 0;

        tunnel = tb_find_first_usb3_tunnel(tb, src_port, dst_port);
        if (tunnel && !tb_port_is_usb3_down(src_port) &&
            !tb_port_is_usb3_up(dst_port)) {
                int ret;

                ret = tb_tunnel_consumed_bandwidth(tunnel, consumed_up,
                                                   consumed_down);
                if (ret)
                        return ret;
        }

        /*
         * If there is anything reserved for PCIe bulk traffic take it
         * into account here too.
         */
        if (tb_tunnel_reserved_pci(port, &pci_consumed_up, &pci_consumed_down)) {
                *consumed_up += pci_consumed_up;
                *consumed_down += pci_consumed_down;
        }

        return 0;
}

/**
 * tb_consumed_dp_bandwidth() - Consumed DP bandwidth over a single link
 * @tb: Domain structure
 * @src_port: Source protocol adapter
 * @dst_port: Destination protocol adapter
 * @port: USB4 port the consumed bandwidth is calculated
 * @consumed_up: Consumed upstream bandwidth (Mb/s)
 * @consumed_down: Consumed downstream bandwidth (Mb/s)
 *
 * Calculates consumed DP bandwidth at @port between path from @src_port
 * to @dst_port. Does not take tunnel starting from @src_port and ending
 * from @src_port into account.
 *
 * If there is bandwidth reserved for any of the groups between
 * @src_port and @dst_port (but not yet used) that is also taken into
 * account in the returned consumed bandwidth.
 *
 * Return: %0 on success, negative errno otherwise.
 */
static int tb_consumed_dp_bandwidth(struct tb *tb,
                                    struct tb_port *src_port,
                                    struct tb_port *dst_port,
                                    struct tb_port *port,
                                    int *consumed_up,
                                    int *consumed_down)
{
        int group_reserved[MAX_GROUPS] = {};
        struct tb_cm *tcm = tb_priv(tb);
        struct tb_tunnel *tunnel;
        bool downstream;
        int i, ret;

        *consumed_up = *consumed_down = 0;

        /*
         * Find all DP tunnels that cross the port and reduce
         * their consumed bandwidth from the available.
         */
        list_for_each_entry(tunnel, &tcm->tunnel_list, list) {
                const struct tb_bandwidth_group *group;
                int dp_consumed_up, dp_consumed_down;

                if (tb_tunnel_is_invalid(tunnel))
                        continue;

                if (!tb_tunnel_is_dp(tunnel))
                        continue;

                if (!tb_tunnel_port_on_path(tunnel, port))
                        continue;

                /*
                 * Calculate what is reserved for groups crossing the
                 * same ports only once (as that is reserved for all the
                 * tunnels in the group).
                 */
                group = tunnel->src_port->group;
                if (group && group->reserved && !group_reserved[group->index])
                        group_reserved[group->index] = group->reserved;

                /*
                 * Ignore the DP tunnel between src_port and dst_port
                 * because it is the same tunnel and we may be
                 * re-calculating estimated bandwidth.
                 */
                if (tunnel->src_port == src_port &&
                    tunnel->dst_port == dst_port)
                        continue;

                ret = tb_tunnel_consumed_bandwidth(tunnel, &dp_consumed_up,
                                                   &dp_consumed_down);
                if (ret)
                        return ret;

                *consumed_up += dp_consumed_up;
                *consumed_down += dp_consumed_down;
        }

        downstream = tb_port_path_direction_downstream(src_port, dst_port);
        for (i = 0; i < ARRAY_SIZE(group_reserved); i++) {
                if (downstream)
                        *consumed_down += group_reserved[i];
                else
                        *consumed_up += group_reserved[i];
        }

        return 0;
}

static bool tb_asym_supported(struct tb_port *src_port, struct tb_port *dst_port,
                              struct tb_port *port)
{
        bool downstream = tb_port_path_direction_downstream(src_port, dst_port);
        enum tb_link_width width;

        if (tb_is_upstream_port(port))
                width = downstream ? TB_LINK_WIDTH_ASYM_RX : TB_LINK_WIDTH_ASYM_TX;
        else
                width = downstream ? TB_LINK_WIDTH_ASYM_TX : TB_LINK_WIDTH_ASYM_RX;

        return tb_port_width_supported(port, width);
}

/**
 * tb_maximum_bandwidth() - Maximum bandwidth over a single link
 * @tb: Domain structure
 * @src_port: Source protocol adapter
 * @dst_port: Destination protocol adapter
 * @port: USB4 port the total bandwidth is calculated
 * @max_up: Maximum upstream bandwidth (Mb/s)
 * @max_down: Maximum downstream bandwidth (Mb/s)
 * @include_asym: Include bandwidth if the link is switched from
 *                symmetric to asymmetric
 *
 * Returns maximum possible bandwidth in @max_up and @max_down over a
 * single link at @port. If @include_asym is set then includes the
 * additional banwdith if the links are transitioned into asymmetric to
 * direction from @src_port to @dst_port.
 *
 * Return: %0 on success, negative errno otherwise.
 */
static int tb_maximum_bandwidth(struct tb *tb, struct tb_port *src_port,
                                struct tb_port *dst_port, struct tb_port *port,
                                int *max_up, int *max_down, bool include_asym)
{
        bool downstream = tb_port_path_direction_downstream(src_port, dst_port);
        int link_speed, link_width, up_bw, down_bw;

        /*
         * Can include asymmetric, only if it is actually supported by
         * the lane adapter.
         */
        if (!tb_asym_supported(src_port, dst_port, port))
                include_asym = false;

        if (tb_is_upstream_port(port)) {
                link_speed = port->sw->link_speed;
                /*
                 * sw->link_width is from upstream perspective so we use
                 * the opposite for downstream of the host router.
                 */
                if (port->sw->link_width == TB_LINK_WIDTH_ASYM_TX) {
                        up_bw = link_speed * 3 * 1000;
                        down_bw = link_speed * 1 * 1000;
                } else if (port->sw->link_width == TB_LINK_WIDTH_ASYM_RX) {
                        up_bw = link_speed * 1 * 1000;
                        down_bw = link_speed * 3 * 1000;
                } else if (include_asym) {
                        /*
                         * The link is symmetric at the moment but we
                         * can switch it to asymmetric as needed. Report
                         * this bandwidth as available (even though it
                         * is not yet enabled).
                         */
                        if (downstream) {
                                up_bw = link_speed * 1 * 1000;
                                down_bw = link_speed * 3 * 1000;
                        } else {
                                up_bw = link_speed * 3 * 1000;
                                down_bw = link_speed * 1 * 1000;
                        }
                } else {
                        up_bw = link_speed * port->sw->link_width * 1000;
                        down_bw = up_bw;
                }
        } else {
                link_speed = tb_port_get_link_speed(port);
                if (link_speed < 0)
                        return link_speed;

                link_width = tb_port_get_link_width(port);
                if (link_width < 0)
                        return link_width;

                if (link_width == TB_LINK_WIDTH_ASYM_TX) {
                        up_bw = link_speed * 1 * 1000;
                        down_bw = link_speed * 3 * 1000;
                } else if (link_width == TB_LINK_WIDTH_ASYM_RX) {
                        up_bw = link_speed * 3 * 1000;
                        down_bw = link_speed * 1 * 1000;
                } else if (include_asym) {
                        /*
                         * The link is symmetric at the moment but we
                         * can switch it to asymmetric as needed. Report
                         * this bandwidth as available (even though it
                         * is not yet enabled).
                         */
                        if (downstream) {
                                up_bw = link_speed * 1 * 1000;
                                down_bw = link_speed * 3 * 1000;
                        } else {
                                up_bw = link_speed * 3 * 1000;
                                down_bw = link_speed * 1 * 1000;
                        }
                } else {
                        up_bw = link_speed * link_width * 1000;
                        down_bw = up_bw;
                }
        }

        /* Leave 10% guard band */
        *max_up = up_bw - up_bw / 10;
        *max_down = down_bw - down_bw / 10;

        tb_port_dbg(port, "link maximum bandwidth %d/%d Mb/s\n", *max_up, *max_down);
        return 0;
}

/**
 * tb_available_bandwidth() - Available bandwidth for tunneling
 * @tb: Domain structure
 * @src_port: Source protocol adapter
 * @dst_port: Destination protocol adapter
 * @available_up: Available bandwidth upstream (Mb/s)
 * @available_down: Available bandwidth downstream (Mb/s)
 * @include_asym: Include bandwidth if the link is switched from
 *                symmetric to asymmetric
 *
 * Calculates maximum available bandwidth for protocol tunneling between
 * @src_port and @dst_port at the moment. This is minimum of maximum
 * link bandwidth across all links reduced by currently consumed
 * bandwidth on that link.
 *
 * If @include_asym is true then includes also bandwidth that can be
 * added when the links are transitioned into asymmetric (but does not
 * transition the links).
 *
 * Return: %0 on success, negative errno otherwise.
 */
static int tb_available_bandwidth(struct tb *tb, struct tb_port *src_port,
                                 struct tb_port *dst_port, int *available_up,
                                 int *available_down, bool include_asym)
{
        struct tb_port *port;
        int ret;

        /* Maximum possible bandwidth asymmetric Gen 4 link is 120 Gb/s */
        *available_up = *available_down = 120000;

        /* Find the minimum available bandwidth over all links */
        tb_for_each_port_on_path(src_port, dst_port, port) {
                int max_up, max_down, consumed_up, consumed_down;

                if (!tb_port_is_null(port))
                        continue;

                ret = tb_maximum_bandwidth(tb, src_port, dst_port, port,
                                           &max_up, &max_down, include_asym);
                if (ret)
                        return ret;

                ret = tb_consumed_usb3_pcie_bandwidth(tb, src_port, dst_port,
                                                      port, &consumed_up,
                                                      &consumed_down);
                if (ret)
                        return ret;
                max_up -= consumed_up;
                max_down -= consumed_down;

                ret = tb_consumed_dp_bandwidth(tb, src_port, dst_port, port,
                                               &consumed_up, &consumed_down);
                if (ret)
                        return ret;
                max_up -= consumed_up;
                max_down -= consumed_down;

                if (max_up < *available_up)
                        *available_up = max_up;
                if (max_down < *available_down)
                        *available_down = max_down;
        }

        if (*available_up < 0)
                *available_up = 0;
        if (*available_down < 0)
                *available_down = 0;

        return 0;
}

static int tb_release_unused_usb3_bandwidth(struct tb *tb,
                                            struct tb_port *src_port,
                                            struct tb_port *dst_port)
{
        struct tb_tunnel *tunnel;

        tunnel = tb_find_first_usb3_tunnel(tb, src_port, dst_port);
        return tunnel ? tb_tunnel_release_unused_bandwidth(tunnel) : 0;
}

static void tb_reclaim_usb3_bandwidth(struct tb *tb, struct tb_port *src_port,
                                      struct tb_port *dst_port)
{
        int ret, available_up, available_down;
        struct tb_tunnel *tunnel;

        tunnel = tb_find_first_usb3_tunnel(tb, src_port, dst_port);
        if (!tunnel)
                return;

        tb_tunnel_dbg(tunnel, "reclaiming unused bandwidth\n");

        /*
         * Calculate available bandwidth for the first hop USB3 tunnel.
         * That determines the whole USB3 bandwidth for this branch.
         */
        ret = tb_available_bandwidth(tb, tunnel->src_port, tunnel->dst_port,
                                     &available_up, &available_down, false);
        if (ret) {
                tb_tunnel_warn(tunnel, "failed to calculate available bandwidth\n");
                return;
        }

        tb_tunnel_dbg(tunnel, "available bandwidth %d/%d Mb/s\n", available_up,
                      available_down);

        tb_tunnel_reclaim_available_bandwidth(tunnel, &available_up, &available_down);
}

static int tb_tunnel_usb3(struct tb *tb, struct tb_switch *sw)
{
        struct tb_switch *parent = tb_switch_parent(sw);
        int ret, available_up, available_down;
        struct tb_port *up, *down, *port;
        struct tb_cm *tcm = tb_priv(tb);
        struct tb_tunnel *tunnel;

        if (!tb_acpi_may_tunnel_usb3()) {
                tb_dbg(tb, "USB3 tunneling disabled, not creating tunnel\n");
                return 0;
        }

        up = tb_switch_find_port(sw, TB_TYPE_USB3_UP);
        if (!up)
                return 0;

        if (!sw->link_usb4)
                return 0;

        /*
         * Look up available down port. Since we are chaining it should
         * be found right above this switch.
         */
        port = tb_switch_downstream_port(sw);
        down = tb_find_usb3_down(parent, port);
        if (!down)
                return 0;

        if (tb_route(parent)) {
                struct tb_port *parent_up;
                /*
                 * Check first that the parent switch has its upstream USB3
                 * port enabled. Otherwise the chain is not complete and
                 * there is no point setting up a new tunnel.
                 */
                parent_up = tb_switch_find_port(parent, TB_TYPE_USB3_UP);
                if (!parent_up || !tb_port_is_enabled(parent_up))
                        return 0;

                /* Make all unused bandwidth available for the new tunnel */
                ret = tb_release_unused_usb3_bandwidth(tb, down, up);
                if (ret)
                        return ret;
        }

        ret = tb_available_bandwidth(tb, down, up, &available_up, &available_down,
                                     false);
        if (ret)
                goto err_reclaim;

        tb_port_dbg(up, "available bandwidth for new USB3 tunnel %d/%d Mb/s\n",
                    available_up, available_down);

        /*
         * If the available bandwidth is less than 1.5 Gb/s notify
         * userspace that the connected isochronous device may not work
         * properly.
         */
        if (available_up < 1500 || available_down < 1500)
                tb_tunnel_event(tb, TB_TUNNEL_LOW_BANDWIDTH, TB_TUNNEL_USB3,
                                down, up);

        tunnel = tb_tunnel_alloc_usb3(tb, up, down, available_up,
                                      available_down);
        if (!tunnel) {
                ret = -ENOMEM;
                goto err_reclaim;
        }

        if (tb_tunnel_activate(tunnel)) {
                tb_port_info(up,
                             "USB3 tunnel activation failed, aborting\n");
                ret = -EIO;
                goto err_free;
        }

        list_add_tail(&tunnel->list, &tcm->tunnel_list);
        if (tb_route(parent))
                tb_reclaim_usb3_bandwidth(tb, down, up);

        return 0;

err_free:
        tb_tunnel_put(tunnel);
err_reclaim:
        if (tb_route(parent))
                tb_reclaim_usb3_bandwidth(tb, down, up);

        return ret;
}

static int tb_create_usb3_tunnels(struct tb_switch *sw)
{
        struct tb_port *port;
        int ret;

        if (!tb_acpi_may_tunnel_usb3())
                return 0;

        if (tb_route(sw)) {
                ret = tb_tunnel_usb3(sw->tb, sw);
                if (ret)
                        return ret;
        }

        tb_switch_for_each_port(sw, port) {
                if (!tb_port_has_remote(port))
                        continue;
                ret = tb_create_usb3_tunnels(port->remote->sw);
                if (ret)
                        return ret;
        }

        return 0;
}

/**
 * tb_configure_asym() - Transition links to asymmetric if needed
 * @tb: Domain structure
 * @src_port: Source adapter to start the transition
 * @dst_port: Destination adapter
 * @requested_up: Additional bandwidth (Mb/s) required upstream
 * @requested_down: Additional bandwidth (Mb/s) required downstream
 *
 * Transition links between @src_port and @dst_port into asymmetric, with
 * three lanes in the direction from @src_port towards @dst_port and one lane
 * in the opposite direction, if the bandwidth requirements
 * (requested + currently consumed) on that link exceed @asym_threshold.
 *
 * Must be called with available >= requested over all links.
 *
 * Return: %0 on success, negative errno otherwise.
 */
static int tb_configure_asym(struct tb *tb, struct tb_port *src_port,
                             struct tb_port *dst_port, int requested_up,
                             int requested_down)
{
        bool clx = false, clx_disabled = false, downstream;
        struct tb_switch *sw;
        struct tb_port *up;
        int ret = 0;

        if (!asym_threshold)
                return 0;

        downstream = tb_port_path_direction_downstream(src_port, dst_port);
        /* Pick up router deepest in the hierarchy */
        if (downstream)
                sw = dst_port->sw;
        else
                sw = src_port->sw;

        tb_for_each_upstream_port_on_path(src_port, dst_port, up) {
                struct tb_port *down = tb_switch_downstream_port(up->sw);
                enum tb_link_width width_up, width_down;
                int consumed_up, consumed_down;

                ret = tb_consumed_dp_bandwidth(tb, src_port, dst_port, up,
                                               &consumed_up, &consumed_down);
                if (ret)
                        break;

                if (downstream) {
                        /*
                         * Downstream so make sure upstream is within the 36G
                         * (40G - guard band 10%), and the requested is above
                         * what the threshold is.
                         */
                        if (consumed_up + requested_up >= TB_ASYM_MIN) {
                                ret = -ENOBUFS;
                                break;
                        }
                        /* Does consumed + requested exceed the threshold */
                        if (consumed_down + requested_down < asym_threshold)
                                continue;

                        width_up = TB_LINK_WIDTH_ASYM_RX;
                        width_down = TB_LINK_WIDTH_ASYM_TX;
                } else {
                        /* Upstream, the opposite of above */
                        if (consumed_down + requested_down >= TB_ASYM_MIN) {
                                ret = -ENOBUFS;
                                break;
                        }
                        if (consumed_up + requested_up < asym_threshold)
                                continue;

                        width_up = TB_LINK_WIDTH_ASYM_TX;
                        width_down = TB_LINK_WIDTH_ASYM_RX;
                }

                if (up->sw->link_width == width_up)
                        continue;

                if (!tb_port_width_supported(up, width_up) ||
                    !tb_port_width_supported(down, width_down))
                        continue;

                /*
                 * Disable CL states before doing any transitions. We
                 * delayed it until now that we know there is a real
                 * transition taking place.
                 */
                if (!clx_disabled) {
                        clx = tb_disable_clx(sw);
                        clx_disabled = true;
                }

                tb_sw_dbg(up->sw, "configuring asymmetric link\n");

                /*
                 * Here requested + consumed > threshold so we need to
                 * transition the link into asymmetric now.
                 */
                ret = tb_switch_set_link_width(up->sw, width_up);
                if (ret) {
                        tb_sw_warn(up->sw, "failed to set link width\n");
                        break;
                }
        }

        /* Re-enable CL states if they were previosly enabled */
        if (clx)
                tb_enable_clx(sw);

        return ret;
}

/**
 * tb_configure_sym() - Transition links to symmetric if possible
 * @tb: Domain structure
 * @src_port: Source adapter to start the transition
 * @dst_port: Destination adapter
 * @keep_asym: Keep asymmetric link if preferred
 *
 * Goes over each link from @src_port to @dst_port and tries to
 * transition the link to symmetric if the currently consumed bandwidth
 * allows and link asymmetric preference is ignored (if @keep_asym is %false).
 *
 * Return: %0 on success, negative errno otherwise.
 */
static int tb_configure_sym(struct tb *tb, struct tb_port *src_port,
                            struct tb_port *dst_port, bool keep_asym)
{
        bool clx = false, clx_disabled = false, downstream;
        struct tb_switch *sw;
        struct tb_port *up;
        int ret = 0;

        if (!asym_threshold)
                return 0;

        downstream = tb_port_path_direction_downstream(src_port, dst_port);
        /* Pick up router deepest in the hierarchy */
        if (downstream)
                sw = dst_port->sw;
        else
                sw = src_port->sw;

        tb_for_each_upstream_port_on_path(src_port, dst_port, up) {
                int consumed_up, consumed_down;

                /* Already symmetric */
                if (up->sw->link_width <= TB_LINK_WIDTH_DUAL)
                        continue;
                /* Unplugged, no need to switch */
                if (up->sw->is_unplugged)
                        continue;

                ret = tb_consumed_dp_bandwidth(tb, src_port, dst_port, up,
                                               &consumed_up, &consumed_down);
                if (ret)
                        break;

                if (downstream) {
                        /*
                         * Downstream so we want the consumed_down < threshold.
                         * Upstream traffic should be less than 36G (40G
                         * guard band 10%) as the link was configured asymmetric
                         * already.
                         */
                        if (consumed_down >= asym_threshold)
                                continue;
                } else {
                        if (consumed_up >= asym_threshold)
                                continue;
                }

                if (up->sw->link_width == TB_LINK_WIDTH_DUAL)
                        continue;

                /*
                 * Here consumed < threshold so we can transition the
                 * link to symmetric.
                 *
                 * However, if the router prefers asymmetric link we
                 * honor that (unless @keep_asym is %false).
                 */
                if (keep_asym &&
                    up->sw->preferred_link_width > TB_LINK_WIDTH_DUAL) {
                        tb_sw_dbg(up->sw, "keeping preferred asymmetric link\n");
                        continue;
                }

                /* Disable CL states before doing any transitions */
                if (!clx_disabled) {
                        clx = tb_disable_clx(sw);
                        clx_disabled = true;
                }

                tb_sw_dbg(up->sw, "configuring symmetric link\n");

                ret = tb_switch_set_link_width(up->sw, TB_LINK_WIDTH_DUAL);
                if (ret) {
                        tb_sw_warn(up->sw, "failed to set link width\n");
                        break;
                }
        }

        /* Re-enable CL states if they were previosly enabled */
        if (clx)
                tb_enable_clx(sw);

        return ret;
}

static void tb_configure_link(struct tb_port *down, struct tb_port *up,
                              struct tb_switch *sw)
{
        struct tb *tb = sw->tb;

        /* Link the routers using both links if available */
        down->remote = up;
        up->remote = down;
        if (down->dual_link_port && up->dual_link_port) {
                down->dual_link_port->remote = up->dual_link_port;
                up->dual_link_port->remote = down->dual_link_port;
        }

        /*
         * Enable lane bonding if the link is currently two single lane
         * links.
         */
        if (sw->link_width < TB_LINK_WIDTH_DUAL)
                tb_switch_set_link_width(sw, TB_LINK_WIDTH_DUAL);

        /*
         * Device router that comes up as symmetric link is
         * connected deeper in the hierarchy, we transition the links
         * above into symmetric if bandwidth allows.
         */
        if (tb_switch_depth(sw) > 1 &&
            tb_port_get_link_generation(up) >= 4 &&
            up->sw->link_width == TB_LINK_WIDTH_DUAL) {
                struct tb_port *host_port;

                host_port = tb_port_at(tb_route(sw), tb->root_switch);
                tb_configure_sym(tb, host_port, up, false);
        }

        /* Set the link configured */
        tb_switch_configure_link(sw);
}

/*
 * tb_scan_switch() - scan for and initialize downstream switches
 */
static void tb_scan_switch(struct tb_switch *sw)
{
        struct tb_port *port;

        pm_runtime_get_sync(&sw->dev);

        tb_switch_for_each_port(sw, port)
                tb_scan_port(port);

        pm_runtime_mark_last_busy(&sw->dev);
        pm_runtime_put_autosuspend(&sw->dev);
}

/*
 * tb_scan_port() - check for and initialize switches below port
 */
static void tb_scan_port(struct tb_port *port)
{
        struct tb_cm *tcm = tb_priv(port->sw->tb);
        struct tb_port *upstream_port;
        bool discovery = false;
        struct tb_switch *sw;

        if (tb_is_upstream_port(port))
                return;

        if (tb_port_is_dpout(port) && tb_dp_port_hpd_is_active(port) == 1 &&
            !tb_dp_port_is_enabled(port)) {
                tb_port_dbg(port, "DP adapter HPD set, queuing hotplug\n");
                tb_queue_hotplug(port->sw->tb, tb_route(port->sw), port->port,
                                 false);
                return;
        }

        if (port->config.type != TB_TYPE_PORT)
                return;
        if (port->dual_link_port && port->link_nr)
                return; /*
                         * Downstream switch is reachable through two ports.
                         * Only scan on the primary port (link_nr == 0).
                         */

        if (port->usb4)
                pm_runtime_get_sync(&port->usb4->dev);

        if (tb_wait_for_port(port, false) <= 0)
                goto out_rpm_put;
        if (port->remote) {
                tb_port_dbg(port, "port already has a remote\n");
                goto out_rpm_put;
        }

        sw = tb_switch_alloc(port->sw->tb, &port->sw->dev,
                             tb_downstream_route(port));
        if (IS_ERR(sw)) {
                /*
                 * Make the downstream retimers available even if there
                 * is no router connected.
                 */
                tb_retimer_scan(port, true);

                /*
                 * If there is an error accessing the connected switch
                 * it may be connected to another domain. Also we allow
                 * the other domain to be connected to a max depth switch.
                 */
                if (PTR_ERR(sw) == -EIO || PTR_ERR(sw) == -EADDRNOTAVAIL)
                        tb_scan_xdomain(port);
                goto out_rpm_put;
        }

        if (tb_switch_configure(sw)) {
                tb_switch_put(sw);
                goto out_rpm_put;
        }

        /*
         * If there was previously another domain connected remove it
         * first.
         */
        if (port->xdomain) {
                tb_xdomain_remove(port->xdomain);
                tb_port_unconfigure_xdomain(port);
                port->xdomain = NULL;
        }

        /*
         * Do not send uevents until we have discovered all existing
         * tunnels and know which switches were authorized already by
         * the boot firmware.
         */
        if (!tcm->hotplug_active) {
                dev_set_uevent_suppress(&sw->dev, true);
                discovery = true;
        }

        /*
         * At the moment Thunderbolt 2 and beyond (devices with LC) we
         * can support runtime PM.
         */
        sw->rpm = sw->generation > 1;

        if (tb_switch_add(sw)) {
                tb_switch_put(sw);
                goto out_rpm_put;
        }

        upstream_port = tb_upstream_port(sw);
        tb_configure_link(port, upstream_port, sw);

        /*
         * Scan for downstream retimers. We only scan them after the
         * router has been enumerated to avoid issues with certain
         * Pluggable devices that expect the host to enumerate them
         * within certain timeout.
         */
        tb_retimer_scan(port, true);

        /*
         * CL0s and CL1 are enabled and supported together.
         * Silently ignore CLx enabling in case CLx is not supported.
         */
        if (discovery)
                tb_sw_dbg(sw, "discovery, not touching CL states\n");
        else if (tb_enable_clx(sw))
                tb_sw_warn(sw, "failed to enable CL states\n");

        if (tb_enable_tmu(sw))
                tb_sw_warn(sw, "failed to enable TMU\n");

        /*
         * Configuration valid needs to be set after the TMU has been
         * enabled for the upstream port of the router so we do it here.
         */
        tb_switch_configuration_valid(sw);

        /* Scan upstream retimers */
        tb_retimer_scan(upstream_port, true);

        /*
         * Create USB 3.x tunnels only when the switch is plugged to the
         * domain. This is because we scan the domain also during discovery
         * and want to discover existing USB 3.x tunnels before we create
         * any new.
         */
        if (tcm->hotplug_active && tb_tunnel_usb3(sw->tb, sw))
                tb_sw_warn(sw, "USB3 tunnel creation failed\n");

        tb_add_dp_resources(sw);
        tb_scan_switch(sw);

out_rpm_put:
        if (port->usb4) {
                pm_runtime_mark_last_busy(&port->usb4->dev);
                pm_runtime_put_autosuspend(&port->usb4->dev);
        }
}

static void
tb_recalc_estimated_bandwidth_for_group(struct tb_bandwidth_group *group)
{
        struct tb_tunnel *first_tunnel;
        struct tb *tb = group->tb;
        struct tb_port *in;
        int ret;

        tb_dbg(tb, "re-calculating bandwidth estimation for group %u\n",
               group->index);

        first_tunnel = NULL;
        list_for_each_entry(in, &group->ports, group_list) {
                int estimated_bw, estimated_up, estimated_down;
                struct tb_tunnel *tunnel;
                struct tb_port *out;

                if (!usb4_dp_port_bandwidth_mode_enabled(in))
                        continue;

                tunnel = tb_find_tunnel(tb, TB_TUNNEL_DP, in, NULL);
                if (WARN_ON(!tunnel))
                        break;

                if (!first_tunnel) {
                        /*
                         * Since USB3 bandwidth is shared by all DP
                         * tunnels under the host router USB4 port, even
                         * if they do not begin from the host router, we
                         * can release USB3 bandwidth just once and not
                         * for each tunnel separately.
                         */
                        first_tunnel = tunnel;
                        ret = tb_release_unused_usb3_bandwidth(tb,
                                first_tunnel->src_port, first_tunnel->dst_port);
                        if (ret) {
                                tb_tunnel_warn(tunnel,
                                        "failed to release unused bandwidth\n");
                                break;
                        }
                }

                out = tunnel->dst_port;
                ret = tb_available_bandwidth(tb, in, out, &estimated_up,
                                             &estimated_down, true);
                if (ret) {
                        tb_tunnel_warn(tunnel,
                                "failed to re-calculate estimated bandwidth\n");
                        break;
                }

                /*
                 * Estimated bandwidth includes:
                 *  - already allocated bandwidth for the DP tunnel
                 *  - available bandwidth along the path
                 *  - bandwidth allocated for USB 3.x but not used.
                 */
                if (tb_tunnel_direction_downstream(tunnel))
                        estimated_bw = estimated_down;
                else
                        estimated_bw = estimated_up;

                /*
                 * If there is reserved bandwidth for the group that is
                 * not yet released we report that too.
                 */
                tb_tunnel_dbg(tunnel,
                              "re-calculated estimated bandwidth %u (+ %u reserved) = %u Mb/s\n",
                              estimated_bw, group->reserved,
                              estimated_bw + group->reserved);

                if (usb4_dp_port_set_estimated_bandwidth(in,
                                estimated_bw + group->reserved))
                        tb_tunnel_warn(tunnel,
                                       "failed to update estimated bandwidth\n");
        }

        if (first_tunnel)
                tb_reclaim_usb3_bandwidth(tb, first_tunnel->src_port,
                                          first_tunnel->dst_port);

        tb_dbg(tb, "bandwidth estimation for group %u done\n", group->index);
}

static void tb_recalc_estimated_bandwidth(struct tb *tb)
{
        struct tb_cm *tcm = tb_priv(tb);
        int i;

        tb_dbg(tb, "bandwidth consumption changed, re-calculating estimated bandwidth\n");

        for (i = 0; i < ARRAY_SIZE(tcm->groups); i++) {
                struct tb_bandwidth_group *group = &tcm->groups[i];

                if (!list_empty(&group->ports))
                        tb_recalc_estimated_bandwidth_for_group(group);
        }

        tb_dbg(tb, "bandwidth re-calculation done\n");
}

static bool __release_group_bandwidth(struct tb_bandwidth_group *group)
{
        if (group->reserved) {
                tb_dbg(group->tb, "group %d released total %d Mb/s\n", group->index,
                        group->reserved);
                group->reserved = 0;
                return true;
        }
        return false;
}

static void __configure_group_sym(struct tb_bandwidth_group *group)
{
        struct tb_tunnel *tunnel;
        struct tb_port *in;

        if (list_empty(&group->ports))
                return;

        /*
         * All the tunnels in the group go through the same USB4 links
         * so we find the first one here and pass the IN and OUT
         * adapters to tb_configure_sym() which now transitions the
         * links back to symmetric if bandwidth requirement < asym_threshold.
         *
         * We do this here to avoid unnecessary transitions (for example
         * if the graphics released bandwidth for other tunnel in the
         * same group).
         */
        in = list_first_entry(&group->ports, struct tb_port, group_list);
        tunnel = tb_find_tunnel(group->tb, TB_TUNNEL_DP, in, NULL);
        if (tunnel)
                tb_configure_sym(group->tb, in, tunnel->dst_port, true);
}

static void tb_bandwidth_group_release_work(struct work_struct *work)
{
        struct tb_bandwidth_group *group =
                container_of(work, typeof(*group), release_work.work);
        struct tb *tb = group->tb;

        mutex_lock(&tb->lock);
        if (__release_group_bandwidth(group))
                tb_recalc_estimated_bandwidth(tb);
        __configure_group_sym(group);
        mutex_unlock(&tb->lock);
}

static void tb_init_bandwidth_groups(struct tb_cm *tcm)
{
        int i;

        for (i = 0; i < ARRAY_SIZE(tcm->groups); i++) {
                struct tb_bandwidth_group *group = &tcm->groups[i];

                group->tb = tcm_to_tb(tcm);
                group->index = i + 1;
                INIT_LIST_HEAD(&group->ports);
                INIT_DELAYED_WORK(&group->release_work,
                                  tb_bandwidth_group_release_work);
        }
}

static void tb_bandwidth_group_attach_port(struct tb_bandwidth_group *group,
                                           struct tb_port *in)
{
        if (!group || WARN_ON(in->group))
                return;

        in->group = group;
        list_add_tail(&in->group_list, &group->ports);

        tb_port_dbg(in, "attached to bandwidth group %d\n", group->index);
}

static struct tb_bandwidth_group *tb_find_free_bandwidth_group(struct tb_cm *tcm)
{
        int i;

        for (i = 0; i < ARRAY_SIZE(tcm->groups); i++) {
                struct tb_bandwidth_group *group = &tcm->groups[i];

                if (list_empty(&group->ports))
                        return group;
        }

        return NULL;
}

static struct tb_bandwidth_group *
tb_attach_bandwidth_group(struct tb_cm *tcm, struct tb_port *in,
                          struct tb_port *out)
{
        struct tb_bandwidth_group *group;
        struct tb_tunnel *tunnel;

        /*
         * Find all DP tunnels that go through all the same USB4 links
         * as this one. Because we always setup tunnels the same way we
         * can just check for the routers at both ends of the tunnels
         * and if they are the same we have a match.
         */
        list_for_each_entry(tunnel, &tcm->tunnel_list, list) {
                if (!tb_tunnel_is_dp(tunnel))
                        continue;

                if (tunnel->src_port->sw == in->sw &&
                    tunnel->dst_port->sw == out->sw) {
                        group = tunnel->src_port->group;
                        if (group) {
                                tb_bandwidth_group_attach_port(group, in);
                                return group;
                        }
                }
        }

        /* Pick up next available group then */
        group = tb_find_free_bandwidth_group(tcm);
        if (group)
                tb_bandwidth_group_attach_port(group, in);
        else
                tb_port_warn(in, "no available bandwidth groups\n");

        return group;
}

static void tb_discover_bandwidth_group(struct tb_cm *tcm, struct tb_port *in,
                                        struct tb_port *out)
{
        if (usb4_dp_port_bandwidth_mode_enabled(in)) {
                int index, i;

                index = usb4_dp_port_group_id(in);
                for (i = 0; i < ARRAY_SIZE(tcm->groups); i++) {
                        if (tcm->groups[i].index == index) {
                                tb_bandwidth_group_attach_port(&tcm->groups[i], in);
                                return;
                        }
                }
        }

        tb_attach_bandwidth_group(tcm, in, out);
}

static void tb_detach_bandwidth_group(struct tb_port *in)
{
        struct tb_bandwidth_group *group = in->group;

        if (group) {
                in->group = NULL;
                list_del_init(&in->group_list);

                tb_port_dbg(in, "detached from bandwidth group %d\n", group->index);

                /* No more tunnels so release the reserved bandwidth if any */
                if (list_empty(&group->ports)) {
                        cancel_delayed_work(&group->release_work);
                        __release_group_bandwidth(group);
                }
        }
}

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;

                        while (parent != tunnel->src_port->sw) {
                                parent->boot = true;
                                parent = tb_switch_parent(parent);
                        }
                } else if (tb_tunnel_is_dp(tunnel)) {
                        struct tb_port *in = tunnel->src_port;
                        struct tb_port *out = tunnel->dst_port;

                        /* Keep the domain from powering down */
                        pm_runtime_get_sync(&in->sw->dev);
                        pm_runtime_get_sync(&out->sw->dev);

                        tb_discover_bandwidth_group(tcm, in, out);
                }
        }
}

static void tb_deactivate_and_free_tunnel(struct tb_tunnel *tunnel)
{
        struct tb_port *src_port, *dst_port;
        struct tb *tb;

        if (!tunnel)
                return;

        tb_tunnel_deactivate(tunnel);
        list_del(&tunnel->list);

        tb = tunnel->tb;
        src_port = tunnel->src_port;
        dst_port = tunnel->dst_port;

        switch (tunnel->type) {
        case TB_TUNNEL_DP:
                tb_detach_bandwidth_group(src_port);
                /*
                 * In case of DP tunnel make sure the DP IN resource is
                 * deallocated properly.
                 */
                tb_switch_dealloc_dp_resource(src_port->sw, src_port);
                /*
                 * If bandwidth on a link is < asym_threshold
                 * transition the link to symmetric.
                 */
                tb_configure_sym(tb, src_port, dst_port, true);
                /* Now we can allow the domain to runtime suspend again */
                pm_runtime_mark_last_busy(&dst_port->sw->dev);
                pm_runtime_put_autosuspend(&dst_port->sw->dev);
                pm_runtime_mark_last_busy(&src_port->sw->dev);
                pm_runtime_put_autosuspend(&src_port->sw->dev);
                fallthrough;

        case TB_TUNNEL_USB3:
                tb_reclaim_usb3_bandwidth(tb, src_port, dst_port);
                break;

        default:
                /*
                 * PCIe and DMA tunnels do not consume guaranteed
                 * bandwidth.
                 */
                break;
        }

        tb_tunnel_put(tunnel);
}

/*
 * tb_free_invalid_tunnels() - destroy tunnels of devices that have gone away
 */
static void tb_free_invalid_tunnels(struct tb *tb)
{
        struct tb_cm *tcm = tb_priv(tb);
        struct tb_tunnel *tunnel;
        struct tb_tunnel *n;

        list_for_each_entry_safe(tunnel, n, &tcm->tunnel_list, list) {
                if (tb_tunnel_is_invalid(tunnel))
                        tb_deactivate_and_free_tunnel(tunnel);
        }
}

/*
 * tb_free_unplugged_children() - traverse hierarchy and free unplugged switches
 */
static void tb_free_unplugged_children(struct tb_switch *sw)
{
        struct tb_port *port;

        tb_switch_for_each_port(sw, port) {
                if (!tb_port_has_remote(port))
                        continue;

                if (port->remote->sw->is_unplugged) {
                        tb_retimer_remove_all(port);
                        tb_remove_dp_resources(port->remote->sw);
                        tb_switch_unconfigure_link(port->remote->sw);
                        tb_switch_set_link_width(port->remote->sw,
                                                 TB_LINK_WIDTH_SINGLE);
                        tb_switch_remove(port->remote->sw);
                        port->remote = NULL;
                        if (port->dual_link_port)
                                port->dual_link_port->remote = NULL;
                } else {
                        tb_free_unplugged_children(port->remote->sw);
                }
        }
}

static struct tb_port *tb_find_pcie_down(struct tb_switch *sw,
                                         const struct tb_port *port)
{
        struct tb_port *down = NULL;

        /*
         * To keep plugging devices consistently in the same PCIe
         * hierarchy, do mapping here for switch downstream PCIe ports.
         */
        if (tb_switch_is_usb4(sw)) {
                down = usb4_switch_map_pcie_down(sw, port);
        } else if (!tb_route(sw)) {
                int phy_port = tb_phy_port_from_link(port->port);
                int index;

                /*
                 * Hard-coded Thunderbolt port to PCIe down port mapping
                 * per controller.
                 */
                if (tb_switch_is_cactus_ridge(sw) ||
                    tb_switch_is_alpine_ridge(sw))
                        index = !phy_port ? 6 : 7;
                else if (tb_switch_is_falcon_ridge(sw))
                        index = !phy_port ? 6 : 8;
                else if (tb_switch_is_titan_ridge(sw))
                        index = !phy_port ? 8 : 9;
                else
                        goto out;

                /* Validate the hard-coding */
                if (WARN_ON(index > sw->config.max_port_number))
                        goto out;

                down = &sw->ports[index];
        }

        if (down) {
                if (WARN_ON(!tb_port_is_pcie_down(down)))
                        goto out;
                if (tb_pci_port_is_enabled(down))
                        goto out;

                return down;
        }

out:
        return tb_find_unused_port(sw, TB_TYPE_PCIE_DOWN);
}

static struct tb_port *tb_find_dp_out(struct tb *tb, struct tb_port *in)
{
        struct tb_port *host_port, *port;
        struct tb_cm *tcm = tb_priv(tb);

        host_port = tb_route(in->sw) ?
                tb_port_at(tb_route(in->sw), tb->root_switch) : NULL;

        list_for_each_entry(port, &tcm->dp_resources, list) {
                if (!tb_port_is_dpout(port))
                        continue;

                if (tb_port_is_enabled(port)) {
                        tb_port_dbg(port, "DP OUT in use\n");
                        continue;
                }

                /* Needs to be on different routers */
                if (in->sw == port->sw) {
                        tb_port_dbg(port, "skipping DP OUT on same router\n");
                        continue;
                }

                tb_port_dbg(port, "DP OUT available\n");

                /*
                 * Keep the DP tunnel under the topology starting from
                 * the same host router downstream port.
                 */
                if (host_port && tb_route(port->sw)) {
                        struct tb_port *p;

                        p = tb_port_at(tb_route(port->sw), tb->root_switch);
                        if (p != host_port)
                                continue;
                }

                return port;
        }

        return NULL;
}

static void tb_dp_tunnel_active(struct tb_tunnel *tunnel, void *data)
{
        struct tb_port *in = tunnel->src_port;
        struct tb_port *out = tunnel->dst_port;
        struct tb *tb = data;

        mutex_lock(&tb->lock);
        if (tb_tunnel_is_active(tunnel)) {
                int consumed_up, consumed_down, ret;

                tb_tunnel_dbg(tunnel, "DPRX capabilities read completed\n");

                /* If fail reading tunnel's consumed bandwidth, tear it down */
                ret = tb_tunnel_consumed_bandwidth(tunnel, &consumed_up,
                                                   &consumed_down);
                if (ret) {
                        tb_tunnel_warn(tunnel,
                                       "failed to read consumed bandwidth, tearing down\n");
                        tb_deactivate_and_free_tunnel(tunnel);
                } else {
                        tb_reclaim_usb3_bandwidth(tb, in, out);
                        /*
                         * Transition the links to asymmetric if the
                         * consumption exceeds the threshold.
                         */
                        tb_configure_asym(tb, in, out, consumed_up,
                                          consumed_down);
                        /*
                         * Update the domain with the new bandwidth
                         * estimation.
                         */
                        tb_recalc_estimated_bandwidth(tb);
                        /*
                         * In case DP tunnel exists, change host
                         * router's 1st children TMU mode to HiFi for
                         * CL0s to work.
                         */
                        tb_increase_tmu_accuracy(tunnel);
                }
        } else {
                struct tb_port *in = tunnel->src_port;

                /*
                 * This tunnel failed to establish. This means DPRX
                 * negotiation most likely did not complete which
                 * happens either because there is no graphics driver
                 * loaded or not all DP cables where connected to the
                 * discrete router.
                 *
                 * In both cases we remove the DP IN adapter from the
                 * available resources as it is not usable. This will
                 * also tear down the tunnel and try to re-use the
                 * released DP OUT.
                 *
                 * It will be added back only if there is hotplug for
                 * the DP IN again.
                 */
                tb_tunnel_warn(tunnel, "not active, tearing down\n");
                tb_dp_resource_unavailable(tb, in, "DPRX negotiation failed");
        }
        mutex_unlock(&tb->lock);

        tb_domain_put(tb);
}

static void tb_tunnel_one_dp(struct tb *tb, struct tb_port *in,
                             struct tb_port *out)
{
        int available_up, available_down, ret, link_nr;
        struct tb_cm *tcm = tb_priv(tb);
        struct tb_tunnel *tunnel;

        /*
         * This is only applicable to links that are not bonded (so
         * when Thunderbolt 1 hardware is involved somewhere in the
         * topology). For these try to share the DP bandwidth between
         * the two lanes.
         */
        link_nr = 1;
        list_for_each_entry(tunnel, &tcm->tunnel_list, list) {
                if (tb_tunnel_is_dp(tunnel)) {
                        link_nr = 0;
                        break;
                }
        }

        /*
         * DP stream needs the domain to be active so runtime resume
         * both ends of the tunnel.
         *
         * This should bring the routers in the middle active as well
         * and keeps the domain from runtime suspending while the DP
         * tunnel is active.
         */
        pm_runtime_get_sync(&in->sw->dev);
        pm_runtime_get_sync(&out->sw->dev);

        if (tb_switch_alloc_dp_resource(in->sw, in)) {
                tb_port_dbg(in, "no resource available for DP IN, not tunneling\n");
                goto err_rpm_put;
        }

        if (!tb_attach_bandwidth_group(tcm, in, out))
                goto err_dealloc_dp;

        /* Make all unused USB3 bandwidth available for the new DP tunnel */
        ret = tb_release_unused_usb3_bandwidth(tb, in, out);
        if (ret) {
                tb_warn(tb, "failed to release unused bandwidth\n");
                goto err_detach_group;
        }

        ret = tb_available_bandwidth(tb, in, out, &available_up, &available_down,
                                     true);
        if (ret) {
                tb_tunnel_event(tb, TB_TUNNEL_NO_BANDWIDTH, TB_TUNNEL_DP, in, out);
                goto err_reclaim_usb;
        }

        tb_dbg(tb, "available bandwidth for new DP tunnel %u/%u Mb/s\n",
               available_up, available_down);

        tunnel = tb_tunnel_alloc_dp(tb, in, out, link_nr, available_up,
                                    available_down, tb_dp_tunnel_active,
                                    tb_domain_get(tb));
        if (!tunnel) {
                tb_port_dbg(out, "could not allocate DP tunnel\n");
                goto err_reclaim_usb;
        }

        list_add_tail(&tunnel->list, &tcm->tunnel_list);

        ret = tb_tunnel_activate(tunnel);
        if (ret && ret != -EINPROGRESS) {
                tb_port_info(out, "DP tunnel activation failed, aborting\n");
                list_del(&tunnel->list);
                goto err_free;
        }

        return;

err_free:
        tb_tunnel_put(tunnel);
err_reclaim_usb:
        tb_reclaim_usb3_bandwidth(tb, in, out);
        tb_domain_put(tb);
err_detach_group:
        tb_detach_bandwidth_group(in);
err_dealloc_dp:
        tb_switch_dealloc_dp_resource(in->sw, in);
err_rpm_put:
        pm_runtime_mark_last_busy(&out->sw->dev);
        pm_runtime_put_autosuspend(&out->sw->dev);
        pm_runtime_mark_last_busy(&in->sw->dev);
        pm_runtime_put_autosuspend(&in->sw->dev);
}

static void tb_tunnel_dp(struct tb *tb)
{
        struct tb_cm *tcm = tb_priv(tb);
        struct tb_port *port, *in, *out;

        if (!tb_acpi_may_tunnel_dp()) {
                tb_dbg(tb, "DP tunneling disabled, not creating tunnel\n");
                return;
        }

        /*
         * Find pair of inactive DP IN and DP OUT adapters and then
         * establish a DP tunnel between them.
         */
        tb_dbg(tb, "looking for DP IN <-> DP OUT pairs:\n");

        in = NULL;
        out = NULL;
        list_for_each_entry(port, &tcm->dp_resources, list) {
                if (!tb_port_is_dpin(port))
                        continue;

                if (tb_port_is_enabled(port)) {
                        tb_port_dbg(port, "DP IN in use\n");
                        continue;
                }

                in = port;
                tb_port_dbg(in, "DP IN available\n");

                out = tb_find_dp_out(tb, port);
                if (out)
                        tb_tunnel_one_dp(tb, in, out);
                else
                        tb_port_dbg(in, "no suitable DP OUT adapter available, not tunneling\n");
        }

        if (!in)
                tb_dbg(tb, "no suitable DP IN adapter available, not tunneling\n");
}

static void tb_enter_redrive(struct tb_port *port)
{
        struct tb_switch *sw = port->sw;

        if (!(sw->quirks & QUIRK_KEEP_POWER_IN_DP_REDRIVE))
                return;

        /*
         * If we get hot-unplug for the DP IN port of the host router
         * and the DP resource is not available anymore it means there
         * is a monitor connected directly to the Type-C port and we are
         * in "redrive" mode. For this to work we cannot enter RTD3 so
         * we bump up the runtime PM reference count here.
         */
        if (!tb_port_is_dpin(port))
                return;
        if (tb_route(sw))
                return;
        if (!tb_switch_query_dp_resource(sw, port)) {
                port->redrive = true;
                pm_runtime_get(&sw->dev);
                tb_port_dbg(port, "enter redrive mode, keeping powered\n");
        }
}

static void tb_exit_redrive(struct tb_port *port)
{
        struct tb_switch *sw = port->sw;

        if (!(sw->quirks & QUIRK_KEEP_POWER_IN_DP_REDRIVE))
                return;

        if (!tb_port_is_dpin(port))
                return;
        if (tb_route(sw))
                return;
        if (port->redrive && tb_switch_query_dp_resource(sw, port)) {
                port->redrive = false;
                pm_runtime_put(&sw->dev);
                tb_port_dbg(port, "exit redrive mode\n");
        }
}

static void tb_switch_enter_redrive(struct tb_switch *sw)
{
        struct tb_port *port;

        tb_switch_for_each_port(sw, port)
                tb_enter_redrive(port);
}

/*
 * Called during system and runtime suspend to forcefully exit redrive
 * mode without querying whether the resource is available.
 */
static void tb_switch_exit_redrive(struct tb_switch *sw)
{
        struct tb_port *port;

        if (!(sw->quirks & QUIRK_KEEP_POWER_IN_DP_REDRIVE))
                return;

        tb_switch_for_each_port(sw, port) {
                if (!tb_port_is_dpin(port))
                        continue;

                if (port->redrive) {
                        port->redrive = false;
                        pm_runtime_put(&sw->dev);
                        tb_port_dbg(port, "exit redrive mode\n");
                }
        }
}

static void tb_dp_resource_unavailable(struct tb *tb, struct tb_port *port,
                                       const char *reason)
{
        struct tb_port *in, *out;
        struct tb_tunnel *tunnel;

        if (tb_port_is_dpin(port)) {
                tb_port_dbg(port, "DP IN resource unavailable: %s\n", reason);
                in = port;
                out = NULL;
        } else {
                tb_port_dbg(port, "DP OUT resource unavailable: %s\n", reason);
                in = NULL;
                out = port;
        }

        tunnel = tb_find_tunnel(tb, TB_TUNNEL_DP, in, out);
        if (tunnel)
                tb_deactivate_and_free_tunnel(tunnel);
        else
                tb_enter_redrive(port);
        list_del_init(&port->list);

        /*
         * See if there is another DP OUT port that can be used for
         * to create another tunnel.
         */
        tb_recalc_estimated_bandwidth(tb);
        tb_tunnel_dp(tb);
}

static void tb_dp_resource_available(struct tb *tb, struct tb_port *port)
{
        struct tb_cm *tcm = tb_priv(tb);
        struct tb_port *p;

        if (tb_port_is_enabled(port))
                return;

        list_for_each_entry(p, &tcm->dp_resources, list) {
                if (p == port)
                        return;
        }

        tb_port_dbg(port, "DP %s resource available after hotplug\n",
                    tb_port_is_dpin(port) ? "IN" : "OUT");
        list_add_tail(&port->list, &tcm->dp_resources);
        tb_exit_redrive(port);

        /* Look for suitable DP IN <-> DP OUT pairs now */
        tb_tunnel_dp(tb);
}

static void tb_disconnect_and_release_dp(struct tb *tb)
{
        struct tb_cm *tcm = tb_priv(tb);
        struct tb_tunnel *tunnel, *n;

        /*
         * Tear down all DP tunnels and release their resources. They
         * will be re-established after resume based on plug events.
         */
        list_for_each_entry_safe_reverse(tunnel, n, &tcm->tunnel_list, list) {
                if (tb_tunnel_is_dp(tunnel))
                        tb_deactivate_and_free_tunnel(tunnel);
        }

        while (!list_empty(&tcm->dp_resources)) {
                struct tb_port *port;

                port = list_first_entry(&tcm->dp_resources,
                                        struct tb_port, list);
                list_del_init(&port->list);
        }
}

static int tb_disconnect_pci(struct tb *tb, struct tb_switch *sw)
{
        struct tb_tunnel *tunnel;
        struct tb_port *up;

        up = tb_switch_find_port(sw, TB_TYPE_PCIE_UP);
        if (WARN_ON(!up))
                return -ENODEV;

        tunnel = tb_find_tunnel(tb, TB_TUNNEL_PCI, NULL, up);
        if (WARN_ON(!tunnel))
                return -ENODEV;

        tb_switch_xhci_disconnect(sw);

        tb_tunnel_deactivate(tunnel);
        list_del(&tunnel->list);
        tb_tunnel_put(tunnel);
        return 0;
}

static int tb_tunnel_pci(struct tb *tb, struct tb_switch *sw)
{
        struct tb_port *up, *down, *port;
        struct tb_cm *tcm = tb_priv(tb);
        struct tb_tunnel *tunnel;

        up = tb_switch_find_port(sw, TB_TYPE_PCIE_UP);
        if (!up)
                return 0;

        /*
         * Look up available down port. Since we are chaining it should
         * be found right above this switch.
         */
        port = tb_switch_downstream_port(sw);
        down = tb_find_pcie_down(tb_switch_parent(sw), port);
        if (!down)
                return 0;

        tunnel = tb_tunnel_alloc_pci(tb, up, down);
        if (!tunnel)
                return -ENOMEM;

        if (tb_tunnel_activate(tunnel)) {
                tb_port_info(up,
                             "PCIe tunnel activation failed, aborting\n");
                tb_tunnel_put(tunnel);
                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");

        if (tb_switch_xhci_connect(sw))
                tb_sw_warn(sw, "failed to connect xHCI\n");

        list_add_tail(&tunnel->list, &tcm->tunnel_list);
        return 0;
}

static int tb_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd,
                                    int transmit_path, int transmit_ring,
                                    int receive_path, int receive_ring)
{
        struct tb_cm *tcm = tb_priv(tb);
        struct tb_port *nhi_port, *dst_port;
        struct tb_tunnel *tunnel;
        struct tb_switch *sw;
        int ret;

        sw = tb_to_switch(xd->dev.parent);
        dst_port = tb_port_at(xd->route, sw);
        nhi_port = tb_switch_find_port(tb->root_switch, TB_TYPE_NHI);

        mutex_lock(&tb->lock);

        /*
         * When tunneling DMA paths the link should not enter CL states
         * so disable them now.
         */
        tb_disable_clx(sw);

        tunnel = tb_tunnel_alloc_dma(tb, nhi_port, dst_port, transmit_path,
                                     transmit_ring, receive_path, receive_ring);
        if (!tunnel) {
                ret = -ENOMEM;
                goto err_clx;
        }

        if (tb_tunnel_activate(tunnel)) {
                tb_port_info(nhi_port,
                             "DMA tunnel activation failed, aborting\n");
                ret = -EIO;
                goto err_free;
        }

        list_add_tail(&tunnel->list, &tcm->tunnel_list);
        mutex_unlock(&tb->lock);
        return 0;

err_free:
        tb_tunnel_put(tunnel);
err_clx:
        tb_enable_clx(sw);
        mutex_unlock(&tb->lock);

        return ret;
}

static void __tb_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd,
                                          int transmit_path, int transmit_ring,
                                          int receive_path, int receive_ring)
{
        struct tb_cm *tcm = tb_priv(tb);
        struct tb_port *nhi_port, *dst_port;
        struct tb_tunnel *tunnel, *n;
        struct tb_switch *sw;

        sw = tb_to_switch(xd->dev.parent);
        dst_port = tb_port_at(xd->route, sw);
        nhi_port = tb_switch_find_port(tb->root_switch, TB_TYPE_NHI);

        list_for_each_entry_safe(tunnel, n, &tcm->tunnel_list, list) {
                if (!tb_tunnel_is_dma(tunnel))
                        continue;
                if (tunnel->src_port != nhi_port || tunnel->dst_port != dst_port)
                        continue;

                if (tb_tunnel_match_dma(tunnel, transmit_path, transmit_ring,
                                        receive_path, receive_ring))
                        tb_deactivate_and_free_tunnel(tunnel);
        }

        /*
         * Try to re-enable CL states now, it is OK if this fails
         * because we may still have another DMA tunnel active through
         * the same host router USB4 downstream port.
         */
        tb_enable_clx(sw);
}

static int tb_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd,
                                       int transmit_path, int transmit_ring,
                                       int receive_path, int receive_ring)
{
        if (!xd->is_unplugged) {
                mutex_lock(&tb->lock);
                __tb_disconnect_xdomain_paths(tb, xd, transmit_path,
                                              transmit_ring, receive_path,
                                              receive_ring);
                mutex_unlock(&tb->lock);
        }
        return 0;
}

/* hotplug handling */

/*
 * tb_handle_hotplug() - handle hotplug event
 *
 * Executes on tb->wq.
 */
static void tb_handle_hotplug(struct work_struct *work)
{
        struct tb_hotplug_event *ev = container_of(work, typeof(*ev), work.work);
        struct tb *tb = ev->tb;
        struct tb_cm *tcm = tb_priv(tb);
        struct tb_switch *sw;
        struct tb_port *port;

        /* Bring the domain back from sleep if it was suspended */
        pm_runtime_get_sync(&tb->dev);

        mutex_lock(&tb->lock);
        if (!tcm->hotplug_active)
                goto out; /* during init, suspend or shutdown */

        sw = tb_switch_find_by_route(tb, ev->route);
        if (!sw) {
                tb_warn(tb,
                        "hotplug event from non existent switch %llx:%x (unplug: %d)\n",
                        ev->route, ev->port, ev->unplug);
                goto out;
        }
        if (ev->port > sw->config.max_port_number) {
                tb_warn(tb,
                        "hotplug event from non existent port %llx:%x (unplug: %d)\n",
                        ev->route, ev->port, ev->unplug);
                goto put_sw;
        }
        port = &sw->ports[ev->port];
        if (tb_is_upstream_port(port)) {
                tb_dbg(tb, "hotplug event for upstream port %llx:%x (unplug: %d)\n",
                       ev->route, ev->port, ev->unplug);
                goto put_sw;
        }

        pm_runtime_get_sync(&sw->dev);

        if (ev->unplug) {
                tb_retimer_remove_all(port);

                if (tb_port_has_remote(port)) {
                        tb_port_dbg(port, "switch unplugged\n");
                        tb_sw_set_unplugged(port->remote->sw);
                        tb_free_invalid_tunnels(tb);
                        tb_remove_dp_resources(port->remote->sw);
                        tb_switch_tmu_disable(port->remote->sw);
                        tb_switch_unconfigure_link(port->remote->sw);
                        tb_switch_set_link_width(port->remote->sw,
                                                 TB_LINK_WIDTH_SINGLE);
                        tb_switch_remove(port->remote->sw);
                        port->remote = NULL;
                        if (port->dual_link_port)
                                port->dual_link_port->remote = NULL;
                        /* Maybe we can create another DP tunnel */
                        tb_recalc_estimated_bandwidth(tb);
                        tb_tunnel_dp(tb);
                } else if (port->xdomain) {
                        struct tb_xdomain *xd = tb_xdomain_get(port->xdomain);

                        tb_port_dbg(port, "xdomain unplugged\n");
                        /*
                         * Service drivers are unbound during
                         * tb_xdomain_remove() so setting XDomain as
                         * unplugged here prevents deadlock if they call
                         * tb_xdomain_disable_paths(). We will tear down
                         * all the tunnels below.
                         */
                        xd->is_unplugged = true;
                        tb_xdomain_remove(xd);
                        port->xdomain = NULL;
                        __tb_disconnect_xdomain_paths(tb, xd, -1, -1, -1, -1);
                        tb_xdomain_put(xd);
                        tb_port_unconfigure_xdomain(port);
                } else if (tb_port_is_dpout(port) || tb_port_is_dpin(port)) {
                        tb_dp_resource_unavailable(tb, port, "adapter unplug");
                } else if (!port->port) {
                        tb_sw_dbg(sw, "xHCI disconnect request\n");
                        tb_switch_xhci_disconnect(sw);
                } else {
                        tb_port_dbg(port,
                                   "got unplug event for disconnected port, ignoring\n");
                }
        } else if (port->remote) {
                tb_port_dbg(port, "got plug event for connected port, ignoring\n");
        } else if (!port->port && sw->authorized) {
                tb_sw_dbg(sw, "xHCI connect request\n");
                tb_switch_xhci_connect(sw);
        } else {
                if (tb_port_is_null(port)) {
                        tb_port_dbg(port, "hotplug: scanning\n");
                        tb_scan_port(port);
                        if (!port->remote)
                                tb_port_dbg(port, "hotplug: no switch found\n");
                } else if (tb_port_is_dpout(port) || tb_port_is_dpin(port)) {
                        tb_dp_resource_available(tb, port);
                }
        }

        pm_runtime_mark_last_busy(&sw->dev);
        pm_runtime_put_autosuspend(&sw->dev);

put_sw:
        tb_switch_put(sw);
out:
        mutex_unlock(&tb->lock);

        pm_runtime_mark_last_busy(&tb->dev);
        pm_runtime_put_autosuspend(&tb->dev);

        kfree(ev);
}

static int tb_alloc_dp_bandwidth(struct tb_tunnel *tunnel, int *requested_up,
                                 int *requested_down)
{
        int allocated_up, allocated_down, available_up, available_down, ret;
        int requested_up_corrected, requested_down_corrected, granularity;
        int max_up, max_down, max_up_rounded, max_down_rounded;
        struct tb_bandwidth_group *group;
        struct tb *tb = tunnel->tb;
        struct tb_port *in, *out;
        bool downstream;

        ret = tb_tunnel_allocated_bandwidth(tunnel, &allocated_up, &allocated_down);
        if (ret)
                return ret;

        in = tunnel->src_port;
        out = tunnel->dst_port;

        tb_tunnel_dbg(tunnel, "bandwidth allocated currently %d/%d Mb/s\n",
                      allocated_up, allocated_down);

        /*
         * If we get rounded up request from graphics side, say HBR2 x 4
         * that is 17500 instead of 17280 (this is because of the
         * granularity), we allow it too. Here the graphics has already
         * negotiated with the DPRX the maximum possible rates (which is
         * 17280 in this case).
         *
         * Since the link cannot go higher than 17280 we use that in our
         * calculations but the DP IN adapter Allocated BW write must be
         * the same value (17500) otherwise the adapter will mark it as
         * failed for graphics.
         */
        ret = tb_tunnel_maximum_bandwidth(tunnel, &max_up, &max_down);
        if (ret)
                goto fail;

        ret = usb4_dp_port_granularity(in);
        if (ret < 0)
                goto fail;
        granularity = ret;

        max_up_rounded = roundup(max_up, granularity);
        max_down_rounded = roundup(max_down, granularity);

        /*
         * This will "fix" the request down to the maximum supported
         * rate * lanes if it is at the maximum rounded up level.
         */
        requested_up_corrected = *requested_up;
        if (requested_up_corrected == max_up_rounded)
                requested_up_corrected = max_up;
        else if (requested_up_corrected < 0)
                requested_up_corrected = 0;
        requested_down_corrected = *requested_down;
        if (requested_down_corrected == max_down_rounded)
                requested_down_corrected = max_down;
        else if (requested_down_corrected < 0)
                requested_down_corrected = 0;

        tb_tunnel_dbg(tunnel, "corrected bandwidth request %d/%d Mb/s\n",
                      requested_up_corrected, requested_down_corrected);

        if ((*requested_up >= 0 && requested_up_corrected > max_up_rounded) ||
            (*requested_down >= 0 && requested_down_corrected > max_down_rounded)) {
                tb_tunnel_dbg(tunnel,
                              "bandwidth request too high (%d/%d Mb/s > %d/%d Mb/s)\n",
                              requested_up_corrected, requested_down_corrected,
                              max_up_rounded, max_down_rounded);
                ret = -ENOBUFS;
                goto fail;
        }

        downstream = tb_tunnel_direction_downstream(tunnel);
        group = in->group;

        if ((*requested_up >= 0 && requested_up_corrected <= allocated_up) ||
            (*requested_down >= 0 && requested_down_corrected <= allocated_down)) {
                if (tunnel->bw_mode) {
                        int reserved;
                        /*
                         * If requested bandwidth is less or equal than
                         * what is currently allocated to that tunnel we
                         * simply change the reservation of the tunnel
                         * and add the released bandwidth for the group
                         * for the next 10s. Then we release it for
                         * others to use.
                         */
                        if (downstream)
                                reserved = allocated_down - *requested_down;
                        else
                                reserved = allocated_up - *requested_up;

                        if (reserved > 0) {
                                group->reserved += reserved;
                                tb_dbg(tb, "group %d reserved %d total %d Mb/s\n",
                                       group->index, reserved, group->reserved);

                                /*
                                 * If it was not already pending,
                                 * schedule release now. If it is then
                                 * postpone it for the next 10s (unless
                                 * it is already running in which case
                                 * the 10s already expired and we should
                                 * give the reserved back to others).
                                 */
                                mod_delayed_work(system_percpu_wq, &group->release_work,
                                        msecs_to_jiffies(TB_RELEASE_BW_TIMEOUT));
                        }
                }

                ret = tb_tunnel_alloc_bandwidth(tunnel, requested_up,
                                                requested_down);
                if (ret)
                        goto fail;

                return 0;
        }

        /*
         * More bandwidth is requested. Release all the potential
         * bandwidth from USB3 first.
         */
        ret = tb_release_unused_usb3_bandwidth(tb, in, out);
        if (ret)
                goto fail;

        /*
         * Then go over all tunnels that cross the same USB4 ports (they
         * are also in the same group but we use the same function here
         * that we use with the normal bandwidth allocation).
         */
        ret = tb_available_bandwidth(tb, in, out, &available_up, &available_down,
                                     true);
        if (ret)
                goto reclaim;

        tb_tunnel_dbg(tunnel, "bandwidth available for allocation %d/%d (+ %u reserved) Mb/s\n",
                      available_up, available_down, group->reserved);

        if ((*requested_up >= 0 &&
                available_up + group->reserved >= requested_up_corrected) ||
            (*requested_down >= 0 &&
                available_down + group->reserved >= requested_down_corrected)) {
                int released = 0;

                /*
                 * If bandwidth on a link is >= asym_threshold
                 * transition the link to asymmetric.
                 */
                ret = tb_configure_asym(tb, in, out, *requested_up,
                                        *requested_down);
                if (ret) {
                        tb_configure_sym(tb, in, out, true);
                        goto fail;
                }

                ret = tb_tunnel_alloc_bandwidth(tunnel, requested_up,
                                                requested_down);
                if (ret) {
                        tb_tunnel_warn(tunnel, "failed to allocate bandwidth\n");
                        tb_configure_sym(tb, in, out, true);
                }

                if (downstream) {
                        if (*requested_down > available_down)
                                released = *requested_down - available_down;
                } else {
                        if (*requested_up > available_up)
                                released = *requested_up - available_up;
                }
                if (released) {
                        group->reserved -= released;
                        tb_dbg(tb, "group %d released %d total %d Mb/s\n",
                               group->index, released, group->reserved);
                }
        } else {
                ret = -ENOBUFS;
        }

reclaim:
        tb_reclaim_usb3_bandwidth(tb, in, out);
fail:
        if (ret && ret != -ENODEV) {
                /*
                 * Write back the same allocated (so no change), this
                 * makes the DPTX request fail on graphics side.
                 */
                tb_tunnel_dbg(tunnel,
                              "failing the request by rewriting allocated %d/%d Mb/s\n",
                              allocated_up, allocated_down);
                tb_tunnel_alloc_bandwidth(tunnel, &allocated_up, &allocated_down);
                tb_tunnel_event(tb, TB_TUNNEL_NO_BANDWIDTH, TB_TUNNEL_DP, in, out);
        }

        return ret;
}

static void tb_handle_dp_bandwidth_request(struct work_struct *work)
{
        struct tb_hotplug_event *ev = container_of(work, typeof(*ev), work.work);
        int requested_bw, requested_up, requested_down, ret;
        struct tb_tunnel *tunnel;
        struct tb *tb = ev->tb;
        struct tb_cm *tcm = tb_priv(tb);
        struct tb_switch *sw;
        struct tb_port *in;

        pm_runtime_get_sync(&tb->dev);

        mutex_lock(&tb->lock);
        if (!tcm->hotplug_active)
                goto unlock;

        sw = tb_switch_find_by_route(tb, ev->route);
        if (!sw) {
                tb_warn(tb, "bandwidth request from non-existent router %llx\n",
                        ev->route);
                goto unlock;
        }

        in = &sw->ports[ev->port];
        if (!tb_port_is_dpin(in)) {
                tb_port_warn(in, "bandwidth request to non-DP IN adapter\n");
                goto put_sw;
        }

        tb_port_dbg(in, "handling bandwidth allocation request, retry %d\n", ev->retry);

        tunnel = tb_find_tunnel(tb, TB_TUNNEL_DP, in, NULL);
        if (!tunnel) {
                tb_port_warn(in, "failed to find tunnel\n");
                goto put_sw;
        }

        if (!usb4_dp_port_bandwidth_mode_enabled(in)) {
                if (tunnel->bw_mode) {
                        /*
                         * Reset the tunnel back to use the legacy
                         * allocation.
                         */
                        tunnel->bw_mode = false;
                        tb_port_dbg(in, "DPTX disabled bandwidth allocation mode\n");
                } else {
                        tb_port_warn(in, "bandwidth allocation mode not enabled\n");
                }
                goto put_sw;
        }

        ret = usb4_dp_port_requested_bandwidth(in);
        if (ret < 0) {
                if (ret == -ENODATA) {
                        /*
                         * There is no request active so this means the
                         * BW allocation mode was enabled from graphics
                         * side. At this point we know that the graphics
                         * driver has read the DPRX capabilities so we
                         * can offer better bandwidth estimation.
                         */
                        tb_port_dbg(in, "DPTX enabled bandwidth allocation mode, updating estimated bandwidth\n");
                        tb_recalc_estimated_bandwidth(tb);
                } else {
                        tb_port_warn(in, "failed to read requested bandwidth\n");
                }
                goto put_sw;
        }
        requested_bw = ret;

        tb_port_dbg(in, "requested bandwidth %d Mb/s\n", requested_bw);

        if (tb_tunnel_direction_downstream(tunnel)) {
                requested_up = -1;
                requested_down = requested_bw;
        } else {
                requested_up = requested_bw;
                requested_down = -1;
        }

        ret = tb_alloc_dp_bandwidth(tunnel, &requested_up, &requested_down);
        if (ret) {
                if (ret == -ENOBUFS) {
                        tb_tunnel_warn(tunnel,
                                       "not enough bandwidth available\n");
                } else if (ret == -ENOTCONN) {
                        tb_tunnel_dbg(tunnel, "not active yet\n");
                        /*
                         * We got bandwidth allocation request but the
                         * tunnel is not yet active. This means that
                         * tb_dp_tunnel_active() is not yet called for
                         * this tunnel. Allow it some time and retry
                         * this request a couple of times.
                         */
                        if (ev->retry < TB_BW_ALLOC_RETRIES) {
                                tb_tunnel_dbg(tunnel,
                                              "retrying bandwidth allocation request\n");
                                tb_queue_dp_bandwidth_request(tb, ev->route,
                                                              ev->port,
                                                              ev->retry + 1,
                                                              msecs_to_jiffies(50));
                        } else {
                                tb_tunnel_dbg(tunnel,
                                              "run out of retries, failing the request");
                        }
                } else {
                        tb_tunnel_warn(tunnel,
                                       "failed to change bandwidth allocation\n");
                }
        } else {
                tb_tunnel_dbg(tunnel,
                              "bandwidth allocation changed to %d/%d Mb/s\n",
                              requested_up, requested_down);

                /* Update other clients about the allocation change */
                tb_recalc_estimated_bandwidth(tb);
        }

put_sw:
        tb_switch_put(sw);
unlock:
        mutex_unlock(&tb->lock);

        pm_runtime_mark_last_busy(&tb->dev);
        pm_runtime_put_autosuspend(&tb->dev);

        kfree(ev);
}

static void tb_queue_dp_bandwidth_request(struct tb *tb, u64 route, u8 port,
                                          int retry, unsigned long delay)
{
        struct tb_hotplug_event *ev;

        ev = kmalloc_obj(*ev);
        if (!ev)
                return;

        ev->tb = tb;
        ev->route = route;
        ev->port = port;
        ev->retry = retry;
        INIT_DELAYED_WORK(&ev->work, tb_handle_dp_bandwidth_request);
        queue_delayed_work(tb->wq, &ev->work, delay);
}

static void tb_handle_notification(struct tb *tb, u64 route,
                                   const struct cfg_error_pkg *error)
{

        switch (error->error) {
        case TB_CFG_ERROR_PCIE_WAKE:
        case TB_CFG_ERROR_DP_CON_CHANGE:
        case TB_CFG_ERROR_DPTX_DISCOVERY:
                if (tb_cfg_ack_notification(tb->ctl, route, error))
                        tb_warn(tb, "could not ack notification on %llx\n",
                                route);
                break;

        case TB_CFG_ERROR_DP_BW:
                if (tb_cfg_ack_notification(tb->ctl, route, error))
                        tb_warn(tb, "could not ack notification on %llx\n",
                                route);
                tb_queue_dp_bandwidth_request(tb, route, error->port, 0, 0);
                break;

        default:
                /* Ignore for now */
                break;
        }
}

/*
 * tb_schedule_hotplug_handler() - callback function for the control channel
 *
 * Delegates to tb_handle_hotplug.
 */
static void tb_handle_event(struct tb *tb, enum tb_cfg_pkg_type type,
                            const void *buf, size_t size)
{
        const struct cfg_event_pkg *pkg = buf;
        u64 route = tb_cfg_get_route(&pkg->header);

        switch (type) {
        case TB_CFG_PKG_ERROR:
                tb_handle_notification(tb, route, (const struct cfg_error_pkg *)buf);
                return;
        case TB_CFG_PKG_EVENT:
                break;
        default:
                tb_warn(tb, "unexpected event %#x, ignoring\n", type);
                return;
        }

        if (tb_cfg_ack_plug(tb->ctl, route, pkg->port, pkg->unplug)) {
                tb_warn(tb, "could not ack plug event on %llx:%x\n", route,
                        pkg->port);
        }

        tb_queue_hotplug(tb, route, pkg->port, pkg->unplug);
}

static void tb_stop(struct tb *tb)
{
        struct tb_cm *tcm = tb_priv(tb);
        struct tb_tunnel *tunnel;
        struct tb_tunnel *n;

        cancel_delayed_work(&tcm->remove_work);
        /* tunnels are only present after everything has been initialized */
        list_for_each_entry_safe(tunnel, n, &tcm->tunnel_list, list) {
                /*
                 * DMA tunnels require the driver to be functional so we
                 * tear them down. Other protocol tunnels can be left
                 * intact.
                 */
                if (tb_tunnel_is_dma(tunnel))
                        tb_tunnel_deactivate(tunnel);
                tb_tunnel_put(tunnel);
        }
        tb_switch_remove(tb->root_switch);
        tcm->hotplug_active = false; /* signal tb_handle_hotplug to quit */
}

static void tb_deinit(struct tb *tb)
{
        struct tb_cm *tcm = tb_priv(tb);
        int i;

        /* Cancel all the release bandwidth workers */
        for (i = 0; i < ARRAY_SIZE(tcm->groups); i++)
                cancel_delayed_work_sync(&tcm->groups[i].release_work);
}

static int tb_scan_finalize_switch(struct device *dev, void *data)
{
        if (tb_is_switch(dev)) {
                struct tb_switch *sw = tb_to_switch(dev);

                /*
                 * If we found that the switch was already setup by the
                 * boot firmware, mark it as authorized now before we
                 * send uevent to userspace.
                 */
                if (sw->boot)
                        sw->authorized = 1;

                dev_set_uevent_suppress(dev, false);
                kobject_uevent(&dev->kobj, KOBJ_ADD);
                device_for_each_child(dev, NULL, tb_scan_finalize_switch);
        }

        return 0;
}

static int tb_start(struct tb *tb, bool reset)
{
        struct tb_cm *tcm = tb_priv(tb);
        bool discover = true;
        int ret;

        tb->root_switch = tb_switch_alloc(tb, &tb->dev, 0);
        if (IS_ERR(tb->root_switch))
                return PTR_ERR(tb->root_switch);

        /*
         * ICM firmware upgrade needs running firmware and in native
         * mode that is not available so disable firmware upgrade of the
         * root switch.
         *
         * However, USB4 routers support NVM firmware upgrade if they
         * implement the necessary router operations.
         */
        tb->root_switch->no_nvm_upgrade = !tb_switch_is_usb4(tb->root_switch);
        /* All USB4 routers support runtime PM */
        tb->root_switch->rpm = tb_switch_is_usb4(tb->root_switch);

        ret = tb_switch_configure(tb->root_switch);
        if (ret) {
                tb_switch_put(tb->root_switch);
                return ret;
        }

        /* Announce the switch to the world */
        ret = tb_switch_add(tb->root_switch);
        if (ret) {
                tb_switch_put(tb->root_switch);
                return ret;
        }

        /*
         * To support highest CLx state, we set host router's TMU to
         * Normal mode.
         */
        tb_switch_tmu_configure(tb->root_switch, TB_SWITCH_TMU_MODE_LOWRES);
        /* Enable TMU if it is off */
        tb_switch_tmu_enable(tb->root_switch);

        /*
         * Boot firmware might have created tunnels of its own. Since we
         * cannot be sure they are usable for us, tear them down and
         * reset the ports to handle it as new hotplug for USB4 v1
         * routers (for USB4 v2 and beyond we already do host reset).
         */
        if (reset && tb_switch_is_usb4(tb->root_switch)) {
                discover = false;
                if (usb4_switch_version(tb->root_switch) == 1)
                        tb_switch_reset(tb->root_switch);
        }

        if (discover) {
                /* 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);
                /* Add DP resources from the DP tunnels created by the boot firmware */
                tb_discover_dp_resources(tb);
        }

        /*
         * If the boot firmware did not create USB 3.x tunnels create them
         * now for the whole topology.
         */
        tb_create_usb3_tunnels(tb->root_switch);
        /* Add DP IN resources for the root switch */
        tb_add_dp_resources(tb->root_switch);
        tb_switch_enter_redrive(tb->root_switch);
        /* Make the discovered switches available to the userspace */
        device_for_each_child(&tb->root_switch->dev, NULL,
                              tb_scan_finalize_switch);

        /* Allow tb_handle_hotplug to progress events */
        tcm->hotplug_active = true;
        return 0;
}

static int tb_suspend_noirq(struct tb *tb)
{
        struct tb_cm *tcm = tb_priv(tb);

        tb_dbg(tb, "suspending...\n");
        tb_disconnect_and_release_dp(tb);
        tb_switch_exit_redrive(tb->root_switch);
        tb_switch_suspend(tb->root_switch, false);
        tcm->hotplug_active = false; /* signal tb_handle_hotplug to quit */
        tb_dbg(tb, "suspend finished\n");

        return 0;
}

static void tb_restore_children(struct tb_switch *sw)
{
        struct tb_port *port;

        /* No need to restore if the router is already unplugged */
        if (sw->is_unplugged)
                return;

        if (tb_enable_clx(sw))
                tb_sw_warn(sw, "failed to re-enable CL states\n");

        if (tb_enable_tmu(sw))
                tb_sw_warn(sw, "failed to restore TMU configuration\n");

        tb_switch_configuration_valid(sw);

        tb_switch_for_each_port(sw, port) {
                if (!tb_port_has_remote(port) && !port->xdomain)
                        continue;

                if (port->remote) {
                        tb_switch_set_link_width(port->remote->sw,
                                                 port->remote->sw->link_width);
                        tb_switch_configure_link(port->remote->sw);

                        tb_restore_children(port->remote->sw);
                } else if (port->xdomain) {
                        tb_port_configure_xdomain(port, port->xdomain);
                }
        }
}

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");

        /*
         * For non-USB4 hosts (Apple systems) remove any PCIe devices
         * the firmware might have setup.
         */
        if (!tb_switch_is_usb4(tb->root_switch))
                tb_switch_reset(tb->root_switch);

        tb_switch_resume(tb->root_switch, false);
        tb_free_invalid_tunnels(tb);
        tb_free_unplugged_children(tb->root_switch);
        tb_restore_children(tb->root_switch);

        /*
         * 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_put(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_activate(tunnel);
        }
        if (!list_empty(&tcm->tunnel_list)) {
                /*
                 * the pcie links need some time to get going.
                 * 100ms works for me...
                 */
                tb_dbg(tb, "tunnels restarted, sleeping for 100ms\n");
                msleep(100);
        }
        tb_switch_enter_redrive(tb->root_switch);
         /* Allow tb_handle_hotplug to progress events */
        tcm->hotplug_active = true;
        tb_dbg(tb, "resume finished\n");

        return 0;
}

static int tb_free_unplugged_xdomains(struct tb_switch *sw)
{
        struct tb_port *port;
        int ret = 0;

        tb_switch_for_each_port(sw, port) {
                if (tb_is_upstream_port(port))
                        continue;
                if (port->xdomain && port->xdomain->is_unplugged) {
                        tb_retimer_remove_all(port);
                        tb_xdomain_remove(port->xdomain);
                        tb_port_unconfigure_xdomain(port);
                        port->xdomain = NULL;
                        ret++;
                } else if (port->remote) {
                        ret += tb_free_unplugged_xdomains(port->remote->sw);
                }
        }

        return ret;
}

static int tb_freeze_noirq(struct tb *tb)
{
        struct tb_cm *tcm = tb_priv(tb);

        tcm->hotplug_active = false;
        return 0;
}

static int tb_thaw_noirq(struct tb *tb)
{
        struct tb_cm *tcm = tb_priv(tb);

        tcm->hotplug_active = true;
        return 0;
}

static void tb_complete(struct tb *tb)
{
        /*
         * Release any unplugged XDomains and if there is a case where
         * another domain is swapped in place of unplugged XDomain we
         * need to run another rescan.
         */
        mutex_lock(&tb->lock);
        if (tb_free_unplugged_xdomains(tb->root_switch))
                tb_scan_switch(tb->root_switch);
        mutex_unlock(&tb->lock);
}

static int tb_runtime_suspend(struct tb *tb)
{
        struct tb_cm *tcm = tb_priv(tb);

        mutex_lock(&tb->lock);
        /*
         * The below call only releases DP resources to allow exiting and
         * re-entering redrive mode.
         */
        tb_disconnect_and_release_dp(tb);
        tb_switch_exit_redrive(tb->root_switch);
        tb_switch_suspend(tb->root_switch, true);
        tcm->hotplug_active = false;
        mutex_unlock(&tb->lock);

        return 0;
}

static void tb_remove_work(struct work_struct *work)
{
        struct tb_cm *tcm = container_of(work, struct tb_cm, remove_work.work);
        struct tb *tb = tcm_to_tb(tcm);

        mutex_lock(&tb->lock);
        if (tb->root_switch) {
                tb_free_unplugged_children(tb->root_switch);
                tb_free_unplugged_xdomains(tb->root_switch);
        }
        mutex_unlock(&tb->lock);
}

static int tb_runtime_resume(struct tb *tb)
{
        struct tb_cm *tcm = tb_priv(tb);
        struct tb_tunnel *tunnel, *n;

        mutex_lock(&tb->lock);
        tb_switch_resume(tb->root_switch, true);
        tb_free_invalid_tunnels(tb);
        tb_restore_children(tb->root_switch);
        list_for_each_entry_safe(tunnel, n, &tcm->tunnel_list, list)
                tb_tunnel_activate(tunnel);
        tb_switch_enter_redrive(tb->root_switch);
        tcm->hotplug_active = true;
        mutex_unlock(&tb->lock);

        /*
         * Schedule cleanup of any unplugged devices. Run this in a
         * separate thread to avoid possible deadlock if the device
         * removal runtime resumes the unplugged device.
         */
        queue_delayed_work(tb->wq, &tcm->remove_work, msecs_to_jiffies(50));
        return 0;
}

static const struct tb_cm_ops tb_cm_ops = {
        .start = tb_start,
        .stop = tb_stop,
        .deinit = tb_deinit,
        .suspend_noirq = tb_suspend_noirq,
        .resume_noirq = tb_resume_noirq,
        .freeze_noirq = tb_freeze_noirq,
        .thaw_noirq = tb_thaw_noirq,
        .complete = tb_complete,
        .runtime_suspend = tb_runtime_suspend,
        .runtime_resume = tb_runtime_resume,
        .handle_event = tb_handle_event,
        .disapprove_switch = tb_disconnect_pci,
        .approve_switch = tb_tunnel_pci,
        .approve_xdomain_paths = tb_approve_xdomain_paths,
        .disconnect_xdomain_paths = tb_disconnect_xdomain_paths,
};

/*
 * During suspend the Thunderbolt controller is reset and all PCIe
 * tunnels are lost. The NHI driver will try to reestablish all tunnels
 * during resume. This adds device links between the tunneled PCIe
 * downstream ports and the NHI so that the device core will make sure
 * NHI is resumed first before the rest.
 */
static bool tb_apple_add_links(struct tb_nhi *nhi)
{
        struct pci_dev *upstream, *pdev;
        bool ret;

        if (!x86_apple_machine)
                return false;

        switch (nhi->pdev->device) {
        case PCI_DEVICE_ID_INTEL_LIGHT_RIDGE:
        case PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C:
        case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_NHI:
        case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI:
                break;
        default:
                return false;
        }

        upstream = pci_upstream_bridge(nhi->pdev);
        while (upstream) {
                if (!pci_is_pcie(upstream))
                        return false;
                if (pci_pcie_type(upstream) == PCI_EXP_TYPE_UPSTREAM)
                        break;
                upstream = pci_upstream_bridge(upstream);
        }

        if (!upstream)
                return false;

        /*
         * For each hotplug downstream port, create add device link
         * back to NHI so that PCIe tunnels can be re-established after
         * sleep.
         */
        ret = false;
        for_each_pci_bridge(pdev, upstream->subordinate) {
                const struct device_link *link;

                if (!pci_is_pcie(pdev))
                        continue;
                if (pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM ||
                    !pdev->is_pciehp)
                        continue;

                link = device_link_add(&pdev->dev, &nhi->pdev->dev,
                                       DL_FLAG_AUTOREMOVE_SUPPLIER |
                                       DL_FLAG_PM_RUNTIME);
                if (link) {
                        dev_dbg(&nhi->pdev->dev, "created link from %s\n",
                                dev_name(&pdev->dev));
                        ret = true;
                } else {
                        dev_warn(&nhi->pdev->dev, "device link creation from %s failed\n",
                                 dev_name(&pdev->dev));
                }
        }

        return ret;
}

struct tb *tb_probe(struct tb_nhi *nhi)
{
        struct tb_cm *tcm;
        struct tb *tb;

        tb = tb_domain_alloc(nhi, TB_TIMEOUT, sizeof(*tcm));
        if (!tb)
                return NULL;

        if (tb_acpi_may_tunnel_pcie())
                tb->security_level = TB_SECURITY_USER;
        else
                tb->security_level = TB_SECURITY_NOPCIE;

        tb->cm_ops = &tb_cm_ops;

        tcm = tb_priv(tb);
        INIT_LIST_HEAD(&tcm->tunnel_list);
        INIT_LIST_HEAD(&tcm->dp_resources);
        INIT_DELAYED_WORK(&tcm->remove_work, tb_remove_work);
        tb_init_bandwidth_groups(tcm);

        tb_dbg(tb, "using software connection manager\n");

        /*
         * Device links are needed to make sure we establish tunnels
         * before the PCIe/USB stack is resumed so complain here if we
         * found them missing.
         */
        if (!tb_apple_add_links(nhi) && !tb_acpi_add_links(nhi))
                tb_warn(tb, "device links to tunneled native ports are missing!\n");

        return tb;
}