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 --- first take posted in August 2005, this version posted on 2006-03-26 Previous discussion: http://marc.theaimsgroup.com/?t=107781458700006 http://marc.theaimsgroup.com/?t=112126523600002 http://marc.theaimsgroup.com/?t=112835623100003 http://marc.theaimsgroup.com/?t=114072686500005 drivers/ieee1394/eth1394.c | 6 +-- drivers/ieee1394/hosts.h | 11 +++---- drivers/ieee1394/ieee1394_core.c | 14 +++++--- drivers/ieee1394/nodemgr.c | 61 +++++++++++++++++++++++++++++++++++++-- drivers/ieee1394/sbp2.c | 4 -- 5 files changed, 76 insertions(+), 20 deletions(-) Index: linux-2.6.17-rc1/drivers/ieee1394/hosts.h =================================================================== --- linux-2.6.17-rc1.orig/drivers/ieee1394/hosts.h 2006-04-14 13:18:47.000000000 +0200 +++ linux-2.6.17-rc1/drivers/ieee1394/hosts.h 2006-04-14 13:20:22.000000000 +0200 @@ -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 */ Index: linux-2.6.17-rc1/drivers/ieee1394/ieee1394_core.c =================================================================== --- linux-2.6.17-rc1.orig/drivers/ieee1394/ieee1394_core.c 2006-04-14 13:18:47.000000000 +0200 +++ linux-2.6.17-rc1/drivers/ieee1394/ieee1394_core.c 2006-04-14 13:20:22.000000000 +0200 @@ -285,9 +285,9 @@ static int check_selfids(struct hpsb_hos 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_ } } } + + /* 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 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); Index: linux-2.6.17-rc1/drivers/ieee1394/nodemgr.c =================================================================== --- linux-2.6.17-rc1.orig/drivers/ieee1394/nodemgr.c 2006-04-14 13:18:47.000000000 +0200 +++ linux-2.6.17-rc1/drivers/ieee1394/nodemgr.c 2006-04-14 13:20:22.000000000 +0200 @@ -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 o 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 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. */ Index: linux-2.6.17-rc1/drivers/ieee1394/eth1394.c =================================================================== --- linux-2.6.17-rc1.orig/drivers/ieee1394/eth1394.c 2006-04-14 13:18:47.000000000 +0200 +++ linux-2.6.17-rc1/drivers/ieee1394/eth1394.c 2006-04-14 13:20:22.000000000 +0200 @@ -502,10 +502,8 @@ static void ether1394_reset_priv (struct /* 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 */ Index: linux-2.6.17-rc1/drivers/ieee1394/sbp2.c =================================================================== --- linux-2.6.17-rc1.orig/drivers/ieee1394/sbp2.c 2006-04-14 13:20:14.000000000 +0200 +++ linux-2.6.17-rc1/drivers/ieee1394/sbp2.c 2006-04-14 13:20:22.000000000 +0200 @@ -1652,10 +1652,8 @@ static int sbp2_max_speed_and_size(struc 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) {