Description:   This attribute contains name of this device extracted from
                the device DROM.
 
+What:          /sys/bus/thunderbolt/devices/.../rx_speed
+Date:          Jan 2020
+KernelVersion: 5.5
+Contact:       Mika Westerberg <mika.westerberg@linux.intel.com>
+Description:   This attribute reports the device RX speed per lane.
+               All RX lanes run at the same speed.
+
+What:          /sys/bus/thunderbolt/devices/.../rx_lanes
+Date:          Jan 2020
+KernelVersion: 5.5
+Contact:       Mika Westerberg <mika.westerberg@linux.intel.com>
+Description:   This attribute reports number of RX lanes the device is
+               using simultaneusly through its upstream port.
+
+What:          /sys/bus/thunderbolt/devices/.../tx_speed
+Date:          Jan 2020
+KernelVersion: 5.5
+Contact:       Mika Westerberg <mika.westerberg@linux.intel.com>
+Description:   This attribute reports the TX speed per lane.
+               All TX lanes run at the same speed.
+
+What:          /sys/bus/thunderbolt/devices/.../tx_lanes
+Date:          Jan 2020
+KernelVersion: 5.5
+Contact:       Mika Westerberg <mika.westerberg@linux.intel.com>
+Description:   This attribute reports number of TX lanes the device is
+               using simultaneusly through its upstream port.
+
 What:          /sys/bus/thunderbolt/devices/.../vendor
 Date:          Sep 2017
 KernelVersion: 4.13
 
                (const struct icm_fr_event_device_connected *)hdr;
        enum tb_security_level security_level;
        struct tb_switch *sw, *parent_sw;
+       bool boot, dual_lane, speed_gen3;
        struct icm *icm = tb_priv(tb);
        bool authorized = false;
        struct tb_xdomain *xd;
        u8 link, depth;
-       bool boot;
        u64 route;
        int ret;
 
        security_level = (pkg->hdr.flags & ICM_FLAGS_SLEVEL_MASK) >>
                         ICM_FLAGS_SLEVEL_SHIFT;
        boot = pkg->link_info & ICM_LINK_INFO_BOOT;
+       dual_lane = pkg->hdr.flags & ICM_FLAGS_DUAL_LANE;
+       speed_gen3 = pkg->hdr.flags & ICM_FLAGS_SPEED_GEN3;
 
        if (pkg->link_info & ICM_LINK_INFO_REJECTED) {
                tb_info(tb, "switch at %u.%u was rejected by ICM firmware because topology limit exceeded\n",
                sw->authorized = authorized;
                sw->security_level = security_level;
                sw->boot = boot;
+               sw->link_speed = speed_gen3 ? 20 : 10;
+               sw->link_width = dual_lane ? 2 : 1;
                sw->rpm = intel_vss_is_rtd3(pkg->ep_name, sizeof(pkg->ep_name));
 
                if (add_switch(parent_sw, sw))
 {
        const struct icm_tr_event_device_connected *pkg =
                (const struct icm_tr_event_device_connected *)hdr;
+       bool authorized, boot, dual_lane, speed_gen3;
        enum tb_security_level security_level;
        struct tb_switch *sw, *parent_sw;
        struct tb_xdomain *xd;
-       bool authorized, boot;
        u64 route;
 
        icm_postpone_rescan(tb);
        security_level = (pkg->hdr.flags & ICM_FLAGS_SLEVEL_MASK) >>
                         ICM_FLAGS_SLEVEL_SHIFT;
        boot = pkg->link_info & ICM_LINK_INFO_BOOT;
+       dual_lane = pkg->hdr.flags & ICM_FLAGS_DUAL_LANE;
+       speed_gen3 = pkg->hdr.flags & ICM_FLAGS_SPEED_GEN3;
 
        if (pkg->link_info & ICM_LINK_INFO_REJECTED) {
                tb_info(tb, "switch at %llx was rejected by ICM firmware because topology limit exceeded\n",
                sw->authorized = authorized;
                sw->security_level = security_level;
                sw->boot = boot;
+               sw->link_speed = speed_gen3 ? 20 : 10;
+               sw->link_width = dual_lane ? 2 : 1;
                sw->rpm = force_rtd3;
                if (!sw->rpm)
                        sw->rpm = intel_vss_is_rtd3(pkg->ep_name,
 
 
        return 0;
 }
+
+/**
+ * tb_lc_lane_bonding_possible() - Is lane bonding possible towards switch
+ * @sw: Switch to check
+ *
+ * Checks whether conditions for lane bonding from parent to @sw are
+ * possible.
+ */
+bool tb_lc_lane_bonding_possible(struct tb_switch *sw)
+{
+       struct tb_port *up;
+       int cap, ret;
+       u32 val;
+
+       if (sw->generation < 2)
+               return false;
+
+       up = tb_upstream_port(sw);
+       cap = find_port_lc_cap(up);
+       if (cap < 0)
+               return false;
+
+       ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, cap + TB_LC_PORT_ATTR, 1);
+       if (ret)
+               return false;
+
+       return !!(val & TB_LC_PORT_ATTR_BE);
+}
 
  * Creates path between two ports starting with given @src_hopid. Reserves
  * HopIDs for each port (they can be different from @src_hopid depending on
  * how many HopIDs each port already have reserved). If there are dual
- * links on the path, prioritizes using @link_nr.
+ * links on the path, prioritizes using @link_nr but takes into account
+ * that the lanes may be bonded.
  *
  * Return: Returns a tb_path on success or NULL on failure.
  */
                if (!in_port)
                        goto err;
 
-               if (in_port->dual_link_port && in_port->link_nr != link_nr)
+               /* When lanes are bonded primary link must be used */
+               if (!in_port->bonded && in_port->dual_link_port &&
+                   in_port->link_nr != link_nr)
                        in_port = in_port->dual_link_port;
 
                ret = tb_port_alloc_in_hopid(in_port, in_hopid, in_hopid);
                if (!out_port)
                        goto err;
 
-               if (out_port->dual_link_port && out_port->link_nr != link_nr)
-                       out_port = out_port->dual_link_port;
+               /*
+                * Pick up right port when going from non-bonded to
+                * bonded or from bonded to non-bonded.
+                */
+               if (out_port->dual_link_port) {
+                       if (!in_port->bonded && out_port->bonded &&
+                           out_port->link_nr) {
+                               /*
+                                * Use primary link when going from
+                                * non-bonded to bonded.
+                                */
+                               out_port = out_port->dual_link_port;
+                       } else if (!out_port->bonded &&
+                                  out_port->link_nr != link_nr) {
+                               /*
+                                * If out port is not bonded follow
+                                * link_nr.
+                                */
+                               out_port = out_port->dual_link_port;
+                       }
+               }
 
                if (i == num_hops - 1)
                        ret = tb_port_alloc_out_hopid(out_port, dst_hopid,
 
        return next;
 }
 
+static int tb_port_get_link_speed(struct tb_port *port)
+{
+       u32 val, speed;
+       int ret;
+
+       if (!port->cap_phy)
+               return -EINVAL;
+
+       ret = tb_port_read(port, &val, TB_CFG_PORT,
+                          port->cap_phy + LANE_ADP_CS_1, 1);
+       if (ret)
+               return ret;
+
+       speed = (val & LANE_ADP_CS_1_CURRENT_SPEED_MASK) >>
+               LANE_ADP_CS_1_CURRENT_SPEED_SHIFT;
+       return speed == LANE_ADP_CS_1_CURRENT_SPEED_GEN3 ? 20 : 10;
+}
+
+static int tb_port_get_link_width(struct tb_port *port)
+{
+       u32 val;
+       int ret;
+
+       if (!port->cap_phy)
+               return -EINVAL;
+
+       ret = tb_port_read(port, &val, TB_CFG_PORT,
+                          port->cap_phy + LANE_ADP_CS_1, 1);
+       if (ret)
+               return ret;
+
+       return (val & LANE_ADP_CS_1_CURRENT_WIDTH_MASK) >>
+               LANE_ADP_CS_1_CURRENT_WIDTH_SHIFT;
+}
+
+static bool tb_port_is_width_supported(struct tb_port *port, int width)
+{
+       u32 phy, widths;
+       int ret;
+
+       if (!port->cap_phy)
+               return false;
+
+       ret = tb_port_read(port, &phy, TB_CFG_PORT,
+                          port->cap_phy + LANE_ADP_CS_0, 1);
+       if (ret)
+               return ret;
+
+       widths = (phy & LANE_ADP_CS_0_SUPPORTED_WIDTH_MASK) >>
+               LANE_ADP_CS_0_SUPPORTED_WIDTH_SHIFT;
+
+       return !!(widths & width);
+}
+
+static int tb_port_set_link_width(struct tb_port *port, unsigned int width)
+{
+       u32 val;
+       int ret;
+
+       if (!port->cap_phy)
+               return -EINVAL;
+
+       ret = tb_port_read(port, &val, TB_CFG_PORT,
+                          port->cap_phy + LANE_ADP_CS_1, 1);
+       if (ret)
+               return ret;
+
+       val &= ~LANE_ADP_CS_1_TARGET_WIDTH_MASK;
+       switch (width) {
+       case 1:
+               val |= LANE_ADP_CS_1_TARGET_WIDTH_SINGLE <<
+                       LANE_ADP_CS_1_TARGET_WIDTH_SHIFT;
+               break;
+       case 2:
+               val |= LANE_ADP_CS_1_TARGET_WIDTH_DUAL <<
+                       LANE_ADP_CS_1_TARGET_WIDTH_SHIFT;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       val |= LANE_ADP_CS_1_LB;
+
+       return tb_port_write(port, &val, TB_CFG_PORT,
+                            port->cap_phy + LANE_ADP_CS_1, 1);
+}
+
+static int tb_port_lane_bonding_enable(struct tb_port *port)
+{
+       int ret;
+
+       /*
+        * Enable lane bonding for both links if not already enabled by
+        * for example the boot firmware.
+        */
+       ret = tb_port_get_link_width(port);
+       if (ret == 1) {
+               ret = tb_port_set_link_width(port, 2);
+               if (ret)
+                       return ret;
+       }
+
+       ret = tb_port_get_link_width(port->dual_link_port);
+       if (ret == 1) {
+               ret = tb_port_set_link_width(port->dual_link_port, 2);
+               if (ret) {
+                       tb_port_set_link_width(port, 1);
+                       return ret;
+               }
+       }
+
+       port->bonded = true;
+       port->dual_link_port->bonded = true;
+
+       return 0;
+}
+
+static void tb_port_lane_bonding_disable(struct tb_port *port)
+{
+       port->dual_link_port->bonded = false;
+       port->bonded = false;
+
+       tb_port_set_link_width(port->dual_link_port, 1);
+       tb_port_set_link_width(port, 1);
+}
+
 /**
  * tb_port_is_enabled() - Is the adapter port enabled
  * @port: Port to check
 }
 static DEVICE_ATTR(key, 0600, key_show, key_store);
 
+static ssize_t speed_show(struct device *dev, struct device_attribute *attr,
+                         char *buf)
+{
+       struct tb_switch *sw = tb_to_switch(dev);
+
+       return sprintf(buf, "%u.0 Gb/s\n", sw->link_speed);
+}
+
+/*
+ * Currently all lanes must run at the same speed but we expose here
+ * both directions to allow possible asymmetric links in the future.
+ */
+static DEVICE_ATTR(rx_speed, 0444, speed_show, NULL);
+static DEVICE_ATTR(tx_speed, 0444, speed_show, NULL);
+
+static ssize_t lanes_show(struct device *dev, struct device_attribute *attr,
+                         char *buf)
+{
+       struct tb_switch *sw = tb_to_switch(dev);
+
+       return sprintf(buf, "%u\n", sw->link_width);
+}
+
+/*
+ * Currently link has same amount of lanes both directions (1 or 2) but
+ * expose them separately to allow possible asymmetric links in the future.
+ */
+static DEVICE_ATTR(rx_lanes, 0444, lanes_show, NULL);
+static DEVICE_ATTR(tx_lanes, 0444, lanes_show, NULL);
+
 static void nvm_authenticate_start(struct tb_switch *sw)
 {
        struct pci_dev *root_port;
        &dev_attr_key.attr,
        &dev_attr_nvm_authenticate.attr,
        &dev_attr_nvm_version.attr,
+       &dev_attr_rx_speed.attr,
+       &dev_attr_rx_lanes.attr,
+       &dev_attr_tx_speed.attr,
+       &dev_attr_tx_lanes.attr,
        &dev_attr_vendor.attr,
        &dev_attr_vendor_name.attr,
        &dev_attr_unique_id.attr,
                    sw->security_level == TB_SECURITY_SECURE)
                        return attr->mode;
                return 0;
+       } else if (attr == &dev_attr_rx_speed.attr ||
+                  attr == &dev_attr_rx_lanes.attr ||
+                  attr == &dev_attr_tx_speed.attr ||
+                  attr == &dev_attr_tx_lanes.attr) {
+               if (tb_route(sw))
+                       return attr->mode;
+               return 0;
        } else if (attr == &dev_attr_nvm_authenticate.attr) {
                if (sw->dma_port && !sw->no_nvm_upgrade)
                        return attr->mode;
        return -ESHUTDOWN;
 }
 
+static bool tb_switch_lane_bonding_possible(struct tb_switch *sw)
+{
+       const struct tb_port *up = tb_upstream_port(sw);
+
+       if (!up->dual_link_port || !up->dual_link_port->remote)
+               return false;
+
+       return tb_lc_lane_bonding_possible(sw);
+}
+
+static int tb_switch_update_link_attributes(struct tb_switch *sw)
+{
+       struct tb_port *up;
+       bool change = false;
+       int ret;
+
+       if (!tb_route(sw) || tb_switch_is_icm(sw))
+               return 0;
+
+       up = tb_upstream_port(sw);
+
+       ret = tb_port_get_link_speed(up);
+       if (ret < 0)
+               return ret;
+       if (sw->link_speed != ret)
+               change = true;
+       sw->link_speed = ret;
+
+       ret = tb_port_get_link_width(up);
+       if (ret < 0)
+               return ret;
+       if (sw->link_width != ret)
+               change = true;
+       sw->link_width = ret;
+
+       /* Notify userspace that there is possible link attribute change */
+       if (device_is_registered(&sw->dev) && change)
+               kobject_uevent(&sw->dev.kobj, KOBJ_CHANGE);
+
+       return 0;
+}
+
+/**
+ * tb_switch_lane_bonding_enable() - Enable lane bonding
+ * @sw: Switch to enable lane bonding
+ *
+ * Connection manager can call this function to enable lane bonding of a
+ * switch. If conditions are correct and both switches support the feature,
+ * lanes are bonded. It is safe to call this to any switch.
+ */
+int tb_switch_lane_bonding_enable(struct tb_switch *sw)
+{
+       struct tb_switch *parent = tb_to_switch(sw->dev.parent);
+       struct tb_port *up, *down;
+       u64 route = tb_route(sw);
+       int ret;
+
+       if (!route)
+               return 0;
+
+       if (!tb_switch_lane_bonding_possible(sw))
+               return 0;
+
+       up = tb_upstream_port(sw);
+       down = tb_port_at(route, parent);
+
+       if (!tb_port_is_width_supported(up, 2) ||
+           !tb_port_is_width_supported(down, 2))
+               return 0;
+
+       ret = tb_port_lane_bonding_enable(up);
+       if (ret) {
+               tb_port_warn(up, "failed to enable lane bonding\n");
+               return ret;
+       }
+
+       ret = tb_port_lane_bonding_enable(down);
+       if (ret) {
+               tb_port_warn(down, "failed to enable lane bonding\n");
+               tb_port_lane_bonding_disable(up);
+               return ret;
+       }
+
+       tb_switch_update_link_attributes(sw);
+
+       tb_sw_dbg(sw, "lane bonding enabled\n");
+       return ret;
+}
+
+/**
+ * tb_switch_lane_bonding_disable() - Disable lane bonding
+ * @sw: Switch whose lane bonding to disable
+ *
+ * Disables lane bonding between @sw and parent. This can be called even
+ * if lanes were not bonded originally.
+ */
+void tb_switch_lane_bonding_disable(struct tb_switch *sw)
+{
+       struct tb_switch *parent = tb_to_switch(sw->dev.parent);
+       struct tb_port *up, *down;
+
+       if (!tb_route(sw))
+               return;
+
+       up = tb_upstream_port(sw);
+       if (!up->bonded)
+               return;
+
+       down = tb_port_at(tb_route(sw), parent);
+
+       tb_port_lane_bonding_disable(up);
+       tb_port_lane_bonding_disable(down);
+
+       tb_switch_update_link_attributes(sw);
+       tb_sw_dbg(sw, "lane bonding disabled\n");
+}
+
 /**
  * tb_switch_add() - Add a switch to the domain
  * @sw: Switch to add
                                return ret;
                        }
                }
+
+               ret = tb_switch_update_link_attributes(sw);
+               if (ret)
+                       return ret;
        }
 
        ret = device_add(&sw->dev);
 
                upstream_port->dual_link_port->remote = port->dual_link_port;
        }
 
+       /* Enable lane bonding if supported */
+       if (tb_switch_lane_bonding_enable(sw))
+               tb_sw_warn(sw, "failed to enable lane bonding\n");
+
        tb_scan_switch(sw);
 }
 
                        continue;
 
                if (port->remote->sw->is_unplugged) {
+                       tb_switch_lane_bonding_disable(port->remote->sw);
                        tb_switch_remove(port->remote->sw);
                        port->remote = NULL;
                        if (port->dual_link_port)
                        tb_port_dbg(port, "switch unplugged\n");
                        tb_sw_set_unplugged(port->remote->sw);
                        tb_free_invalid_tunnels(tb);
+                       tb_switch_lane_bonding_disable(port->remote->sw);
                        tb_switch_remove(port->remote->sw);
                        port->remote = NULL;
                        if (port->dual_link_port)
        return 0;
 }
 
+static void tb_restore_children(struct tb_switch *sw)
+{
+       struct tb_port *port;
+
+       tb_switch_for_each_port(sw, port) {
+               if (!tb_port_has_remote(port))
+                       continue;
+
+               if (tb_switch_lane_bonding_enable(port->remote->sw))
+                       dev_warn(&sw->dev, "failed to restore lane bonding\n");
+
+               tb_restore_children(port->remote->sw);
+       }
+}
+
 static int tb_resume_noirq(struct tb *tb)
 {
        struct tb_cm *tcm = tb_priv(tb);
        tb_switch_resume(tb->root_switch);
        tb_free_invalid_tunnels(tb);
        tb_free_unplugged_children(tb->root_switch);
+       tb_restore_children(tb->root_switch);
        list_for_each_entry_safe(tunnel, n, &tcm->tunnel_list, list)
                tb_tunnel_restart(tunnel);
        if (!list_empty(&tcm->tunnel_list)) {
 
  * @device: Device ID of the switch
  * @vendor_name: Name of the vendor (or %NULL if not known)
  * @device_name: Name of the device (or %NULL if not known)
+ * @link_speed: Speed of the link in Gb/s
+ * @link_width: Width of the link (1 or 2)
  * @generation: Switch Thunderbolt generation
  * @cap_plug_events: Offset to the plug events capability (%0 if not found)
  * @cap_lc: Offset to the link controller capability (%0 if not found)
        u16 device;
        const char *vendor_name;
        const char *device_name;
+       unsigned int link_speed;
+       unsigned int link_width;
        unsigned int generation;
        int cap_plug_events;
        int cap_lc;
  * @cap_adap: Offset of the adapter specific capability (%0 if not present)
  * @port: Port number on switch
  * @disabled: Disabled by eeprom
+ * @bonded: true if the port is bonded (two lanes combined as one)
  * @dual_link_port: If the switch is connected using two ports, points
  *                 to the other port.
  * @link_nr: Is this primary or secondary port on the dual_link.
        int cap_adap;
        u8 port;
        bool disabled;
+       bool bonded;
        struct tb_port *dual_link_port;
        u8 link_nr:1;
        struct ida in_hopids;
        return !sw->config.enabled;
 }
 
+int tb_switch_lane_bonding_enable(struct tb_switch *sw);
+void tb_switch_lane_bonding_disable(struct tb_switch *sw);
+
 int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged);
 int tb_port_add_nfc_credits(struct tb_port *port, int credits);
 int tb_port_set_initial_credits(struct tb_port *port, u32 credits);
 int tb_lc_configure_link(struct tb_switch *sw);
 void tb_lc_unconfigure_link(struct tb_switch *sw);
 int tb_lc_set_sleep(struct tb_switch *sw);
+bool tb_lc_lane_bonding_possible(struct tb_switch *sw);
 
 static inline int tb_route_length(u64 route)
 {
 
 #define ICM_FLAGS_NO_KEY               BIT(1)
 #define ICM_FLAGS_SLEVEL_SHIFT         3
 #define ICM_FLAGS_SLEVEL_MASK          GENMASK(4, 3)
+#define ICM_FLAGS_DUAL_LANE            BIT(5)
+#define ICM_FLAGS_SPEED_GEN3           BIT(7)
 #define ICM_FLAGS_WRITE                        BIT(7)
 
 struct icm_pkg_driver_ready {
 
 #define ADP_CS_5_LCA_MASK                      GENMASK(28, 22)
 #define ADP_CS_5_LCA_SHIFT                     22
 
+/* Lane adapter registers */
+#define LANE_ADP_CS_0                          0x00
+#define LANE_ADP_CS_0_SUPPORTED_WIDTH_MASK     GENMASK(25, 20)
+#define LANE_ADP_CS_0_SUPPORTED_WIDTH_SHIFT    20
+#define LANE_ADP_CS_1                          0x01
+#define LANE_ADP_CS_1_TARGET_WIDTH_MASK                GENMASK(9, 4)
+#define LANE_ADP_CS_1_TARGET_WIDTH_SHIFT       4
+#define LANE_ADP_CS_1_TARGET_WIDTH_SINGLE      0x1
+#define LANE_ADP_CS_1_TARGET_WIDTH_DUAL                0x3
+#define LANE_ADP_CS_1_LB                       BIT(15)
+#define LANE_ADP_CS_1_CURRENT_SPEED_MASK       GENMASK(19, 16)
+#define LANE_ADP_CS_1_CURRENT_SPEED_SHIFT      16
+#define LANE_ADP_CS_1_CURRENT_SPEED_GEN2       0x8
+#define LANE_ADP_CS_1_CURRENT_SPEED_GEN3       0x4
+#define LANE_ADP_CS_1_CURRENT_WIDTH_MASK       GENMASK(25, 20)
+#define LANE_ADP_CS_1_CURRENT_WIDTH_SHIFT      20
+
 /* Display Port adapter registers */
 #define ADP_DP_CS_0                            0x00
 #define ADP_DP_CS_0_VIDEO_HOPID_MASK           GENMASK(26, 16)
 #define TB_LC_FUSE                     0x03
 
 /* Link controller registers */
+#define TB_LC_PORT_ATTR                        0x8d
+#define TB_LC_PORT_ATTR_BE             BIT(12)
+
 #define TB_LC_SX_CTRL                  0x96
 #define TB_LC_SX_CTRL_L1C              BIT(16)
 #define TB_LC_SX_CTRL_L2C              BIT(20)
 
        return 0;
 }
 
+static int tb_initial_credits(const struct tb_switch *sw)
+{
+       /* If the path is complete sw is not NULL */
+       if (sw) {
+               /* More credits for faster link */
+               switch (sw->link_speed * sw->link_width) {
+               case 40:
+                       return 32;
+               case 20:
+                       return 24;
+               }
+       }
+
+       return 16;
+}
+
 static void tb_pci_init_path(struct tb_path *path)
 {
        path->egress_fc_enable = TB_PATH_SOURCE | TB_PATH_INTERNAL;
        path->drop_packages = 0;
        path->nfc_credits = 0;
        path->hops[0].initial_credits = 7;
-       path->hops[1].initial_credits = 16;
+       path->hops[1].initial_credits =
+               tb_initial_credits(path->hops[1].in_port->sw);
 }
 
 /**