]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - drivers/thunderbolt/switch.c
thunderbolt: Add path setup code.
[mirror_ubuntu-bionic-kernel.git] / drivers / thunderbolt / switch.c
index b31b8cef301d693cc75f8c40aabc02fb66f414b1..667413f3ad7a780e716ebaed33a60c9d6ebe40d5 100644 (file)
@@ -138,6 +138,40 @@ int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged)
        return 0;
 }
 
+/**
+ * tb_port_add_nfc_credits() - add/remove non flow controlled credits to port
+ *
+ * Change the number of NFC credits allocated to @port by @credits. To remove
+ * NFC credits pass a negative amount of credits.
+ *
+ * Return: Returns 0 on success or an error code on failure.
+ */
+int tb_port_add_nfc_credits(struct tb_port *port, int credits)
+{
+       if (credits == 0)
+               return 0;
+       tb_port_info(port,
+                    "adding %#x NFC credits (%#x -> %#x)",
+                    credits,
+                    port->config.nfc_credits,
+                    port->config.nfc_credits + credits);
+       port->config.nfc_credits += credits;
+       return tb_port_write(port, &port->config.nfc_credits,
+                            TB_CFG_PORT, 4, 1);
+}
+
+/**
+ * tb_port_clear_counter() - clear a counter in TB_CFG_COUNTER
+ *
+ * Return: Returns 0 on success or an error code on failure.
+ */
+int tb_port_clear_counter(struct tb_port *port, int counter)
+{
+       u32 zero[3] = { 0, 0, 0 };
+       tb_port_info(port, "clearing counter %d\n", counter);
+       return tb_port_write(port, zero, TB_CFG_COUNTERS, 3 * counter, 3);
+}
+
 /**
  * tb_init_port() - initialize a port
  *
@@ -195,6 +229,24 @@ static void tb_dump_switch(struct tb *tb, struct tb_regs_switch_header *sw)
                sw->__unknown1, sw->__unknown4);
 }
 
+struct tb_switch *get_switch_at_route(struct tb_switch *sw, u64 route)
+{
+       u8 next_port = route; /*
+                              * Routes use a stride of 8 bits,
+                              * eventhough a port index has 6 bits at most.
+                              * */
+       if (route == 0)
+               return sw;
+       if (next_port > sw->config.max_port_number)
+               return 0;
+       if (tb_is_upstream_port(&sw->ports[next_port]))
+               return 0;
+       if (!sw->ports[next_port].remote)
+               return 0;
+       return get_switch_at_route(sw->ports[next_port].remote->sw,
+                                  route >> TB_ROUTE_SHIFT);
+}
+
 /**
  * tb_plug_events_active() - enable/disable plug events on a switch
  *
@@ -249,7 +301,8 @@ void tb_switch_free(struct tb_switch *sw)
                sw->ports[i].remote = NULL;
        }
 
-       tb_plug_events_active(sw, false);
+       if (!sw->is_unplugged)
+               tb_plug_events_active(sw, false);
 
        kfree(sw->ports);
        kfree(sw);
@@ -333,3 +386,24 @@ err:
        return NULL;
 }
 
+/**
+ * tb_sw_set_unpplugged() - set is_unplugged on switch and downstream switches
+ */
+void tb_sw_set_unpplugged(struct tb_switch *sw)
+{
+       int i;
+       if (sw == sw->tb->root_switch) {
+               tb_sw_WARN(sw, "cannot unplug root switch\n");
+               return;
+       }
+       if (sw->is_unplugged) {
+               tb_sw_WARN(sw, "is_unplugged already set\n");
+               return;
+       }
+       sw->is_unplugged = true;
+       for (i = 0; i <= sw->config.max_port_number; i++) {
+               if (!tb_is_upstream_port(&sw->ports[i]) && sw->ports[i].remote)
+                       tb_sw_set_unpplugged(sw->ports[i].remote->sw);
+       }
+}
+