Commit 647dcb5f authored by Ben Collins's avatar Ben Collins

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: default avatarStefan 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: default avatarBen Collins <bcollins@ubuntu.com>
parent 57fdb58f
......@@ -502,10 +502,8 @@ static void ether1394_reset_priv (struct net_device *dev, int set_mtu)
/* Determine speed limit */
for (i = 0; i < host->node_count; i++)
if (max_speed > host->speed_map[NODEID_TO_NODE(host->node_id) *
64 + i])
max_speed = host->speed_map[NODEID_TO_NODE(host->node_id) *
64 + i];
if (max_speed > host->speed[i])
max_speed = host->speed[i];
priv->bc_sspd = max_speed;
/* We'll use our maxpayload as the default mtu */
......
......@@ -30,12 +30,13 @@ struct hpsb_host {
unsigned char iso_listen_count[64];
int node_count; /* number of identified nodes on this bus */
int selfid_count; /* total number of SelfIDs received */
int nodes_active; /* number of nodes that are actually active */
int node_count; /* number of identified nodes on this bus */
int selfid_count; /* total number of SelfIDs received */
int nodes_active; /* number of nodes with active link layer */
u8 speed[63]; /* speed between each node and local node */
nodeid_t node_id; /* node ID of this host */
nodeid_t irm_id; /* ID of this bus' isochronous resource manager */
nodeid_t node_id; /* node ID of this host */
nodeid_t irm_id; /* ID of this bus' isochronous resource manager */
nodeid_t busmgr_id; /* ID of this bus' bus manager */
/* this nodes state */
......
......@@ -285,9 +285,9 @@ static int check_selfids(struct hpsb_host *host)
static void build_speed_map(struct hpsb_host *host, int nodecount)
{
u8 speedcap[nodecount];
u8 cldcnt[nodecount];
u8 *map = host->speed_map;
u8 *speedcap = host->speed;
struct selfid *sid;
struct ext_selfid *esid;
int i, j, n;
......@@ -354,6 +354,11 @@ static void build_speed_map(struct hpsb_host *host, int nodecount)
}
}
}
/* assume maximum speed for 1394b PHYs, nodemgr will correct it */
for (n = 0; n < nodecount; n++)
if (speedcap[n] == 3)
speedcap[n] = IEEE1394_SPEED_MAX;
}
......@@ -554,11 +559,10 @@ int hpsb_send_packet(struct hpsb_packet *packet)
return 0;
}
if (packet->type == hpsb_async && packet->node_id != ALL_NODES) {
if (packet->type == hpsb_async &&
NODEID_TO_NODE(packet->node_id) != ALL_NODES)
packet->speed_code =
host->speed_map[NODEID_TO_NODE(host->node_id) * 64
+ NODEID_TO_NODE(packet->node_id)];
}
host->speed[NODEID_TO_NODE(packet->node_id)];
dump_packet("send packet", packet->header, packet->header_size, packet->speed_code);
......
......@@ -38,6 +38,7 @@ struct nodemgr_csr_info {
struct hpsb_host *host;
nodeid_t nodeid;
unsigned int generation;
unsigned int speed_unverified:1;
};
......@@ -57,23 +58,75 @@ static char *nodemgr_find_oui_name(int oui)
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 @@ static void nodemgr_node_scan_one(struct host_info *hi,
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. */
......
......@@ -1664,10 +1664,8 @@ static int sbp2_max_speed_and_size(struct scsi_id_instance_data *scsi_id)
SBP2_DEBUG_ENTER();
/* Initial setting comes from the hosts speed map */
scsi_id->speed_code =
hi->host->speed_map[NODEID_TO_NODE(hi->host->node_id) * 64 +
NODEID_TO_NODE(scsi_id->ne->nodeid)];
hi->host->speed[NODEID_TO_NODE(scsi_id->ne->nodeid)];
/* Bump down our speed if the user requested it */
if (scsi_id->speed_code > max_speed) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment