From: Andreas Noever <andreas.noever@gmail.com>
To: linux-kernel@vger.kernel.org
Cc: Andreas Noever <andreas.noever@gmail.com>
Subject: [PATCH 10/12] thunderbolt: Add path setup code.
Date: Fri, 29 Nov 2013 02:35:47 +0100 [thread overview]
Message-ID: <1385688949-7101-11-git-send-email-andreas.noever@gmail.com> (raw)
In-Reply-To: <1385688949-7101-1-git-send-email-andreas.noever@gmail.com>
A thunderbolt path is a unidirectional channel between two thunderbolt
ports. Two such paths are needed to establish a pci tunnel.
This patch introduces struct tb_path as well as a set of tb_path_*
methods which are used do activate & deactive paths.
Signed-off-by: Andreas Noever <andreas.noever@gmail.com>
---
drivers/thunderbolt/tb.c | 260 +++++++++++++++++++++++++++++++++++++++++++++++
drivers/thunderbolt/tb.h | 60 +++++++++++
2 files changed, 320 insertions(+)
diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c
index d9bce38..0fea997 100644
--- a/drivers/thunderbolt/tb.c
+++ b/drivers/thunderbolt/tb.c
@@ -84,6 +84,35 @@ static void tb_dump_port(struct tb *tb, struct tb_regs_port_header *port)
port->nfc_credits);
}
+static void tb_dump_hop(struct tb_port *port, struct tb_regs_hop *hop)
+{
+ tb_port_info(port,
+ " Hop through port %d to hop %d (%s)\n",
+ hop->out_port,
+ hop->next_hop,
+ hop->enable ? "enabled" : "disabled");
+ tb_port_info(port,
+ " Weight: %d Priority: %d Credits: %d Drop: %d\n",
+ hop->weight,
+ hop->priority,
+ hop->initial_credits,
+ hop->drop_packages);
+ tb_port_info(port,
+ " Counter enabled: %d Counter index: %d\n",
+ hop->counter_enable,
+ hop->counter);
+ tb_port_info(port,
+ " Flow Control (In/Eg): %d/%d Shared Buffer (In/Eg): %d/%d\n",
+ hop->ingress_fc,
+ hop->egress_fc,
+ hop->ingress_shared_buffer,
+ hop->egress_shared_buffer);
+ tb_port_info(port,
+ " Unknown1: %#x Unknown2: %#x Unknown3: %#x\n",
+ hop->unknown1,
+ hop->unknown2,
+ hop->unknown3);
+}
/**
* tb_downstream_route() - get route to downstream switch
@@ -305,6 +334,40 @@ static int tb_wait_for_port(struct tb_port *port)
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.
+ */
+static 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.
+ */
+static 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);
+}
+
/* thunderbolt switch utility functions */
@@ -482,6 +545,203 @@ err:
return NULL;
}
+/* thunderbolt path handling */
+
+/**
+ * tb_path_alloc() - allocate a thunderbolt path
+ *
+ * Return: Returns a tb_path on success or an error code on failure.
+ */
+struct tb_path *tb_path_alloc(struct tb *tb, int num_hops)
+{
+ struct tb_path *path = kzalloc(sizeof(*path), GFP_KERNEL);
+ if (!path)
+ return NULL;
+ path->hops = kcalloc(num_hops, sizeof(*path->hops), GFP_KERNEL);
+ if (!path->hops) {
+ kfree(path);
+ return NULL;
+ }
+ path->tb = tb;
+ path->path_length = num_hops;
+ return path;
+}
+
+/**
+ * tb_path_free() - free a deactivated path
+ */
+void tb_path_free(struct tb_path *path)
+{
+ if (path->activated) {
+ tb_WARN(path->tb, "trying to free an activated path\n")
+ return;
+ }
+ kfree(path->hops);
+ kfree(path);
+}
+
+static void __tb_path_deallocate_nfc(struct tb_path *path, int first_hop)
+{
+ int i, res;
+ for (i = first_hop; i < path->path_length; i++) {
+ res = tb_port_add_nfc_credits(path->hops[i].in_port,
+ -path->nfc_credits);
+ if (res)
+ tb_port_warn(path->hops[i].in_port,
+ "nfc credits deallocation failed for hop %d\n",
+ i);
+ }
+}
+
+static void __tb_path_deactivate_hops(struct tb_path *path, int first_hop)
+{
+ int i, res;
+ struct tb_regs_hop hop = { };
+ for (i = first_hop; i < path->path_length; i++) {
+ res = tb_port_write(path->hops[i].in_port,
+ &hop,
+ TB_CFG_HOPS,
+ 2 * path->hops[i].in_hop_index,
+ 2);
+ if (res)
+ tb_port_warn(path->hops[i].in_port,
+ "hop deactivation failed for hop %d, index %d\n",
+ i,
+ path->hops[i].in_hop_index);
+ }
+}
+
+void tb_path_deactivate(struct tb_path *path)
+{
+ if (!path->activated) {
+ tb_WARN(path->tb, "trying to deactivate an inactive path\n");
+ return;
+ }
+ tb_info(path->tb,
+ "deactivating path from %llx:%x to %llx:%x\n",
+ tb_route(path->hops[0].in_port->sw),
+ path->hops[0].in_port->port,
+ tb_route(path->hops[path->path_length - 1].out_port->sw),
+ path->hops[path->path_length - 1].out_port->port);
+ __tb_path_deactivate_hops(path, 0);
+ __tb_path_deallocate_nfc(path, 0);
+ path->activated = false;
+}
+
+/**
+ * tb_path_activate() - activate a path
+ *
+ * Activate a path starting with the last hop and iterating backwards. The
+ * caller must fill path->hops before calling path_activate().
+ *
+ * Return: Returns 0 on success or an error code on failure.
+ */
+int tb_path_activate(struct tb_path *path)
+{
+ int i, res;
+ enum tb_path_port out_mask, in_mask;
+ if (path->activated) {
+ tb_WARN(path->tb, "trying to activate already activated path\n");
+ return -EINVAL;
+ }
+
+ tb_info(path->tb,
+ "activating path from %llx:%x to %llx:%x\n",
+ tb_route(path->hops[0].in_port->sw),
+ path->hops[0].in_port->port,
+ tb_route(path->hops[path->path_length - 1].out_port->sw),
+ path->hops[path->path_length - 1].out_port->port);
+
+ /* Clear counters. */
+ for (i = path->path_length - 1; i >= 0; i--) {
+ if (path->hops[i].in_counter_index == -1)
+ continue;
+ res = tb_port_clear_counter(path->hops[i].in_port,
+ path->hops[i].in_counter_index);
+ if (res)
+ goto err;
+ }
+
+ /* Add non flow controlled credits. */
+ for (i = path->path_length - 1; i >= 0; i--) {
+ res = tb_port_add_nfc_credits(path->hops[i].in_port,
+ path->nfc_credits);
+ if (res) {
+ __tb_path_deallocate_nfc(path, i);
+ goto err;
+ }
+ }
+
+ /* Activate hops. */
+ for (i = path->path_length - 1; i >= 0; i--) {
+ struct tb_regs_hop hop;
+
+ /* dword 0 */
+ hop.next_hop = path->hops[i].next_hop_index;
+ hop.out_port = path->hops[i].out_port->port;
+ /* TODO: figure out why these are good values */
+ hop.initial_credits = (i == path->path_length - 1) ? 16 : 7;
+ hop.unknown1 = 0;
+ hop.enable = 1;
+
+ /* dword 1 */
+ out_mask = (i == path->path_length - 1) ?
+ TB_PATH_DESTINATION : TB_PATH_INTERNAL;
+ in_mask = (i == 0) ? TB_PATH_SOURCE : TB_PATH_INTERNAL;
+ hop.weight = path->weight;
+ hop.unknown2 = 0;
+ hop.priority = path->priority;
+ hop.drop_packages = path->drop_packages;
+ hop.counter = path->hops[i].in_counter_index;
+ hop.counter_enable = path->hops[i].in_counter_index != -1;
+ hop.ingress_fc = path->ingress_fc_enable & in_mask;
+ hop.egress_fc = path->egress_fc_enable & out_mask;
+ hop.ingress_shared_buffer = path->ingress_shared_buffer
+ & in_mask;
+ hop.egress_shared_buffer = path->egress_shared_buffer
+ & out_mask;
+ hop.unknown3 = 0;
+
+ tb_port_info(path->hops[i].in_port,
+ "Writing hop %d, index %d",
+ i,
+ path->hops[i].in_hop_index);
+ tb_dump_hop(path->hops[i].in_port, &hop);
+ res = tb_port_write(path->hops[i].in_port,
+ &hop,
+ TB_CFG_HOPS,
+ 2 * path->hops[i].in_hop_index,
+ 2);
+ if (res) {
+ __tb_path_deactivate_hops(path, i);
+ __tb_path_deallocate_nfc(path, 0);
+ goto err;
+ }
+ }
+ path->activated = true;
+ tb_info(path->tb, "path activation complete\n");
+ return 0;
+err:
+ tb_WARN(path->tb, "path activation failed\n");
+ return res;
+}
+
+/**
+ * tb_path_is_invalid() - check whether any ports on the path are invalid
+ *
+ * Return: Returns true if the path is invalid, false otherwise.
+ */
+bool tb_path_is_invalid(struct tb_path *path)
+{
+ int i = 0;
+ for (i = 0; i < path->path_length; i++) {
+ if (path->hops[i].in_port->invalid)
+ return true;
+ if (path->hops[i].out_port->invalid)
+ return true;
+ }
+ return false;
+}
/* startup, enumeration, hot plug handling and shutdown */
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index fe35d4d..cc1f079 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -39,6 +39,60 @@ struct tb_port {
};
/**
+ * struct tb_path_hop - routing information for a tb_path
+ *
+ * Hop configuration is always done on the IN port of a switch.
+ * in_port and out_port have to be on the same switch. Packets arriving on
+ * in_port with "hop" = in_hop_index will get routed to through out_port. The
+ * next hop to take (on out_port->remote) is determined by next_hop_index.
+ *
+ * in_counter_index is the index of a counter (in TB_CFG_COUNTERS) on the in
+ * port.
+ */
+struct tb_path_hop {
+ struct tb_port *in_port;
+ struct tb_port *out_port;
+ int in_hop_index;
+ int in_counter_index; /* write -1 to disable counters for this hop. */
+ int next_hop_index;
+};
+
+/**
+ * enum tb_path_port - path options mask
+ */
+enum tb_path_port {
+ TB_PATH_NONE = 0,
+ TB_PATH_SOURCE = 1, /* activate on the first hop (out of src) */
+ TB_PATH_INTERNAL = 2, /* activate on other hops (not the first/last) */
+ TB_PATH_DESTINATION = 4, /* activate on the last hop (into dst) */
+ TB_PATH_ALL = 7,
+};
+
+/**
+ * struct tb_path - a unidirectional path between two ports
+ *
+ * A path consists of a number of hops (see tb_path_hop). To establish a PCIe
+ * tunnel two paths have to be created between the two PCIe ports.
+ *
+ */
+struct tb_path {
+ struct tb *tb;
+ int nfc_credits; /* non flow controlled credits */
+ enum tb_path_port ingress_shared_buffer;
+ enum tb_path_port egress_shared_buffer;
+ enum tb_path_port ingress_fc_enable;
+ enum tb_path_port egress_fc_enable;
+
+ int priority:3;
+ int weight:4;
+ bool drop_packages;
+ bool activated;
+ struct tb_path_hop *hops;
+ int path_length; /* number of hops */
+};
+
+
+/**
* struct tb - main thunderbolt bus structure
*/
struct tb {
@@ -164,4 +218,10 @@ void thunderbolt_shutdown_and_free(struct tb *tb);
int tb_find_cap(struct tb_port *port, enum tb_cfg_space space, u32 value);
+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);
+void tb_path_deactivate(struct tb_path *path);
+bool tb_path_is_invalid(struct tb_path *path);
+
#endif
--
1.8.4.2
next prev parent reply other threads:[~2013-11-29 1:40 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2013-11-29 1:35 [PATCH 00/12] Thunderbolt hotplug support for Apple hardware (testers needed) Andreas Noever
2013-11-29 1:35 ` [PATCH 01/12] thunderbolt: Add initial cactus ridge NHI support Andreas Noever
2013-11-29 1:35 ` [PATCH 02/12] thunderbolt: Add configuration channel interface Andreas Noever
2013-11-29 1:35 ` [PATCH 03/12] thunderbolt: Setup configuration channel Andreas Noever
2013-11-29 1:35 ` [PATCH 04/12] thunderbolt: Add tb_regs.h Andreas Noever
2013-11-29 1:35 ` [PATCH 05/12] thunderbolt: Initialize root switch and ports Andreas Noever
2013-11-29 1:35 ` [PATCH 06/12] thunderbolt: Add thunderbolt capability handling Andreas Noever
2013-11-29 1:35 ` [PATCH 07/12] thunderbolt: Enable plug events Andreas Noever
2013-11-29 1:35 ` [PATCH 08/12] thunderbolt: Scan for downstream switches Andreas Noever
2013-11-29 1:35 ` [PATCH 09/12] thunderbolt: Handle hotplug events Andreas Noever
2013-11-29 1:35 ` Andreas Noever [this message]
2013-11-29 1:35 ` [PATCH 11/12] thunderbolt: Add support for simple pci tunnels Andreas Noever
2013-11-29 1:35 ` [PATCH 12/12] thunderbolt: Scan and activate one PCI device Andreas Noever
2013-12-02 16:29 ` [PATCH 00/12] Thunderbolt hotplug support for Apple hardware (testers needed) Matthew Garrett
2014-03-04 0:09 ` Matthew Garrett
2014-03-04 23:59 ` Andreas Noever
2014-03-05 0:26 ` Matthew Garrett
2014-03-08 2:40 ` Matthew Garrett
2014-03-11 13:08 ` Andreas Noever
2014-03-11 14:00 ` Matthew Garrett
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1385688949-7101-11-git-send-email-andreas.noever@gmail.com \
--to=andreas.noever@gmail.com \
--cc=linux-kernel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.