LoongArch: Parse MADT to get multi-processor information
[linux-2.6-microblaze.git] / drivers / thunderbolt / switch.c
index ac87e8b..561e1d7 100644 (file)
@@ -693,8 +693,14 @@ static int __tb_port_enable(struct tb_port *port, bool enable)
        else
                phy |= LANE_ADP_CS_1_LD;
 
-       return tb_port_write(port, &phy, TB_CFG_PORT,
-                            port->cap_phy + LANE_ADP_CS_1, 1);
+
+       ret = tb_port_write(port, &phy, TB_CFG_PORT,
+                           port->cap_phy + LANE_ADP_CS_1, 1);
+       if (ret)
+               return ret;
+
+       tb_port_dbg(port, "lane %sabled\n", enable ? "en" : "dis");
+       return 0;
 }
 
 /**
@@ -993,7 +999,17 @@ static bool tb_port_is_width_supported(struct tb_port *port, int width)
        return !!(widths & width);
 }
 
-static int tb_port_set_link_width(struct tb_port *port, unsigned int width)
+/**
+ * tb_port_set_link_width() - Set target link width of the lane adapter
+ * @port: Lane adapter
+ * @width: Target link width (%1 or %2)
+ *
+ * Sets the target link width of the lane adapter to @width. Does not
+ * enable/disable lane bonding. For that call tb_port_set_lane_bonding().
+ *
+ * Return: %0 in case of success and negative errno in case of error
+ */
+int tb_port_set_link_width(struct tb_port *port, unsigned int width)
 {
        u32 val;
        int ret;
@@ -1020,12 +1036,58 @@ static int tb_port_set_link_width(struct tb_port *port, unsigned int width)
                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);
 }
 
+/**
+ * tb_port_set_lane_bonding() - Enable/disable lane bonding
+ * @port: Lane adapter
+ * @bonding: enable/disable bonding
+ *
+ * Enables or disables lane bonding. This should be called after target
+ * link width has been set (tb_port_set_link_width()). Note in most
+ * cases one should use tb_port_lane_bonding_enable() instead to enable
+ * lane bonding.
+ *
+ * As a side effect sets @port->bonding accordingly (and does the same
+ * for lane 1 too).
+ *
+ * Return: %0 in case of success and negative errno in case of error
+ */
+int tb_port_set_lane_bonding(struct tb_port *port, bool bonding)
+{
+       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;
+
+       if (bonding)
+               val |= LANE_ADP_CS_1_LB;
+       else
+               val &= ~LANE_ADP_CS_1_LB;
+
+       ret = tb_port_write(port, &val, TB_CFG_PORT,
+                           port->cap_phy + LANE_ADP_CS_1, 1);
+       if (ret)
+               return ret;
+
+       /*
+        * When lane 0 bonding is set it will affect lane 1 too so
+        * update both.
+        */
+       port->bonded = bonding;
+       port->dual_link_port->bonded = bonding;
+
+       return 0;
+}
+
 /**
  * tb_port_lane_bonding_enable() - Enable bonding on port
  * @port: port to enable
@@ -1050,22 +1112,27 @@ int tb_port_lane_bonding_enable(struct tb_port *port)
        if (ret == 1) {
                ret = tb_port_set_link_width(port, 2);
                if (ret)
-                       return ret;
+                       goto err_lane0;
        }
 
        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;
-               }
+               if (ret)
+                       goto err_lane0;
        }
 
-       port->bonded = true;
-       port->dual_link_port->bonded = true;
+       ret = tb_port_set_lane_bonding(port, true);
+       if (ret)
+               goto err_lane1;
 
        return 0;
+
+err_lane1:
+       tb_port_set_link_width(port->dual_link_port, 1);
+err_lane0:
+       tb_port_set_link_width(port, 1);
+       return ret;
 }
 
 /**
@@ -1074,13 +1141,10 @@ int tb_port_lane_bonding_enable(struct tb_port *port)
  *
  * Disable bonding by setting the link width of the port and the
  * other port in case of dual link port.
- *
  */
 void tb_port_lane_bonding_disable(struct tb_port *port)
 {
-       port->dual_link_port->bonded = false;
-       port->bonded = false;
-
+       tb_port_set_lane_bonding(port, false);
        tb_port_set_link_width(port->dual_link_port, 1);
        tb_port_set_link_width(port, 1);
 }
@@ -1104,10 +1168,17 @@ int tb_port_wait_for_link_width(struct tb_port *port, int width,
 
        do {
                ret = tb_port_get_link_width(port);
-               if (ret < 0)
-                       return ret;
-               else if (ret == width)
+               if (ret < 0) {
+                       /*
+                        * Sometimes we get port locked error when
+                        * polling the lanes so we can ignore it and
+                        * retry.
+                        */
+                       if (ret != -EACCES)
+                               return ret;
+               } else if (ret == width) {
                        return 0;
+               }
 
                usleep_range(1000, 2000);
        } while (ktime_before(ktime_get(), timeout));