+/**
+ * rio_chk_dev_route - Validate route to the specified device.
+ * @rdev: RIO device failed to respond
+ * @nrdev: Last active device on the route to rdev
+ * @npnum: nrdev's port number on the route to rdev
+ *
+ * Follows a route to the specified RIO device to determine the last available
+ * device (and corresponding RIO port) on the route.
+ */
+static int
+rio_chk_dev_route(struct rio_dev *rdev, struct rio_dev **nrdev, int *npnum)
+{
+ u32 result;
+ int p_port, dstid, rc = -EIO;
+ struct rio_dev *prev = NULL;
+
+ /* Find switch with failed RIO link */
+ while (rdev->prev && (rdev->prev->pef & RIO_PEF_SWITCH)) {
+ if (!rio_read_config_32(rdev->prev, RIO_DEV_ID_CAR, &result)) {
+ prev = rdev->prev;
+ break;
+ }
+ rdev = rdev->prev;
+ }
+
+ if (prev == NULL)
+ goto err_out;
+
+ dstid = (rdev->pef & RIO_PEF_SWITCH) ?
+ rdev->rswitch->destid : rdev->destid;
+ p_port = prev->rswitch->route_table[dstid];
+
+ if (p_port != RIO_INVALID_ROUTE) {
+ pr_debug("RIO: link failed on [%s]-P%d\n",
+ rio_name(prev), p_port);
+ *nrdev = prev;
+ *npnum = p_port;
+ rc = 0;
+ } else
+ pr_debug("RIO: failed to trace route to %s\n", rio_name(rdev));
+err_out:
+ return rc;
+}
+
+/**
+ * rio_mport_chk_dev_access - Validate access to the specified device.
+ * @mport: Master port to send transactions
+ * @destid: Device destination ID in network
+ * @hopcount: Number of hops into the network
+ */
+int
+rio_mport_chk_dev_access(struct rio_mport *mport, u16 destid, u8 hopcount)
+{
+ int i = 0;
+ u32 tmp;
+
+ while (rio_mport_read_config_32(mport, destid, hopcount,
+ RIO_DEV_ID_CAR, &tmp)) {
+ i++;
+ if (i == RIO_MAX_CHK_RETRY)
+ return -EIO;
+ mdelay(1);
+ }
+
+ return 0;
+}
+
+/**
+ * rio_chk_dev_access - Validate access to the specified device.
+ * @rdev: Pointer to RIO device control structure
+ */
+static int rio_chk_dev_access(struct rio_dev *rdev)
+{
+ u8 hopcount = 0xff;
+ u16 destid = rdev->destid;
+
+ if (rdev->rswitch) {
+ destid = rdev->rswitch->destid;
+ hopcount = rdev->rswitch->hopcount;
+ }
+
+ return rio_mport_chk_dev_access(rdev->net->hport, destid, hopcount);
+}
+
+/**
+ * rio_get_input_status - Sends a Link-Request/Input-Status control symbol and
+ * returns link-response (if requested).
+ * @rdev: RIO devive to issue Input-status command
+ * @pnum: Device port number to issue the command
+ * @lnkresp: Response from a link partner
+ */
+static int
+rio_get_input_status(struct rio_dev *rdev, int pnum, u32 *lnkresp)
+{
+ struct rio_mport *mport = rdev->net->hport;
+ u16 destid = rdev->rswitch->destid;
+ u8 hopcount = rdev->rswitch->hopcount;
+ u32 regval;
+ int checkcount;
+
+ if (lnkresp) {
+ /* Read from link maintenance response register
+ * to clear valid bit */
+ rio_mport_read_config_32(mport, destid, hopcount,
+ rdev->phys_efptr + RIO_PORT_N_MNT_RSP_CSR(pnum),
+ ®val);
+ udelay(50);
+ }
+
+ /* Issue Input-status command */
+ rio_mport_write_config_32(mport, destid, hopcount,
+ rdev->phys_efptr + RIO_PORT_N_MNT_REQ_CSR(pnum),
+ RIO_MNT_REQ_CMD_IS);
+
+ /* Exit if the response is not expected */
+ if (lnkresp == NULL)
+ return 0;
+
+ checkcount = 3;
+ while (checkcount--) {
+ udelay(50);
+ rio_mport_read_config_32(mport, destid, hopcount,
+ rdev->phys_efptr + RIO_PORT_N_MNT_RSP_CSR(pnum),
+ ®val);
+ if (regval & RIO_PORT_N_MNT_RSP_RVAL) {
+ *lnkresp = regval;
+ return 0;
+ }
+ }
+
+ return -EIO;
+}
+
+/**
+ * rio_clr_err_stopped - Clears port Error-stopped states.
+ * @rdev: Pointer to RIO device control structure
+ * @pnum: Switch port number to clear errors
+ * @err_status: port error status (if 0 reads register from device)
+ */
+static int rio_clr_err_stopped(struct rio_dev *rdev, u32 pnum, u32 err_status)
+{
+ struct rio_mport *mport = rdev->net->hport;
+ u16 destid = rdev->rswitch->destid;
+ u8 hopcount = rdev->rswitch->hopcount;
+ struct rio_dev *nextdev = rdev->rswitch->nextdev[pnum];
+ u32 regval;
+ u32 far_ackid, far_linkstat, near_ackid;
+
+ if (err_status == 0)
+ rio_mport_read_config_32(mport, destid, hopcount,
+ rdev->phys_efptr + RIO_PORT_N_ERR_STS_CSR(pnum),
+ &err_status);
+
+ if (err_status & RIO_PORT_N_ERR_STS_PW_OUT_ES) {
+ pr_debug("RIO_EM: servicing Output Error-Stopped state\n");
+ /*
+ * Send a Link-Request/Input-Status control symbol
+ */
+ if (rio_get_input_status(rdev, pnum, ®val)) {
+ pr_debug("RIO_EM: Input-status response timeout\n");
+ goto rd_err;
+ }
+
+ pr_debug("RIO_EM: SP%d Input-status response=0x%08x\n",
+ pnum, regval);
+ far_ackid = (regval & RIO_PORT_N_MNT_RSP_ASTAT) >> 5;
+ far_linkstat = regval & RIO_PORT_N_MNT_RSP_LSTAT;
+ rio_mport_read_config_32(mport, destid, hopcount,
+ rdev->phys_efptr + RIO_PORT_N_ACK_STS_CSR(pnum),
+ ®val);
+ pr_debug("RIO_EM: SP%d_ACK_STS_CSR=0x%08x\n", pnum, regval);
+ near_ackid = (regval & RIO_PORT_N_ACK_INBOUND) >> 24;
+ pr_debug("RIO_EM: SP%d far_ackID=0x%02x far_linkstat=0x%02x" \
+ " near_ackID=0x%02x\n",
+ pnum, far_ackid, far_linkstat, near_ackid);
+
+ /*
+ * If required, synchronize ackIDs of near and
+ * far sides.
+ */
+ if ((far_ackid != ((regval & RIO_PORT_N_ACK_OUTSTAND) >> 8)) ||
+ (far_ackid != (regval & RIO_PORT_N_ACK_OUTBOUND))) {
+ /* Align near outstanding/outbound ackIDs with
+ * far inbound.
+ */
+ rio_mport_write_config_32(mport, destid,
+ hopcount, rdev->phys_efptr +
+ RIO_PORT_N_ACK_STS_CSR(pnum),
+ (near_ackid << 24) |
+ (far_ackid << 8) | far_ackid);
+ /* Align far outstanding/outbound ackIDs with
+ * near inbound.
+ */
+ far_ackid++;
+ if (nextdev)
+ rio_write_config_32(nextdev,
+ nextdev->phys_efptr +
+ RIO_PORT_N_ACK_STS_CSR(RIO_GET_PORT_NUM(nextdev->swpinfo)),
+ (far_ackid << 24) |
+ (near_ackid << 8) | near_ackid);
+ else
+ pr_debug("RIO_EM: Invalid nextdev pointer (NULL)\n");
+ }
+rd_err:
+ rio_mport_read_config_32(mport, destid, hopcount,
+ rdev->phys_efptr + RIO_PORT_N_ERR_STS_CSR(pnum),
+ &err_status);
+ pr_debug("RIO_EM: SP%d_ERR_STS_CSR=0x%08x\n", pnum, err_status);
+ }
+
+ if ((err_status & RIO_PORT_N_ERR_STS_PW_INP_ES) && nextdev) {
+ pr_debug("RIO_EM: servicing Input Error-Stopped state\n");
+ rio_get_input_status(nextdev,
+ RIO_GET_PORT_NUM(nextdev->swpinfo), NULL);
+ udelay(50);
+
+ rio_mport_read_config_32(mport, destid, hopcount,
+ rdev->phys_efptr + RIO_PORT_N_ERR_STS_CSR(pnum),
+ &err_status);
+ pr_debug("RIO_EM: SP%d_ERR_STS_CSR=0x%08x\n", pnum, err_status);
+ }
+
+ return (err_status & (RIO_PORT_N_ERR_STS_PW_OUT_ES |
+ RIO_PORT_N_ERR_STS_PW_INP_ES)) ? 1 : 0;
+}
+