ieee1394: support for slow links or slow 1394b phy ports

Add support for the following types of hardware:
 + nodes that have a link speed < PHY speed
 + 1394b PHYs that are less than S800 capable
 + 1394b/1394a adapter cable between two 1394b PHYs
Also, S1600 and S3200 are now supported if IEEE1394_SPEED_MAX is raised.

A probing function is added to nodemgr's config ROM fetching routine
which adjusts the allowable speed if an access problem was encountered.
Pros and Cons of the approach:
 + minimum code footprint to support this less widely used hardware
 + nearly no overhead for unaffected hardware
 - ineffective before nodemgr began to read the ROM of affected nodes
 - ineffective if ieee1394 is loaded with disable_nodemgr=1
The speed map CSRs which are published to the bus are not touched by the
patch.

Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
Cc: Hakan Ardo <hakan@debian.org>
Cc: Calculex <linux@calculex.com>
Cc: Robert J. Kosinski <robk@cmcherald.com>
Signed-off-by: Ben Collins <bcollins@ubuntu.com>
diff --git a/drivers/ieee1394/nodemgr.c b/drivers/ieee1394/nodemgr.c
index 082c7fd..948f1b8 100644
--- a/drivers/ieee1394/nodemgr.c
+++ b/drivers/ieee1394/nodemgr.c
@@ -38,6 +38,7 @@
 	struct hpsb_host *host;
 	nodeid_t nodeid;
 	unsigned int generation;
+	unsigned int speed_unverified:1;
 };
 
 
@@ -57,23 +58,75 @@
 	return NULL;
 }
 
+/*
+ * Correct the speed map entry.  This is necessary
+ *  - for nodes with link speed < phy speed,
+ *  - for 1394b nodes with negotiated phy port speed < IEEE1394_SPEED_MAX.
+ * A possible speed is determined by trial and error, using quadlet reads.
+ */
+static int nodemgr_check_speed(struct nodemgr_csr_info *ci, u64 addr,
+			       quadlet_t *buffer)
+{
+	quadlet_t q;
+	u8 i, *speed, old_speed, good_speed;
+	int ret;
+
+	speed = ci->host->speed + NODEID_TO_NODE(ci->nodeid);
+	old_speed = *speed;
+	good_speed = IEEE1394_SPEED_MAX + 1;
+
+	/* Try every speed from S100 to old_speed.
+	 * If we did it the other way around, a too low speed could be caught
+	 * if the retry succeeded for some other reason, e.g. because the link
+	 * just finished its initialization. */
+	for (i = IEEE1394_SPEED_100; i <= old_speed; i++) {
+		*speed = i;
+		ret = hpsb_read(ci->host, ci->nodeid, ci->generation, addr,
+				&q, sizeof(quadlet_t));
+		if (ret)
+			break;
+		*buffer = q;
+		good_speed = i;
+	}
+	if (good_speed <= IEEE1394_SPEED_MAX) {
+		HPSB_DEBUG("Speed probe of node " NODE_BUS_FMT " yields %s",
+			   NODE_BUS_ARGS(ci->host, ci->nodeid),
+			   hpsb_speedto_str[good_speed]);
+		*speed = good_speed;
+		ci->speed_unverified = 0;
+		return 0;
+	}
+	*speed = old_speed;
+	return ret;
+}
 
 static int nodemgr_bus_read(struct csr1212_csr *csr, u64 addr, u16 length,
                             void *buffer, void *__ci)
 {
 	struct nodemgr_csr_info *ci = (struct nodemgr_csr_info*)__ci;
-	int i, ret = 0;
+	int i, ret;
 
 	for (i = 1; ; i++) {
 		ret = hpsb_read(ci->host, ci->nodeid, ci->generation, addr,
 				buffer, length);
-		if (!ret || i == 3)
+		if (!ret) {
+			ci->speed_unverified = 0;
+			break;
+		}
+		/* Give up after 3rd failure. */
+		if (i == 3)
 			break;
 
+		/* The ieee1394_core guessed the node's speed capability from
+		 * the self ID.  Check whether a lower speed works. */
+		if (ci->speed_unverified && length == sizeof(quadlet_t)) {
+			ret = nodemgr_check_speed(ci, addr, buffer);
+			if (!ret)
+				break;
+		}
 		if (msleep_interruptible(334))
 			return -EINTR;
 	}
-
 	return ret;
 }
 
@@ -1204,6 +1257,8 @@
 	ci->host = host;
 	ci->nodeid = nodeid;
 	ci->generation = generation;
+	ci->speed_unverified =
+		host->speed[NODEID_TO_NODE(nodeid)] > IEEE1394_SPEED_100;
 
 	/* We need to detect when the ConfigROM's generation has changed,
 	 * so we only update the node's info when it needs to be.  */