All of lore.kernel.org
 help / color / mirror / Atom feed
From: Andreas Noever <andreas.noever@gmail.com>
To: linux-kernel@vger.kernel.org, Matthew Garrett <mjg59@srcf.ucam.org>
Cc: Daniel J Blueman <daniel@quora.org>,
	Bjorn Helgaas <bhelgaas@google.com>,
	linux-pci@vger.kernel.org,
	Andreas Noever <andreas.noever@gmail.com>
Subject: [PATCH v2 08/14] thunderbolt: Scan for downstream switches
Date: Fri, 11 Apr 2014 02:24:55 +0200	[thread overview]
Message-ID: <1397175901-4023-9-git-send-email-andreas.noever@gmail.com> (raw)
In-Reply-To: <1397175901-4023-1-git-send-email-andreas.noever@gmail.com>

Add utility methods tb_port_state and tb_wait_for_port. Add
tb_scan_switch which recursively checks for downstream switches.

Signed-off-by: Andreas Noever <andreas.noever@gmail.com>
---
 drivers/thunderbolt/switch.c | 97 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/thunderbolt/tb.c     | 44 ++++++++++++++++++++
 drivers/thunderbolt/tb.h     | 16 ++++++++
 3 files changed, 157 insertions(+)

diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
index 21457cc..279b9c5 100644
--- a/drivers/thunderbolt/switch.c
+++ b/drivers/thunderbolt/switch.c
@@ -53,6 +53,92 @@ static void tb_dump_port(struct tb *tb, struct tb_regs_port_header *port)
 }
 
 /**
+ * tb_port_state() - get connectedness state of a port
+ *
+ * The port must have a TB_CAP_PHY (i.e. it should be a real port).
+ *
+ * Return: Returns an enum tb_port_state on success or an error code on failure.
+ */
+static int tb_port_state(struct tb_port *port)
+{
+	struct tb_cap_phy phy;
+	int res;
+	if (port->cap_phy == 0) {
+		tb_port_WARN(port, "does not have a PHY\n");
+		return -EINVAL;
+	}
+	res = tb_port_read(port, &phy, TB_CFG_PORT, port->cap_phy, 2);
+	if (res)
+		return res;
+	return phy.state;
+}
+
+/**
+ * tb_wait_for_port() - wait for a port to become ready
+ *
+ * Wait up to 1 second for a port to reach state TB_PORT_UP. If
+ * wait_if_unplugged is set then we also wait if the port is in state
+ * TB_PORT_UNPLUGGED (it takes a while for the device to be registered after
+ * switch resume). Otherwise we only wait if a device is registered but the link
+ * has not yet been established.
+ *
+ * Return: Returns an error code on failure. Returns 0 if the port is not
+ * connected or failed to reach state TB_PORT_UP within one second. Returns 1
+ * if the port is connected and in state TB_PORT_UP.
+ */
+int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged)
+{
+	int retries = 10;
+	int state;
+	if (!port->cap_phy) {
+		tb_port_WARN(port, "does not have PHY\n");
+		return -EINVAL;
+	}
+	if (tb_is_upstream_port(port)) {
+		tb_port_WARN(port, "is the upstream port\n");
+		return -EINVAL;
+	}
+
+	while (retries--) {
+		state = tb_port_state(port);
+		if (state < 0)
+			return state;
+		if (state == TB_PORT_DISABLED) {
+			tb_port_info(port, "is disabled (state: 0)\n");
+			return 0;
+		}
+		if (state == TB_PORT_UNPLUGGED) {
+			if (wait_if_unplugged) {
+				/* used during resume */
+				tb_port_info(port,
+					     "is unplugged (state: 7), retrying...\n");
+				msleep(100);
+				continue;
+			}
+			tb_port_info(port, "is unplugged (state: 7)\n");
+			return 0;
+		}
+		if (state == TB_PORT_UP) {
+			tb_port_info(port,
+				     "is connected, link is up (state: 2)\n");
+			return 1;
+		}
+
+		/*
+		 * After plug-in the state is TB_PORT_CONNECTING. Give it some
+		 * time.
+		 */
+		tb_port_info(port,
+			     "is connected, link is not up (state: %d), retrying...\n",
+			     state);
+		msleep(100);
+	}
+	tb_port_warn(port,
+		     "failed to reach state TB_PORT_UP. Ignoring port...\n");
+	return 0;
+}
+
+/**
  * tb_init_port() - initialize a port
  *
  * This is a helper method for tb_switch_alloc. Does not check or initialize
@@ -63,6 +149,7 @@ static void tb_dump_port(struct tb *tb, struct tb_regs_port_header *port)
 static int tb_init_port(struct tb_switch *sw, u8 port_nr)
 {
 	int res;
+	int cap;
 	struct tb_port *port = &sw->ports[port_nr];
 	port->sw = sw;
 	port->port = port_nr;
@@ -71,6 +158,16 @@ static int tb_init_port(struct tb_switch *sw, u8 port_nr)
 	if (res)
 		return res;
 
+	/* Port 0 is the switch itself and has no PHY. */
+	if (port->config.type == TB_TYPE_PORT && port_nr != 0) {
+		cap = tb_find_cap(port, TB_CFG_PORT, TB_CAP_PHY);
+
+		if (cap > 0)
+			port->cap_phy = cap;
+		else
+			tb_port_WARN(port, "non switch port without a PHY\n");
+	}
+
 	tb_dump_port(sw->tb, &port->config);
 
 	/* TODO: Read dual link port, DP port and more from EEPROM. */
diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c
index f1b6100..3b716fd 100644
--- a/drivers/thunderbolt/tb.c
+++ b/drivers/thunderbolt/tb.c
@@ -11,6 +11,47 @@
 #include "tb.h"
 #include "tb_regs.h"
 
+
+/* enumeration & hot plug handling */
+
+
+static void tb_scan_port(struct tb_port *port);
+
+/**
+ * tb_scan_switch() - scan for and initialize downstream switches
+ */
+static void tb_scan_switch(struct tb_switch *sw)
+{
+	int i;
+	for (i = 1; i <= sw->config.max_port_number; i++)
+		tb_scan_port(&sw->ports[i]);
+}
+
+/**
+ * tb_scan_port() - check for and initialize switches below port
+ */
+static void tb_scan_port(struct tb_port *port)
+{
+	struct tb_switch *sw;
+	if (tb_is_upstream_port(port))
+		return;
+	if (port->config.type != TB_TYPE_PORT)
+		return;
+	if (tb_wait_for_port(port, false) <= 0)
+		return;
+	if (port->remote) {
+		tb_port_WARN(port, "port already has a remote!\n");
+		return;
+	}
+	sw = tb_switch_alloc(port->sw->tb, tb_downstream_route(port));
+	if (!sw)
+		return;
+	port->remote = tb_upstream_port(sw);
+	tb_upstream_port(sw)->remote = port;
+	tb_scan_switch(sw);
+}
+
+
 /* hotplug handling */
 
 struct tb_hotplug_event {
@@ -134,6 +175,9 @@ struct tb *thunderbolt_alloc_and_start(struct tb_nhi *nhi)
 	if (!tb->root_switch)
 		goto err_locked;
 
+	/* Full scan to discover devices added before the driver was loaded. */
+	tb_scan_switch(tb->root_switch);
+
 	/* Allow tb_handle_hotplug to progress events */
 	tb->hotplug_active = true;
 	mutex_unlock(&tb->lock);
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index feb6c7a..7e7b3fe 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -29,6 +29,7 @@ struct tb_port {
 	struct tb_regs_port_header config;
 	struct tb_switch *sw;
 	struct tb_port *remote; /* remote port, NULL if not connected */
+	int cap_phy; /* offset, zero if not found */
 	u8 port; /* port number on switch */
 };
 
@@ -160,6 +161,8 @@ void thunderbolt_shutdown_and_free(struct tb *tb);
 struct tb_switch *tb_switch_alloc(struct tb *tb, u64 route);
 void tb_switch_free(struct tb_switch *sw);
 
+int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged);
+
 int tb_find_cap(struct tb_port *port, enum tb_cfg_space space, u32 value);
 
 
@@ -173,4 +176,17 @@ static inline bool tb_is_upstream_port(struct tb_port *port)
 	return port == tb_upstream_port(port->sw);
 }
 
+/**
+ * tb_downstream_route() - get route to downstream switch
+ *
+ * Port must not be the upstream port (otherwise a loop is created).
+ *
+ * Return: Returns a route to the switch behind @port.
+ */
+static inline u64 tb_downstream_route(struct tb_port *port)
+{
+	return tb_route(port->sw)
+	       | ((u64) port->port << (port->sw->config.depth * 8));
+}
+
 #endif
-- 
1.9.2


  parent reply	other threads:[~2014-04-11  0:29 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-04-11  0:24 [Patch v2 00/14] Thunderbolt support for Apple MBP Andreas Noever
2014-04-11  0:24 ` [PATCH v2 01/14] thunderbolt: Add initial cactus ridge NHI support Andreas Noever
2014-04-11  0:24 ` [PATCH v2 02/14] thunderbolt: Add control channel interface Andreas Noever
2014-04-11  0:24 ` [PATCH v2 03/14] thunderbolt: Setup control channel Andreas Noever
2014-04-11  0:24 ` [PATCH v2 04/14] thunderbolt: Add tb_regs.h Andreas Noever
2014-04-11  0:24 ` [PATCH v2 05/14] thunderbolt: Initialize root switch and ports Andreas Noever
2014-04-11  0:24 ` [PATCH v2 06/14] thunderbolt: Add thunderbolt capability handling Andreas Noever
2014-04-11  0:24 ` [PATCH v2 07/14] thunderbolt: Enable plug events Andreas Noever
2014-04-11  0:24 ` Andreas Noever [this message]
2014-04-11  0:24 ` [PATCH v2 09/14] thunderbolt: Handle hotplug events Andreas Noever
2014-04-11  0:24 ` [PATCH v2 10/14] thunderbolt: Add path setup code Andreas Noever
2014-04-11  0:24 ` [PATCH v2 11/14] thunderbolt: Add support for simple pci tunnels Andreas Noever
2014-04-11  0:24 ` [PATCH v2 12/14] pci: Suspend/resume support for appel thunderbolt Andreas Noever
2014-04-15 17:37   ` Bjorn Helgaas
2014-04-15 23:31     ` Andreas Noever
2014-04-11  0:25 ` [PATCH v2 13/14] thunderbolt: Read switch uid from EEPROM Andreas Noever
2014-04-11  0:25 ` [PATCH v2 14/14] thunderbolt: Add suspend/hibernate support Andreas Noever
2014-04-11 21:06 ` [Patch v2 00/14] Thunderbolt support for Apple MBP Greg KH

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=1397175901-4023-9-git-send-email-andreas.noever@gmail.com \
    --to=andreas.noever@gmail.com \
    --cc=bhelgaas@google.com \
    --cc=daniel@quora.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pci@vger.kernel.org \
    --cc=mjg59@srcf.ucam.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.