in rio-scan.c [522:694]
static int rio_enum_peer(struct rio_net *net, struct rio_mport *port,
u8 hopcount, struct rio_dev *prev, int prev_port)
{
struct rio_dev *rdev;
u32 regval;
int tmp;
if (rio_mport_chk_dev_access(port,
RIO_ANY_DESTID(port->sys_size), hopcount)) {
pr_debug("RIO: device access check failed\n");
return -1;
}
if (rio_get_host_deviceid_lock(port, hopcount) == port->host_deviceid) {
pr_debug("RIO: PE already discovered by this host\n");
/*
* Already discovered by this host. Add it as another
* link to the existing device.
*/
rio_mport_read_config_32(port, RIO_ANY_DESTID(port->sys_size),
hopcount, RIO_COMPONENT_TAG_CSR, ®val);
if (regval) {
rdev = rio_get_comptag((regval & 0xffff), NULL);
if (rdev && prev && rio_is_switch(prev)) {
pr_debug("RIO: redundant path to %s\n",
rio_name(rdev));
prev->rswitch->nextdev[prev_port] = rdev;
}
}
return 0;
}
/* Attempt to acquire device lock */
rio_mport_write_config_32(port, RIO_ANY_DESTID(port->sys_size),
hopcount,
RIO_HOST_DID_LOCK_CSR, port->host_deviceid);
while ((tmp = rio_get_host_deviceid_lock(port, hopcount))
< port->host_deviceid) {
/* Delay a bit */
mdelay(1);
/* Attempt to acquire device lock again */
rio_mport_write_config_32(port, RIO_ANY_DESTID(port->sys_size),
hopcount,
RIO_HOST_DID_LOCK_CSR,
port->host_deviceid);
}
if (rio_get_host_deviceid_lock(port, hopcount) > port->host_deviceid) {
pr_debug(
"RIO: PE locked by a higher priority host...retreating\n");
return -1;
}
/* Setup new RIO device */
rdev = rio_setup_device(net, port, RIO_ANY_DESTID(port->sys_size),
hopcount, 1);
if (rdev) {
rdev->prev = prev;
if (prev && rio_is_switch(prev))
prev->rswitch->nextdev[prev_port] = rdev;
} else
return -1;
if (rio_is_switch(rdev)) {
int sw_destid;
int cur_destid;
int sw_inport;
u16 destid;
int port_num;
sw_inport = RIO_GET_PORT_NUM(rdev->swpinfo);
rio_route_add_entry(rdev, RIO_GLOBAL_TABLE,
port->host_deviceid, sw_inport, 0);
rdev->rswitch->route_table[port->host_deviceid] = sw_inport;
destid = rio_destid_first(net);
while (destid != RIO_INVALID_DESTID && destid < next_destid) {
if (destid != port->host_deviceid) {
rio_route_add_entry(rdev, RIO_GLOBAL_TABLE,
destid, sw_inport, 0);
rdev->rswitch->route_table[destid] = sw_inport;
}
destid = rio_destid_next(net, destid + 1);
}
pr_debug(
"RIO: found %s (vid %4.4x did %4.4x) with %d ports\n",
rio_name(rdev), rdev->vid, rdev->did,
RIO_GET_TOTAL_PORTS(rdev->swpinfo));
sw_destid = next_destid;
for (port_num = 0;
port_num < RIO_GET_TOTAL_PORTS(rdev->swpinfo);
port_num++) {
if (sw_inport == port_num) {
rio_enable_rx_tx_port(port, 0,
RIO_ANY_DESTID(port->sys_size),
hopcount, port_num);
rdev->rswitch->port_ok |= (1 << port_num);
continue;
}
cur_destid = next_destid;
if (rio_sport_is_active(rdev, port_num)) {
pr_debug(
"RIO: scanning device on port %d\n",
port_num);
rio_enable_rx_tx_port(port, 0,
RIO_ANY_DESTID(port->sys_size),
hopcount, port_num);
rdev->rswitch->port_ok |= (1 << port_num);
rio_route_add_entry(rdev, RIO_GLOBAL_TABLE,
RIO_ANY_DESTID(port->sys_size),
port_num, 0);
if (rio_enum_peer(net, port, hopcount + 1,
rdev, port_num) < 0)
return -1;
/* Update routing tables */
destid = rio_destid_next(net, cur_destid + 1);
if (destid != RIO_INVALID_DESTID) {
for (destid = cur_destid;
destid < next_destid;) {
if (destid != port->host_deviceid) {
rio_route_add_entry(rdev,
RIO_GLOBAL_TABLE,
destid,
port_num,
0);
rdev->rswitch->
route_table[destid] =
port_num;
}
destid = rio_destid_next(net,
destid + 1);
}
}
} else {
/* If switch supports Error Management,
* set PORT_LOCKOUT bit for unused port
*/
if (rdev->em_efptr)
rio_set_port_lockout(rdev, port_num, 1);
rdev->rswitch->port_ok &= ~(1 << port_num);
}
}
/* Direct Port-write messages to the enumeratiing host */
if ((rdev->src_ops & RIO_SRC_OPS_PORT_WRITE) &&
(rdev->em_efptr)) {
rio_write_config_32(rdev,
rdev->em_efptr + RIO_EM_PW_TGT_DEVID,
(port->host_deviceid << 16) |
(port->sys_size << 15));
}
rio_init_em(rdev);
/* Check for empty switch */
if (next_destid == sw_destid)
next_destid = rio_destid_alloc(net);
rdev->destid = sw_destid;
} else
pr_debug("RIO: found %s (vid %4.4x did %4.4x)\n",
rio_name(rdev), rdev->vid, rdev->did);
return 0;
}