thunderbolt: Add helper function to iterate from one port to another
authorMika Westerberg <mika.westerberg@linux.intel.com>
Sun, 19 Feb 2017 19:51:30 +0000 (21:51 +0200)
committerMika Westerberg <mika.westerberg@linux.intel.com>
Thu, 18 Apr 2019 08:18:52 +0000 (11:18 +0300)
We need to be able to walk from one port to another when we are creating
paths where there are multiple switches between two ports. For this
reason introduce a new function tb_next_port_on_path().

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Reviewed-by: Lukas Wunner <lukas@wunner.de>
drivers/thunderbolt/switch.c
drivers/thunderbolt/tb.h

index 6f98b3d6eb2a05713ec648ab19cb106966a78456..ecd41f7b764964abb1455b60d13d04b14682174e 100644 (file)
@@ -676,6 +676,60 @@ void tb_port_release_out_hopid(struct tb_port *port, int hopid)
        ida_simple_remove(&port->out_hopids, hopid);
 }
 
+/**
+ * tb_next_port_on_path() - Return next port for given port on a path
+ * @start: Start port of the walk
+ * @end: End port of the walk
+ * @prev: Previous port (%NULL if this is the first)
+ *
+ * This function can be used to walk from one port to another if they
+ * are connected through zero or more switches. If the @prev is dual
+ * link port, the function follows that link and returns another end on
+ * that same link.
+ *
+ * If the @end port has been reached, return %NULL.
+ *
+ * Domain tb->lock must be held when this function is called.
+ */
+struct tb_port *tb_next_port_on_path(struct tb_port *start, struct tb_port *end,
+                                    struct tb_port *prev)
+{
+       struct tb_port *next;
+
+       if (!prev)
+               return start;
+
+       if (prev->sw == end->sw) {
+               if (prev == end)
+                       return NULL;
+               return end;
+       }
+
+       if (start->sw->config.depth < end->sw->config.depth) {
+               if (prev->remote &&
+                   prev->remote->sw->config.depth > prev->sw->config.depth)
+                       next = prev->remote;
+               else
+                       next = tb_port_at(tb_route(end->sw), prev->sw);
+       } else {
+               if (tb_is_upstream_port(prev)) {
+                       next = prev->remote;
+               } else {
+                       next = tb_upstream_port(prev->sw);
+                       /*
+                        * Keep the same link if prev and next are both
+                        * dual link ports.
+                        */
+                       if (next->dual_link_port &&
+                           next->link_nr != prev->link_nr) {
+                               next = next->dual_link_port;
+                       }
+               }
+       }
+
+       return next;
+}
+
 /**
  * tb_pci_port_enable() - Enable PCIe adapter port
  * @port: PCIe port to enable
index 43bc4f4900213d25d56c49dd5e1d2845ebb1458e..11d5ab53ad36b39d29c4fae0b401aa687f07a021 100644 (file)
@@ -495,6 +495,8 @@ int tb_port_alloc_in_hopid(struct tb_port *port, int hopid, int max_hopid);
 void tb_port_release_in_hopid(struct tb_port *port, int hopid);
 int tb_port_alloc_out_hopid(struct tb_port *port, int hopid, int max_hopid);
 void tb_port_release_out_hopid(struct tb_port *port, int hopid);
+struct tb_port *tb_next_port_on_path(struct tb_port *start, struct tb_port *end,
+                                    struct tb_port *prev);
 
 int tb_switch_find_vse_cap(struct tb_switch *sw, enum tb_switch_vse_cap vsec);
 int tb_port_find_cap(struct tb_port *port, enum tb_port_cap cap);