thunderbolt: Generalize tunnel creation functionality
authorMika Westerberg <mika.westerberg@linux.intel.com>
Sun, 19 Feb 2017 11:48:29 +0000 (13:48 +0200)
committerMika Westerberg <mika.westerberg@linux.intel.com>
Thu, 18 Apr 2019 08:18:52 +0000 (11:18 +0300)
To be able to tunnel non-PCIe traffic, separate tunnel functionality
into generic and PCIe specific parts. Rename struct tb_pci_tunnel to
tb_tunnel, and make it hold an array of paths instead of just two.
Update all the tunneling functions to take this structure as parameter.

We also move tb_pci_port_active() to switch.c (and rename it) where we
will be keeping all port and switch related functions.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
drivers/thunderbolt/switch.c
drivers/thunderbolt/tb.c
drivers/thunderbolt/tb.h
drivers/thunderbolt/tb_regs.h
drivers/thunderbolt/tunnel.c
drivers/thunderbolt/tunnel.h

index 9756e6279dc91f58efafc551214509e9f42a0ae5..b132439618da906df9306c82e29ce61df65c3efd 100644 (file)
@@ -599,6 +599,19 @@ static int tb_init_port(struct tb_port *port)
 
 }
 
+/**
+ * tb_pci_port_enable() - Enable PCIe adapter port
+ * @port: PCIe port to enable
+ * @enable: Enable/disable the PCIe adapter
+ */
+int tb_pci_port_enable(struct tb_port *port, bool enable)
+{
+       u32 word = enable ? TB_PCI_EN : 0x0;
+       if (!port->cap_adap)
+               return -ENXIO;
+       return tb_port_write(port, &word, TB_CFG_PORT, port->cap_adap, 1);
+}
+
 /* switch utility functions */
 
 static void tb_dump_switch(struct tb *tb, struct tb_regs_switch_header *sw)
index 8de43a2ab20521b7f6a2858abc5360d1cefb7d6a..36dad0a00ac2a5e83e3a088d491fd0a45aaba4aa 100644 (file)
@@ -91,14 +91,14 @@ static void tb_scan_port(struct tb_port *port)
 static void tb_free_invalid_tunnels(struct tb *tb)
 {
        struct tb_cm *tcm = tb_priv(tb);
-       struct tb_pci_tunnel *tunnel;
-       struct tb_pci_tunnel *n;
+       struct tb_tunnel *tunnel;
+       struct tb_tunnel *n;
 
        list_for_each_entry_safe(tunnel, n, &tcm->tunnel_list, list) {
-               if (tb_pci_is_invalid(tunnel)) {
-                       tb_pci_deactivate(tunnel);
+               if (tb_tunnel_is_invalid(tunnel)) {
+                       tb_tunnel_deactivate(tunnel);
                        list_del(&tunnel->list);
-                       tb_pci_free(tunnel);
+                       tb_tunnel_free(tunnel);
                }
        }
 }
@@ -178,7 +178,7 @@ static void tb_activate_pcie_devices(struct tb *tb)
        struct tb_switch *sw;
        struct tb_port *up_port;
        struct tb_port *down_port;
-       struct tb_pci_tunnel *tunnel;
+       struct tb_tunnel *tunnel;
        struct tb_cm *tcm = tb_priv(tb);
 
        /* scan for pcie devices at depth 1*/
@@ -214,17 +214,17 @@ static void tb_activate_pcie_devices(struct tb *tb)
                                     "All PCIe down ports are occupied, aborting\n");
                        continue;
                }
-               tunnel = tb_pci_alloc(tb, up_port, down_port);
+               tunnel = tb_tunnel_alloc_pci(tb, up_port, down_port);
                if (!tunnel) {
                        tb_port_info(up_port,
                                     "PCIe tunnel allocation failed, aborting\n");
                        continue;
                }
 
-               if (tb_pci_activate(tunnel)) {
+               if (tb_tunnel_activate(tunnel)) {
                        tb_port_info(up_port,
                                     "PCIe tunnel activation failed, aborting\n");
-                       tb_pci_free(tunnel);
+                       tb_tunnel_free(tunnel);
                        continue;
                }
 
@@ -353,13 +353,13 @@ static void tb_handle_event(struct tb *tb, enum tb_cfg_pkg_type type,
 static void tb_stop(struct tb *tb)
 {
        struct tb_cm *tcm = tb_priv(tb);
-       struct tb_pci_tunnel *tunnel;
-       struct tb_pci_tunnel *n;
+       struct tb_tunnel *tunnel;
+       struct tb_tunnel *n;
 
        /* tunnels are only present after everything has been initialized */
        list_for_each_entry_safe(tunnel, n, &tcm->tunnel_list, list) {
-               tb_pci_deactivate(tunnel);
-               tb_pci_free(tunnel);
+               tb_tunnel_deactivate(tunnel);
+               tb_tunnel_free(tunnel);
        }
        tb_switch_remove(tb->root_switch);
        tcm->hotplug_active = false; /* signal tb_handle_hotplug to quit */
@@ -418,7 +418,7 @@ static int tb_suspend_noirq(struct tb *tb)
 static int tb_resume_noirq(struct tb *tb)
 {
        struct tb_cm *tcm = tb_priv(tb);
-       struct tb_pci_tunnel *tunnel, *n;
+       struct tb_tunnel *tunnel, *n;
 
        tb_dbg(tb, "resuming...\n");
 
@@ -429,7 +429,7 @@ static int tb_resume_noirq(struct tb *tb)
        tb_free_invalid_tunnels(tb);
        tb_free_unplugged_children(tb->root_switch);
        list_for_each_entry_safe(tunnel, n, &tcm->tunnel_list, list)
-               tb_pci_restart(tunnel);
+               tb_tunnel_restart(tunnel);
        if (!list_empty(&tcm->tunnel_list)) {
                /*
                 * the pcie links need some time to get going.
index b4d7c4d408bd902cc66702fdeff51171bccf675f..d1f8e9722f337886e2f6baa2be6741faf9e57419 100644 (file)
@@ -457,6 +457,8 @@ int tb_port_clear_counter(struct tb_port *port, int counter);
 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);
 
+int tb_pci_port_enable(struct tb_port *port, bool enable);
+
 struct tb_path *tb_path_alloc(struct tb *tb, int num_hops);
 void tb_path_free(struct tb_path *path);
 int tb_path_activate(struct tb_path *path);
index 82ac4ec8757f0071829d4331f6a8895a56fea7b9..75e935acade56881cf7a787567ca92e4ecc1b60b 100644 (file)
@@ -211,6 +211,10 @@ struct tb_regs_port_header {
 
 } __packed;
 
+/* PCIe adapter registers */
+
+#define TB_PCI_EN                      BIT(31)
+
 /* Hop register from TB_CFG_HOPS. 8 byte per entry. */
 struct tb_regs_hop {
        /* DWORD 0 */
index 1e470564e99df6cd1762ef6f69caa73cf4aa78b4..20ce28276f7aa70c4d209ccf8bdf267345426b86 100644 (file)
@@ -1,8 +1,9 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- * Thunderbolt Cactus Ridge driver - Tunneling support
+ * Thunderbolt driver - Tunneling support
  *
  * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com>
+ * Copyright (C) 2019, Intel Corporation
  */
 
 #include <linux/slab.h>
 #include "tunnel.h"
 #include "tb.h"
 
+#define TB_PCI_PATH_DOWN               0
+#define TB_PCI_PATH_UP                 1
+
 #define __TB_TUNNEL_PRINT(level, tunnel, fmt, arg...)                   \
        do {                                                            \
-               struct tb_pci_tunnel *__tunnel = (tunnel);              \
+               struct tb_tunnel *__tunnel = (tunnel);                  \
                level(__tunnel->tb, "%llx:%x <-> %llx:%x (PCI): " fmt,  \
-                     tb_route(__tunnel->down_port->sw),                \
-                     __tunnel->down_port->port,                        \
-                     tb_route(__tunnel->up_port->sw),                  \
-                     __tunnel->up_port->port,                          \
+                     tb_route(__tunnel->src_port->sw),                 \
+                     __tunnel->src_port->port,                         \
+                     tb_route(__tunnel->dst_port->sw),                 \
+                     __tunnel->dst_port->port,                         \
                      ## arg);                                          \
        } while (0)
 
 #define tb_tunnel_info(tunnel, fmt, arg...) \
        __TB_TUNNEL_PRINT(tb_info, tunnel, fmt, ##arg)
 
+static struct tb_tunnel *tb_tunnel_alloc(struct tb *tb, size_t npaths)
+{
+       struct tb_tunnel *tunnel;
+
+       tunnel = kzalloc(sizeof(*tunnel), GFP_KERNEL);
+       if (!tunnel)
+               return NULL;
+
+       tunnel->paths = kcalloc(npaths, sizeof(tunnel->paths[0]), GFP_KERNEL);
+       if (!tunnel->paths) {
+               tb_tunnel_free(tunnel);
+               return NULL;
+       }
+
+       INIT_LIST_HEAD(&tunnel->list);
+       tunnel->tb = tb;
+       tunnel->npaths = npaths;
+
+       return tunnel;
+}
+
+static int tb_pci_activate(struct tb_tunnel *tunnel, bool activate)
+{
+       int res;
+
+       res = tb_pci_port_enable(tunnel->src_port, activate);
+       if (res)
+               return res;
+
+       return tb_pci_port_enable(tunnel->dst_port, activate);
+}
+
 static void tb_pci_init_path(struct tb_path *path)
 {
        path->egress_fc_enable = TB_PATH_SOURCE | TB_PATH_INTERNAL;
@@ -42,7 +78,10 @@ static void tb_pci_init_path(struct tb_path *path)
 }
 
 /**
- * tb_pci_alloc() - allocate a pci tunnel
+ * tb_tunnel_alloc_pci() - allocate a pci tunnel
+ * @tb: Pointer to the domain structure
+ * @up: PCIe upstream adapter port
+ * @down: PCIe downstream adapter port
  *
  * Allocate a PCI tunnel. The ports must be of type TB_TYPE_PCIE_UP and
  * TB_TYPE_PCIE_DOWN.
@@ -54,170 +93,185 @@ static void tb_pci_init_path(struct tb_path *path)
  * my thunderbolt devices). Therefore at most ONE path per device may be
  * activated.
  *
- * Return: Returns a tb_pci_tunnel on success or NULL on failure.
+ * Return: Returns a tb_tunnel on success or NULL on failure.
  */
-struct tb_pci_tunnel *tb_pci_alloc(struct tb *tb, struct tb_port *up,
-                                  struct tb_port *down)
+struct tb_tunnel *tb_tunnel_alloc_pci(struct tb *tb, struct tb_port *up,
+                                     struct tb_port *down)
 {
-       struct tb_pci_tunnel *tunnel = kzalloc(sizeof(*tunnel), GFP_KERNEL);
+       struct tb_path *path_to_up;
+       struct tb_path *path_to_down;
+       struct tb_tunnel *tunnel;
+
+       tunnel = tb_tunnel_alloc(tb, 2);
        if (!tunnel)
-               goto err;
-       tunnel->tb = tb;
-       tunnel->down_port = down;
-       tunnel->up_port = up;
-       INIT_LIST_HEAD(&tunnel->list);
-       tunnel->path_to_up = tb_path_alloc(up->sw->tb, 2);
-       if (!tunnel->path_to_up)
-               goto err;
-       tunnel->path_to_down = tb_path_alloc(up->sw->tb, 2);
-       if (!tunnel->path_to_down)
-               goto err;
-       tb_pci_init_path(tunnel->path_to_up);
-       tb_pci_init_path(tunnel->path_to_down);
-
-       tunnel->path_to_up->hops[0].in_port = down;
-       tunnel->path_to_up->hops[0].in_hop_index = 8;
-       tunnel->path_to_up->hops[0].in_counter_index = -1;
-       tunnel->path_to_up->hops[0].out_port = tb_upstream_port(up->sw)->remote;
-       tunnel->path_to_up->hops[0].next_hop_index = 8;
-
-       tunnel->path_to_up->hops[1].in_port = tb_upstream_port(up->sw);
-       tunnel->path_to_up->hops[1].in_hop_index = 8;
-       tunnel->path_to_up->hops[1].in_counter_index = -1;
-       tunnel->path_to_up->hops[1].out_port = up;
-       tunnel->path_to_up->hops[1].next_hop_index = 8;
-
-       tunnel->path_to_down->hops[0].in_port = up;
-       tunnel->path_to_down->hops[0].in_hop_index = 8;
-       tunnel->path_to_down->hops[0].in_counter_index = -1;
-       tunnel->path_to_down->hops[0].out_port = tb_upstream_port(up->sw);
-       tunnel->path_to_down->hops[0].next_hop_index = 8;
-
-       tunnel->path_to_down->hops[1].in_port =
-               tb_upstream_port(up->sw)->remote;
-       tunnel->path_to_down->hops[1].in_hop_index = 8;
-       tunnel->path_to_down->hops[1].in_counter_index = -1;
-       tunnel->path_to_down->hops[1].out_port = down;
-       tunnel->path_to_down->hops[1].next_hop_index = 8;
-       return tunnel;
+               return NULL;
 
-err:
-       if (tunnel) {
-               if (tunnel->path_to_down)
-                       tb_path_free(tunnel->path_to_down);
-               if (tunnel->path_to_up)
-                       tb_path_free(tunnel->path_to_up);
-               kfree(tunnel);
+       tunnel->activate = tb_pci_activate;
+       tunnel->src_port = down;
+       tunnel->dst_port = up;
+
+       path_to_up = tb_path_alloc(tb, 2);
+       if (!path_to_up) {
+               tb_tunnel_free(tunnel);
+               return NULL;
        }
-       return NULL;
+       tunnel->paths[TB_PCI_PATH_UP] = path_to_up;
+
+       path_to_down = tb_path_alloc(tb, 2);
+       if (!path_to_down) {
+               tb_tunnel_free(tunnel);
+               return NULL;
+       }
+       tunnel->paths[TB_PCI_PATH_DOWN] = path_to_down;
+
+       tb_pci_init_path(path_to_up);
+       tb_pci_init_path(path_to_down);
+
+       path_to_up->hops[0].in_port = down;
+       path_to_up->hops[0].in_hop_index = 8;
+       path_to_up->hops[0].in_counter_index = -1;
+       path_to_up->hops[0].out_port = tb_upstream_port(up->sw)->remote;
+       path_to_up->hops[0].next_hop_index = 8;
+
+       path_to_up->hops[1].in_port = tb_upstream_port(up->sw);
+       path_to_up->hops[1].in_hop_index = 8;
+       path_to_up->hops[1].in_counter_index = -1;
+       path_to_up->hops[1].out_port = up;
+       path_to_up->hops[1].next_hop_index = 8;
+
+       path_to_down->hops[0].in_port = up;
+       path_to_down->hops[0].in_hop_index = 8;
+       path_to_down->hops[0].in_counter_index = -1;
+       path_to_down->hops[0].out_port = tb_upstream_port(up->sw);
+       path_to_down->hops[0].next_hop_index = 8;
+
+       path_to_down->hops[1].in_port = tb_upstream_port(up->sw)->remote;
+       path_to_down->hops[1].in_hop_index = 8;
+       path_to_down->hops[1].in_counter_index = -1;
+       path_to_down->hops[1].out_port = down;
+       path_to_down->hops[1].next_hop_index = 8;
+
+       return tunnel;
 }
 
 /**
- * tb_pci_free() - free a tunnel
+ * tb_tunnel_free() - free a tunnel
+ * @tunnel: Tunnel to be freed
  *
  * The tunnel must have been deactivated.
  */
-void tb_pci_free(struct tb_pci_tunnel *tunnel)
+void tb_tunnel_free(struct tb_tunnel *tunnel)
 {
-       if (tunnel->path_to_up->activated || tunnel->path_to_down->activated) {
-               tb_tunnel_WARN(tunnel, "trying to free an activated tunnel\n");
+       int i;
+
+       if (!tunnel)
                return;
+
+       for (i = 0; i < tunnel->npaths; i++) {
+               if (tunnel->paths[i] && tunnel->paths[i]->activated) {
+                       tb_tunnel_WARN(tunnel,
+                                      "trying to free an activated tunnel\n");
+                       return;
+               }
        }
-       tb_path_free(tunnel->path_to_up);
-       tb_path_free(tunnel->path_to_down);
+
+       for (i = 0; i < tunnel->npaths; i++) {
+               if (tunnel->paths[i])
+                       tb_path_free(tunnel->paths[i]);
+       }
+
+       kfree(tunnel->paths);
        kfree(tunnel);
 }
 
 /**
- * tb_pci_is_invalid - check whether an activated path is still valid
+ * tb_tunnel_is_invalid - check whether an activated path is still valid
+ * @tunnel: Tunnel to check
  */
-bool tb_pci_is_invalid(struct tb_pci_tunnel *tunnel)
+bool tb_tunnel_is_invalid(struct tb_tunnel *tunnel)
 {
-       WARN_ON(!tunnel->path_to_up->activated);
-       WARN_ON(!tunnel->path_to_down->activated);
+       int i;
 
-       return tb_path_is_invalid(tunnel->path_to_up)
-              || tb_path_is_invalid(tunnel->path_to_down);
-}
+       for (i = 0; i < tunnel->npaths; i++) {
+               WARN_ON(!tunnel->paths[i]->activated);
+               if (tb_path_is_invalid(tunnel->paths[i]))
+                       return true;
+       }
 
-/**
- * tb_pci_port_active() - activate/deactivate PCI capability
- *
- * Return: Returns 0 on success or an error code on failure.
- */
-static int tb_pci_port_active(struct tb_port *port, bool active)
-{
-       u32 word = active ? 0x80000000 : 0x0;
-       if (!port->cap_adap)
-               return -ENXIO;
-       return tb_port_write(port, &word, TB_CFG_PORT, port->cap_adap, 1);
+       return false;
 }
 
 /**
- * tb_pci_restart() - activate a tunnel after a hardware reset
+ * tb_tunnel_restart() - activate a tunnel after a hardware reset
+ * @tunnel: Tunnel to restart
+ *
+ * Return: 0 on success and negative errno in case if failure
  */
-int tb_pci_restart(struct tb_pci_tunnel *tunnel)
+int tb_tunnel_restart(struct tb_tunnel *tunnel)
 {
-       int res;
-       tunnel->path_to_up->activated = false;
-       tunnel->path_to_down->activated = false;
+       int res, i;
 
        tb_tunnel_info(tunnel, "activating\n");
 
-       res = tb_path_activate(tunnel->path_to_up);
-       if (res)
-               goto err;
-       res = tb_path_activate(tunnel->path_to_down);
-       if (res)
-               goto err;
+       for (i = 0; i < tunnel->npaths; i++) {
+               tunnel->paths[i]->activated = false;
+               res = tb_path_activate(tunnel->paths[i]);
+               if (res)
+                       goto err;
+       }
 
-       res = tb_pci_port_active(tunnel->down_port, true);
-       if (res)
-               goto err;
+       if (tunnel->activate) {
+               res = tunnel->activate(tunnel, true);
+               if (res)
+                       goto err;
+       }
 
-       res = tb_pci_port_active(tunnel->up_port, true);
-       if (res)
-               goto err;
        return 0;
+
 err:
        tb_tunnel_warn(tunnel, "activation failed\n");
-       tb_pci_deactivate(tunnel);
+       tb_tunnel_deactivate(tunnel);
        return res;
 }
 
 /**
- * tb_pci_activate() - activate a tunnel
+ * tb_tunnel_activate() - activate a tunnel
+ * @tunnel: Tunnel to activate
  *
  * Return: Returns 0 on success or an error code on failure.
  */
-int tb_pci_activate(struct tb_pci_tunnel *tunnel)
+int tb_tunnel_activate(struct tb_tunnel *tunnel)
 {
-       if (tunnel->path_to_up->activated || tunnel->path_to_down->activated) {
-               tb_tunnel_WARN(tunnel,
-                              "trying to activate an already activated tunnel\n");
-               return -EINVAL;
-       }
+       int i;
 
-       return tb_pci_restart(tunnel);
-}
+       tb_tunnel_info(tunnel, "activating\n");
 
+       for (i = 0; i < tunnel->npaths; i++) {
+               if (tunnel->paths[i]->activated) {
+                       tb_tunnel_WARN(tunnel,
+                                      "trying to activate an already activated tunnel\n");
+                       return -EINVAL;
+               }
+       }
 
+       return tb_tunnel_restart(tunnel);
+}
 
 /**
- * tb_pci_deactivate() - deactivate a tunnel
+ * tb_tunnel_deactivate() - deactivate a tunnel
+ * @tunnel: Tunnel to deactivate
  */
-void tb_pci_deactivate(struct tb_pci_tunnel *tunnel)
+void tb_tunnel_deactivate(struct tb_tunnel *tunnel)
 {
+       int i;
+
        tb_tunnel_info(tunnel, "deactivating\n");
-       /*
-        * TODO: enable reset by writing 0x04000000 to TB_CAP_PCIE + 1 on up
-        * port. Seems to have no effect?
-        */
-       tb_pci_port_active(tunnel->up_port, false);
-       tb_pci_port_active(tunnel->down_port, false);
-       if (tunnel->path_to_down->activated)
-               tb_path_deactivate(tunnel->path_to_down);
-       if (tunnel->path_to_up->activated)
-               tb_path_deactivate(tunnel->path_to_up);
-}
 
+       if (tunnel->activate)
+               tunnel->activate(tunnel, false);
+
+       for (i = 0; i < tunnel->npaths; i++) {
+               if (tunnel->paths[i]->activated)
+                       tb_path_deactivate(tunnel->paths[i]);
+       }
+}
index dff0f27d6ab54f6bb5c2983bb3b466c8add2b37b..b4e992165e56359a77aef27855aee1b0676d31e9 100644 (file)
@@ -1,8 +1,9 @@
 /* SPDX-License-Identifier: GPL-2.0 */
 /*
- * Thunderbolt Cactus Ridge driver - Tunneling support
+ * Thunderbolt driver - Tunneling support
  *
  * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com>
+ * Copyright (C) 2019, Intel Corporation
  */
 
 #ifndef TB_TUNNEL_H_
 
 #include "tb.h"
 
-struct tb_pci_tunnel {
+/**
+ * struct tb_tunnel - Tunnel between two ports
+ * @tb: Pointer to the domain
+ * @src_port: Source port of the tunnel
+ * @dst_port: Destination port of the tunnel
+ * @paths: All paths required by the tunnel
+ * @npaths: Number of paths in @paths
+ * @activate: Optional tunnel specific activation/deactivation
+ * @list: Tunnels are linked using this field
+ */
+struct tb_tunnel {
        struct tb *tb;
-       struct tb_port *up_port;
-       struct tb_port *down_port;
-       struct tb_path *path_to_up;
-       struct tb_path *path_to_down;
+       struct tb_port *src_port;
+       struct tb_port *dst_port;
+       struct tb_path **paths;
+       size_t npaths;
+       int (*activate)(struct tb_tunnel *tunnel, bool activate);
        struct list_head list;
 };
 
-struct tb_pci_tunnel *tb_pci_alloc(struct tb *tb, struct tb_port *up,
-                                  struct tb_port *down);
-void tb_pci_free(struct tb_pci_tunnel *tunnel);
-int tb_pci_activate(struct tb_pci_tunnel *tunnel);
-int tb_pci_restart(struct tb_pci_tunnel *tunnel);
-void tb_pci_deactivate(struct tb_pci_tunnel *tunnel);
-bool tb_pci_is_invalid(struct tb_pci_tunnel *tunnel);
+struct tb_tunnel *tb_tunnel_alloc_pci(struct tb *tb, struct tb_port *up,
+                                     struct tb_port *down);
+void tb_tunnel_free(struct tb_tunnel *tunnel);
+int tb_tunnel_activate(struct tb_tunnel *tunnel);
+int tb_tunnel_restart(struct tb_tunnel *tunnel);
+void tb_tunnel_deactivate(struct tb_tunnel *tunnel);
+bool tb_tunnel_is_invalid(struct tb_tunnel *tunnel);
 
 #endif