]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/infiniband/hw/ipath/ipath_intr.c
IB/ipath: Misc changes to prepare for IB7220 introduction
[linux-2.6-omap-h63xx.git] / drivers / infiniband / hw / ipath / ipath_intr.c
index c61f9da2964ab25a51bb686808ffaa3c4ceb1e33..90b972f6a8401b1df97c56e9fd6d6b9fba00792f 100644 (file)
@@ -32,6 +32,7 @@
  */
 
 #include <linux/pci.h>
+#include <linux/delay.h>
 
 #include "ipath_kernel.h"
 #include "ipath_verbs.h"
@@ -59,9 +60,11 @@ static void ipath_clrpiobuf(struct ipath_devdata *dd, u32 pnum)
        dev_info(&dd->pcidev->dev,
                "Rewrite PIO buffer %u, to recover from parity error\n",
                pnum);
-       *pbuf = dwcnt+1; /* no flush required, since already in freeze */
-       while(--dwcnt)
-               *pbuf++ = 0;
+
+       /* no flush required, since already in freeze */
+       writel(dwcnt + 1, pbuf);
+       while (--dwcnt)
+               writel(0, pbuf++);
 }
 
 /*
@@ -70,7 +73,7 @@ static void ipath_clrpiobuf(struct ipath_devdata *dd, u32 pnum)
  * If rewrite is true, and bits are set in the sendbufferror registers,
  * we'll write to the buffer, for error recovery on parity errors.
  */
-static void ipath_disarm_senderrbufs(struct ipath_devdata *dd, int rewrite)
+void ipath_disarm_senderrbufs(struct ipath_devdata *dd, int rewrite)
 {
        u32 piobcnt;
        unsigned long sbuf[4];
@@ -84,12 +87,14 @@ static void ipath_disarm_senderrbufs(struct ipath_devdata *dd, int rewrite)
                dd, dd->ipath_kregs->kr_sendbuffererror);
        sbuf[1] = ipath_read_kreg64(
                dd, dd->ipath_kregs->kr_sendbuffererror + 1);
-       if (piobcnt > 128) {
+       if (piobcnt > 128)
                sbuf[2] = ipath_read_kreg64(
                        dd, dd->ipath_kregs->kr_sendbuffererror + 2);
+       if (piobcnt > 192)
                sbuf[3] = ipath_read_kreg64(
                        dd, dd->ipath_kregs->kr_sendbuffererror + 3);
-       }
+       else
+               sbuf[3] = 0;
 
        if (sbuf[0] || sbuf[1] || (piobcnt > 128 && (sbuf[2] || sbuf[3]))) {
                int i;
@@ -254,24 +259,20 @@ void ipath_format_hwerrors(u64 hwerrs,
 }
 
 /* return the strings for the most common link states */
-static char *ib_linkstate(u32 linkstate)
+static char *ib_linkstate(struct ipath_devdata *dd, u64 ibcs)
 {
        char *ret;
+       u32 state;
 
-       switch (linkstate) {
-       case IPATH_IBSTATE_INIT:
+       state = ipath_ib_state(dd, ibcs);
+       if (state == dd->ib_init)
                ret = "Init";
-               break;
-       case IPATH_IBSTATE_ARM:
+       else if (state == dd->ib_arm)
                ret = "Arm";
-               break;
-       case IPATH_IBSTATE_ACTIVE:
+       else if (state == dd->ib_active)
                ret = "Active";
-               break;
-       default:
+       else
                ret = "Down";
-       }
-
        return ret;
 }
 
@@ -286,103 +287,170 @@ void signal_ib_event(struct ipath_devdata *dd, enum ib_event_type ev)
 }
 
 static void handle_e_ibstatuschanged(struct ipath_devdata *dd,
-                                    ipath_err_t errs, int noprint)
+                                    ipath_err_t errs)
 {
-       u64 val;
-       u32 ltstate, lstate;
+       u32 ltstate, lstate, ibstate, lastlstate;
+       u32 init = dd->ib_init;
+       u32 arm = dd->ib_arm;
+       u32 active = dd->ib_active;
+       const u64 ibcs = ipath_read_kreg64(dd, dd->ipath_kregs->kr_ibcstatus);
+
+       lstate = ipath_ib_linkstate(dd, ibcs); /* linkstate */
+       ibstate = ipath_ib_state(dd, ibcs);
+       /* linkstate at last interrupt */
+       lastlstate = ipath_ib_linkstate(dd, dd->ipath_lastibcstat);
+       ltstate = ipath_ib_linktrstate(dd, ibcs); /* linktrainingtate */
 
        /*
-        * even if diags are enabled, we want to notice LINKINIT, etc.
-        * We just don't want to change the LED state, or
-        * dd->ipath_kregs->kr_ibcctrl
+        * Since going into a recovery state causes the link state to go
+        * down and since recovery is transitory, it is better if we "miss"
+        * ever seeing the link training state go into recovery (i.e.,
+        * ignore this transition for link state special handling purposes)
+        * without even updating ipath_lastibcstat.
         */
-       val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_ibcstatus);
-       lstate = val & IPATH_IBSTATE_MASK;
+       if ((ltstate == INFINIPATH_IBCS_LT_STATE_RECOVERRETRAIN) ||
+           (ltstate == INFINIPATH_IBCS_LT_STATE_RECOVERWAITRMT) ||
+           (ltstate == INFINIPATH_IBCS_LT_STATE_RECOVERIDLE))
+               goto done;
 
        /*
-        * this is confusing enough when it happens that I want to always put it
-        * on the console and in the logs.  If it was a requested state change,
-        * we'll have already cleared the flags, so we won't print this warning
+        * if linkstate transitions into INIT from any of the various down
+        * states, or if it transitions from any of the up (INIT or better)
+        * states into any of the down states (except link recovery), then
+        * call the chip-specific code to take appropriate actions.
         */
-       if ((lstate != IPATH_IBSTATE_ARM && lstate != IPATH_IBSTATE_ACTIVE)
-               && (dd->ipath_flags & (IPATH_LINKARMED | IPATH_LINKACTIVE))) {
-               dev_info(&dd->pcidev->dev, "Link state changed from %s to %s\n",
-                                (dd->ipath_flags & IPATH_LINKARMED) ? "ARM" : "ACTIVE",
-                                ib_linkstate(lstate));
-               /*
-                * Flush all queued sends when link went to DOWN or INIT,
-                * to be sure that they don't block SMA and other MAD packets
-                */
-               ipath_cancel_sends(dd, 1);
-       }
-       else if (lstate == IPATH_IBSTATE_INIT || lstate == IPATH_IBSTATE_ARM ||
-           lstate == IPATH_IBSTATE_ACTIVE) {
-               /*
-                * only print at SMA if there is a change, debug if not
-                * (sometimes we want to know that, usually not).
-                */
-               if (lstate == ((unsigned) dd->ipath_lastibcstat
-                              & IPATH_IBSTATE_MASK)) {
-                       ipath_dbg("Status change intr but no change (%s)\n",
-                                 ib_linkstate(lstate));
+       if (lstate >= INFINIPATH_IBCS_L_STATE_INIT &&
+               lastlstate == INFINIPATH_IBCS_L_STATE_DOWN) {
+               /* transitioned to UP */
+               if (dd->ipath_f_ib_updown(dd, 1, ibcs)) {
+                       /* link came up, so we must no longer be disabled */
+                       dd->ipath_flags &= ~IPATH_IB_LINK_DISABLED;
+                       ipath_cdbg(LINKVERB, "LinkUp handled, skipped\n");
+                       goto skip_ibchange; /* chip-code handled */
+               }
+       } else if ((lastlstate >= INFINIPATH_IBCS_L_STATE_INIT ||
+               (dd->ipath_flags & IPATH_IB_FORCE_NOTIFY)) &&
+               ltstate <= INFINIPATH_IBCS_LT_STATE_CFGWAITRMT &&
+               ltstate != INFINIPATH_IBCS_LT_STATE_LINKUP) {
+               int handled;
+               handled = dd->ipath_f_ib_updown(dd, 0, ibcs);
+               dd->ipath_flags &= ~IPATH_IB_FORCE_NOTIFY;
+               if (handled) {
+                       ipath_cdbg(LINKVERB, "LinkDown handled, skipped\n");
+                       goto skip_ibchange; /* chip-code handled */
                }
-               else
-                       ipath_cdbg(VERBOSE, "Unit %u link state %s, last "
-                                  "was %s\n", dd->ipath_unit,
-                                  ib_linkstate(lstate),
-                                  ib_linkstate((unsigned)
-                                               dd->ipath_lastibcstat
-                                               & IPATH_IBSTATE_MASK));
        }
-       else {
-               lstate = dd->ipath_lastibcstat & IPATH_IBSTATE_MASK;
-               if (lstate == IPATH_IBSTATE_INIT ||
-                   lstate == IPATH_IBSTATE_ARM ||
-                   lstate == IPATH_IBSTATE_ACTIVE)
-                       ipath_cdbg(VERBOSE, "Unit %u link state down"
-                                  " (state 0x%x), from %s\n",
-                                  dd->ipath_unit,
-                                  (u32)val & IPATH_IBSTATE_MASK,
-                                  ib_linkstate(lstate));
-               else
-                       ipath_cdbg(VERBOSE, "Unit %u link state changed "
-                                  "to 0x%x from down (%x)\n",
-                                  dd->ipath_unit, (u32) val, lstate);
+
+       /*
+        * Significant enough to always print and get into logs, if it was
+        * unexpected.  If it was a requested state change, we'll have
+        * already cleared the flags, so we won't print this warning
+        */
+       if ((ibstate != arm && ibstate != active) &&
+           (dd->ipath_flags & (IPATH_LINKARMED | IPATH_LINKACTIVE))) {
+               dev_info(&dd->pcidev->dev, "Link state changed from %s "
+                        "to %s\n", (dd->ipath_flags & IPATH_LINKARMED) ?
+                        "ARM" : "ACTIVE", ib_linkstate(dd, ibcs));
        }
-       ltstate = (val >> INFINIPATH_IBCS_LINKTRAININGSTATE_SHIFT) &
-               INFINIPATH_IBCS_LINKTRAININGSTATE_MASK;
-       lstate = (val >> INFINIPATH_IBCS_LINKSTATE_SHIFT) &
-               INFINIPATH_IBCS_LINKSTATE_MASK;
 
        if (ltstate == INFINIPATH_IBCS_LT_STATE_POLLACTIVE ||
            ltstate == INFINIPATH_IBCS_LT_STATE_POLLQUIET) {
-               u32 last_ltstate;
-
+               u32 lastlts;
+               lastlts = ipath_ib_linktrstate(dd, dd->ipath_lastibcstat);
                /*
-                * Ignore cycling back and forth from Polling.Active
-                * to Polling.Quiet while waiting for the other end of
-                * the link to come up. We will cycle back and forth
-                * between them if no cable is plugged in,
-                * the other device is powered off or disabled, etc.
+                * Ignore cycling back and forth from Polling.Active to
+                * Polling.Quiet while waiting for the other end of the link
+                * to come up, except to try and decide if we are connected
+                * to a live IB device or not.  We will cycle back and
+                * forth between them if no cable is plugged in, the other
+                * device is powered off or disabled, etc.
                 */
-               last_ltstate = (dd->ipath_lastibcstat >>
-                               INFINIPATH_IBCS_LINKTRAININGSTATE_SHIFT)
-                       & INFINIPATH_IBCS_LINKTRAININGSTATE_MASK;
-               if (last_ltstate == INFINIPATH_IBCS_LT_STATE_POLLACTIVE
-                   || last_ltstate ==
-                   INFINIPATH_IBCS_LT_STATE_POLLQUIET) {
-                       if (dd->ipath_ibpollcnt > 40) {
+               if (lastlts == INFINIPATH_IBCS_LT_STATE_POLLACTIVE ||
+                   lastlts == INFINIPATH_IBCS_LT_STATE_POLLQUIET) {
+                       if (!(dd->ipath_flags & IPATH_IB_AUTONEG_INPROG) &&
+                            (++dd->ipath_ibpollcnt == 40)) {
                                dd->ipath_flags |= IPATH_NOCABLE;
                                *dd->ipath_statusp |=
                                        IPATH_STATUS_IB_NOCABLE;
-                       } else
-                               dd->ipath_ibpollcnt++;
+                               ipath_cdbg(LINKVERB, "Set NOCABLE\n");
+                       }
+                       ipath_cdbg(LINKVERB, "POLL change to %s (%x)\n",
+                               ipath_ibcstatus_str[ltstate], ibstate);
                        goto skip_ibchange;
                }
        }
-       dd->ipath_ibpollcnt = 0;        /* some state other than 2 or 3 */
+
+       dd->ipath_ibpollcnt = 0; /* not poll*, now */
        ipath_stats.sps_iblink++;
-       if (ltstate != INFINIPATH_IBCS_LT_STATE_LINKUP) {
+
+       if (ibstate != init && dd->ipath_lastlinkrecov && ipath_linkrecovery) {
+               u64 linkrecov;
+               linkrecov = ipath_snap_cntr(dd,
+                       dd->ipath_cregs->cr_iblinkerrrecovcnt);
+               if (linkrecov != dd->ipath_lastlinkrecov) {
+                       ipath_dbg("IB linkrecov up %Lx (%s %s) recov %Lu\n",
+                               ibcs, ib_linkstate(dd, ibcs),
+                               ipath_ibcstatus_str[ltstate],
+                               linkrecov);
+                       /* and no more until active again */
+                       dd->ipath_lastlinkrecov = 0;
+                       ipath_set_linkstate(dd, IPATH_IB_LINKDOWN);
+                       goto skip_ibchange;
+               }
+       }
+
+       if (ibstate == init || ibstate == arm || ibstate == active) {
+               *dd->ipath_statusp &= ~IPATH_STATUS_IB_NOCABLE;
+               if (ibstate == init || ibstate == arm) {
+                       *dd->ipath_statusp &= ~IPATH_STATUS_IB_READY;
+                       if (dd->ipath_flags & IPATH_LINKACTIVE)
+                               signal_ib_event(dd, IB_EVENT_PORT_ERR);
+               }
+               if (ibstate == arm) {
+                       dd->ipath_flags |= IPATH_LINKARMED;
+                       dd->ipath_flags &= ~(IPATH_LINKUNK |
+                               IPATH_LINKINIT | IPATH_LINKDOWN |
+                               IPATH_LINKACTIVE | IPATH_NOCABLE);
+                       ipath_hol_down(dd);
+               } else  if (ibstate == init) {
+                       /*
+                        * set INIT and DOWN.  Down is checked by
+                        * most of the other code, but INIT is
+                        * useful to know in a few places.
+                        */
+                       dd->ipath_flags |= IPATH_LINKINIT |
+                               IPATH_LINKDOWN;
+                       dd->ipath_flags &= ~(IPATH_LINKUNK |
+                               IPATH_LINKARMED | IPATH_LINKACTIVE |
+                               IPATH_NOCABLE);
+                       ipath_hol_down(dd);
+               } else {  /* active */
+                       dd->ipath_lastlinkrecov = ipath_snap_cntr(dd,
+                               dd->ipath_cregs->cr_iblinkerrrecovcnt);
+                       *dd->ipath_statusp |=
+                               IPATH_STATUS_IB_READY | IPATH_STATUS_IB_CONF;
+                       dd->ipath_flags |= IPATH_LINKACTIVE;
+                       dd->ipath_flags &= ~(IPATH_LINKUNK | IPATH_LINKINIT
+                               | IPATH_LINKDOWN | IPATH_LINKARMED |
+                               IPATH_NOCABLE);
+                       signal_ib_event(dd, IB_EVENT_PORT_ACTIVE);
+                       /* LED active not handled in chip _f_updown */
+                       dd->ipath_f_setextled(dd, lstate, ltstate);
+                       ipath_hol_up(dd);
+               }
+
+               /*
+                * print after we've already done the work, so as not to
+                * delay the state changes and notifications, for debugging
+                */
+               if (lstate == lastlstate)
+                       ipath_cdbg(LINKVERB, "Unchanged from last: %s "
+                               "(%x)\n", ib_linkstate(dd, ibcs), ibstate);
+               else
+                       ipath_cdbg(VERBOSE, "Unit %u: link up to %s %s (%x)\n",
+                                 dd->ipath_unit, ib_linkstate(dd, ibcs),
+                                 ipath_ibcstatus_str[ltstate],  ibstate);
+       } else { /* down */
                if (dd->ipath_flags & IPATH_LINKACTIVE)
                        signal_ib_event(dd, IB_EVENT_PORT_ERR);
                dd->ipath_flags |= IPATH_LINKDOWN;
@@ -391,65 +459,24 @@ static void handle_e_ibstatuschanged(struct ipath_devdata *dd,
                                     IPATH_LINKARMED);
                *dd->ipath_statusp &= ~IPATH_STATUS_IB_READY;
                dd->ipath_lli_counter = 0;
-               if (!noprint) {
-                       if (((dd->ipath_lastibcstat >>
-                             INFINIPATH_IBCS_LINKSTATE_SHIFT) &
-                            INFINIPATH_IBCS_LINKSTATE_MASK)
-                           == INFINIPATH_IBCS_L_STATE_ACTIVE)
-                               /* if from up to down be more vocal */
-                               ipath_cdbg(VERBOSE,
-                                          "Unit %u link now down (%s)\n",
-                                          dd->ipath_unit,
-                                          ipath_ibcstatus_str[ltstate]);
-                       else
-                               ipath_cdbg(VERBOSE, "Unit %u link is "
-                                          "down (%s)\n", dd->ipath_unit,
-                                          ipath_ibcstatus_str[ltstate]);
-               }
 
-               dd->ipath_f_setextled(dd, lstate, ltstate);
-       } else if ((val & IPATH_IBSTATE_MASK) == IPATH_IBSTATE_ACTIVE) {
-               dd->ipath_flags |= IPATH_LINKACTIVE;
-               dd->ipath_flags &=
-                       ~(IPATH_LINKUNK | IPATH_LINKINIT | IPATH_LINKDOWN |
-                         IPATH_LINKARMED | IPATH_NOCABLE);
-               *dd->ipath_statusp &= ~IPATH_STATUS_IB_NOCABLE;
-               *dd->ipath_statusp |=
-                       IPATH_STATUS_IB_READY | IPATH_STATUS_IB_CONF;
-               dd->ipath_f_setextled(dd, lstate, ltstate);
-               signal_ib_event(dd, IB_EVENT_PORT_ACTIVE);
-       } else if ((val & IPATH_IBSTATE_MASK) == IPATH_IBSTATE_INIT) {
-               if (dd->ipath_flags & IPATH_LINKACTIVE)
-                       signal_ib_event(dd, IB_EVENT_PORT_ERR);
-               /*
-                * set INIT and DOWN.  Down is checked by most of the other
-                * code, but INIT is useful to know in a few places.
-                */
-               dd->ipath_flags |= IPATH_LINKINIT | IPATH_LINKDOWN;
-               dd->ipath_flags &=
-                       ~(IPATH_LINKUNK | IPATH_LINKACTIVE | IPATH_LINKARMED
-                         | IPATH_NOCABLE);
-               *dd->ipath_statusp &= ~(IPATH_STATUS_IB_NOCABLE
-                                       | IPATH_STATUS_IB_READY);
-               dd->ipath_f_setextled(dd, lstate, ltstate);
-       } else if ((val & IPATH_IBSTATE_MASK) == IPATH_IBSTATE_ARM) {
-               if (dd->ipath_flags & IPATH_LINKACTIVE)
-                       signal_ib_event(dd, IB_EVENT_PORT_ERR);
-               dd->ipath_flags |= IPATH_LINKARMED;
-               dd->ipath_flags &=
-                       ~(IPATH_LINKUNK | IPATH_LINKDOWN | IPATH_LINKINIT |
-                         IPATH_LINKACTIVE | IPATH_NOCABLE);
-               *dd->ipath_statusp &= ~(IPATH_STATUS_IB_NOCABLE
-                                       | IPATH_STATUS_IB_READY);
-               dd->ipath_f_setextled(dd, lstate, ltstate);
-       } else {
-               if (!noprint)
-                       ipath_dbg("IBstatuschange unit %u: %s (%x)\n",
-                                 dd->ipath_unit,
-                                 ipath_ibcstatus_str[ltstate], ltstate);
+               if (lastlstate != INFINIPATH_IBCS_L_STATE_DOWN)
+                       ipath_cdbg(VERBOSE, "Unit %u link state down "
+                                  "(state 0x%x), from %s\n",
+                                  dd->ipath_unit, lstate,
+                                  ib_linkstate(dd, dd->ipath_lastibcstat));
+               else
+                       ipath_cdbg(LINKVERB, "Unit %u link state changed "
+                                  "to %s (0x%x) from down (%x)\n",
+                                  dd->ipath_unit,
+                                  ipath_ibcstatus_str[ltstate],
+                                  ibstate, lastlstate);
        }
+
 skip_ibchange:
-       dd->ipath_lastibcstat = val;
+       dd->ipath_lastibcstat = ibcs;
+done:
+       return;
 }
 
 static void handle_supp_msgs(struct ipath_devdata *dd,
@@ -580,18 +607,19 @@ static int handle_errors(struct ipath_devdata *dd, ipath_err_t errs)
                 * ones on this particular interrupt, which also isn't great
                 */
                dd->ipath_maskederrs |= dd->ipath_lasterror | errs;
+
                dd->ipath_errormask &= ~dd->ipath_maskederrs;
                ipath_write_kreg(dd, dd->ipath_kregs->kr_errormask,
-                       dd->ipath_errormask);
+                                dd->ipath_errormask);
                s_iserr = ipath_decode_err(msg, sizeof msg,
-                       dd->ipath_maskederrs);
+                                          dd->ipath_maskederrs);
 
                if (dd->ipath_maskederrs &
-                       ~(INFINIPATH_E_RRCVEGRFULL |
-                       INFINIPATH_E_RRCVHDRFULL | INFINIPATH_E_PKTERRS))
+                   ~(INFINIPATH_E_RRCVEGRFULL |
+                     INFINIPATH_E_RRCVHDRFULL | INFINIPATH_E_PKTERRS))
                        ipath_dev_err(dd, "Temporarily disabling "
                            "error(s) %llx reporting; too frequent (%s)\n",
-                               (unsigned long long)dd->ipath_maskederrs,
+                               (unsigned long long) dd->ipath_maskederrs,
                                msg);
                else {
                        /*
@@ -683,9 +711,8 @@ static int handle_errors(struct ipath_devdata *dd, ipath_err_t errs)
                for (i = 0; i < dd->ipath_cfgports; i++) {
                        struct ipath_portdata *pd = dd->ipath_pd[i];
                        if (i == 0) {
-                               hd = dd->ipath_port0head;
-                               tl = (u32) le64_to_cpu(
-                                       *dd->ipath_hdrqtailptr);
+                               hd = pd->port_head;
+                               tl = ipath_get_hdrqtail(pd);
                        } else if (pd && pd->port_cnt &&
                                   pd->port_rcvhdrtail_kvaddr) {
                                /*
@@ -693,7 +720,7 @@ static int handle_errors(struct ipath_devdata *dd, ipath_err_t errs)
                                 * except kernel
                                 */
                                tl = *(u64 *) pd->port_rcvhdrtail_kvaddr;
-                               if (tl == dd->ipath_lastrcvhdrqtails[i])
+                               if (tl == pd->port_lastrcvhdrqtail)
                                        continue;
                                hd = ipath_read_ureg32(dd, ur_rcvhdrhead,
                                                       i);
@@ -703,7 +730,7 @@ static int handle_errors(struct ipath_devdata *dd, ipath_err_t errs)
                            (!hd && tl == dd->ipath_hdrqlast)) {
                                if (i == 0)
                                        chkerrpkts = 1;
-                               dd->ipath_lastrcvhdrqtails[i] = tl;
+                               pd->port_lastrcvhdrqtail = tl;
                                pd->port_hdrqfull++;
                                /* flush hdrqfull so that poll() sees it */
                                wmb();
@@ -712,6 +739,8 @@ static int handle_errors(struct ipath_devdata *dd, ipath_err_t errs)
                }
        }
        if (errs & INFINIPATH_E_RRCVEGRFULL) {
+               struct ipath_portdata *pd = dd->ipath_pd[0];
+
                /*
                 * since this is of less importance and not likely to
                 * happen without also getting hdrfull, only count
@@ -719,8 +748,7 @@ static int handle_errors(struct ipath_devdata *dd, ipath_err_t errs)
                 * vs user)
                 */
                ipath_stats.sps_etidfull++;
-               if (dd->ipath_port0head !=
-                   (u32) le64_to_cpu(*dd->ipath_hdrqtailptr))
+               if (pd->port_head != ipath_get_hdrqtail(pd))
                        chkerrpkts = 1;
        }
 
@@ -739,16 +767,13 @@ static int handle_errors(struct ipath_devdata *dd, ipath_err_t errs)
                dd->ipath_flags &= ~(IPATH_LINKUNK | IPATH_LINKINIT
                                     | IPATH_LINKARMED | IPATH_LINKACTIVE);
                *dd->ipath_statusp &= ~IPATH_STATUS_IB_READY;
-               if (!noprint) {
-                       u64 st = ipath_read_kreg64(
-                               dd, dd->ipath_kregs->kr_ibcstatus);
 
-                       ipath_dbg("Lost link, link now down (%s)\n",
-                                 ipath_ibcstatus_str[st & 0xf]);
-               }
+               ipath_dbg("Lost link, link now down (%s)\n",
+                       ipath_ibcstatus_str[ipath_read_kreg64(dd,
+                       dd->ipath_kregs->kr_ibcstatus) & 0xf]);
        }
        if (errs & INFINIPATH_E_IBSTATUSCHANGED)
-               handle_e_ibstatuschanged(dd, errs, noprint);
+               handle_e_ibstatuschanged(dd, errs);
 
        if (errs & INFINIPATH_E_RESET) {
                if (!noprint)
@@ -777,7 +802,6 @@ static int handle_errors(struct ipath_devdata *dd, ipath_err_t errs)
        return chkerrpkts;
 }
 
-
 /*
  * try to cleanup as much as possible for anything that might have gone
  * wrong while in freeze mode, such as pio buffers being written by user
@@ -794,7 +818,7 @@ static int handle_errors(struct ipath_devdata *dd, ipath_err_t errs)
 void ipath_clear_freeze(struct ipath_devdata *dd)
 {
        int i, im;
-       __le64 val;
+       u64 val;
 
        /* disable error interrupts, to avoid confusion */
        ipath_write_kreg(dd, dd->ipath_kregs->kr_errormask, 0ULL);
@@ -813,11 +837,7 @@ void ipath_clear_freeze(struct ipath_devdata *dd)
                         dd->ipath_control);
 
        /* ensure pio avail updates continue */
-       ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl,
-                dd->ipath_sendctrl & ~INFINIPATH_S_PIOBUFAVAILUPD);
-       ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch);
-       ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl,
-                dd->ipath_sendctrl);
+       ipath_force_pio_avail_update(dd);
 
        /*
         * We just enabled pioavailupdate, so dma copy is almost certainly
@@ -825,10 +845,13 @@ void ipath_clear_freeze(struct ipath_devdata *dd)
         */
        for (i = 0; i < dd->ipath_pioavregs; i++) {
                /* deal with 6110 chip bug */
-               im = i > 3 ? ((i&1) ? i-1 : i+1) : i;
-               val = ipath_read_kreg64(dd, (0x1000/sizeof(u64))+im);
-               dd->ipath_pioavailregs_dma[i] = dd->ipath_pioavailshadow[i]
-                       = le64_to_cpu(val);
+               im = (i > 3 && (dd->ipath_flags & IPATH_SWAP_PIOBUFS)) ?
+                       i ^ 1 : i;
+               val = ipath_read_kreg64(dd, (0x1000 / sizeof(u64)) + im);
+               dd->ipath_pioavailregs_dma[i] = cpu_to_le64(val);
+               dd->ipath_pioavailshadow[i] = val |
+                       (~dd->ipath_pioavailkernel[i] <<
+                       INFINIPATH_SENDPIOAVAIL_BUSY_SHIFT);
        }
 
        /*
@@ -849,7 +872,7 @@ void ipath_clear_freeze(struct ipath_devdata *dd)
 
 /* this is separate to allow for better optimization of ipath_intr() */
 
-static void ipath_bad_intr(struct ipath_devdata *dd, u32 * unexpectp)
+static noinline void ipath_bad_intr(struct ipath_devdata *dd, u32 *unexpectp)
 {
        /*
         * sometimes happen during driver init and unload, don't want
@@ -877,7 +900,7 @@ static void ipath_bad_intr(struct ipath_devdata *dd, u32 * unexpectp)
                                dd->ipath_f_free_irq(dd);
                        }
                }
-               if (ipath_read_kreg32(dd, dd->ipath_kregs->kr_intmask)) {
+               if (ipath_read_ireg(dd, dd->ipath_kregs->kr_intmask)) {
                        ipath_dev_err(dd, "%u unexpected interrupts, "
                                      "disabling interrupts completely\n",
                                      *unexpectp);
@@ -892,7 +915,7 @@ static void ipath_bad_intr(struct ipath_devdata *dd, u32 * unexpectp)
                          "ignoring\n");
 }
 
-static void ipath_bad_regread(struct ipath_devdata *dd)
+static noinline void ipath_bad_regread(struct ipath_devdata *dd)
 {
        static int allbits;
 
@@ -920,31 +943,9 @@ static void ipath_bad_regread(struct ipath_devdata *dd)
        }
 }
 
-static void handle_port_pioavail(struct ipath_devdata *dd)
-{
-       u32 i;
-       /*
-        * start from port 1, since for now port 0  is never using
-        * wait_event for PIO
-        */
-       for (i = 1; dd->ipath_portpiowait && i < dd->ipath_cfgports; i++) {
-               struct ipath_portdata *pd = dd->ipath_pd[i];
-
-               if (pd && pd->port_cnt &&
-                   dd->ipath_portpiowait & (1U << i)) {
-                       clear_bit(i, &dd->ipath_portpiowait);
-                       if (test_bit(IPATH_PORT_WAITING_PIO,
-                                    &pd->port_flag)) {
-                               clear_bit(IPATH_PORT_WAITING_PIO,
-                                         &pd->port_flag);
-                               wake_up_interruptible(&pd->port_wait);
-                       }
-               }
-       }
-}
-
 static void handle_layer_pioavail(struct ipath_devdata *dd)
 {
+       unsigned long flags;
        int ret;
 
        ret = ipath_ib_piobufavail(dd->verbs_dev);
@@ -953,9 +954,12 @@ static void handle_layer_pioavail(struct ipath_devdata *dd)
 
        return;
 set:
-       set_bit(IPATH_S_PIOINTBUFAVAIL, &dd->ipath_sendctrl);
+       spin_lock_irqsave(&dd->ipath_sendctrl_lock, flags);
+       dd->ipath_sendctrl |= INFINIPATH_S_PIOINTBUFAVAIL;
        ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl,
                         dd->ipath_sendctrl);
+       ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch);
+       spin_unlock_irqrestore(&dd->ipath_sendctrl_lock, flags);
 }
 
 /*
@@ -963,24 +967,33 @@ set:
  * process was waiting for a packet to arrive, and didn't want
  * to poll
  */
-static void handle_urcv(struct ipath_devdata *dd, u32 istat)
+static void handle_urcv(struct ipath_devdata *dd, u64 istat)
 {
        u64 portr;
        int i;
        int rcvdint = 0;
 
-       /* test_bit below needs this... */
+       /*
+        * test_and_clear_bit(IPATH_PORT_WAITING_RCV) and
+        * test_and_clear_bit(IPATH_PORT_WAITING_URG) below
+        * would both like timely updates of the bits so that
+        * we don't pass them by unnecessarily.  the rmb()
+        * here ensures that we see them promptly -- the
+        * corresponding wmb()'s are in ipath_poll_urgent()
+        * and ipath_poll_next()...
+        */
        rmb();
-       portr = ((istat >> INFINIPATH_I_RCVAVAIL_SHIFT) &
-                dd->ipath_i_rcvavail_mask)
-               | ((istat >> INFINIPATH_I_RCVURG_SHIFT) &
-                  dd->ipath_i_rcvurg_mask);
+       portr = ((istat >> dd->ipath_i_rcvavail_shift) &
+                dd->ipath_i_rcvavail_mask) |
+               ((istat >> dd->ipath_i_rcvurg_shift) &
+                dd->ipath_i_rcvurg_mask);
        for (i = 1; i < dd->ipath_cfgports; i++) {
                struct ipath_portdata *pd = dd->ipath_pd[i];
+
                if (portr & (1 << i) && pd && pd->port_cnt) {
                        if (test_and_clear_bit(IPATH_PORT_WAITING_RCV,
                                               &pd->port_flag)) {
-                               clear_bit(i + INFINIPATH_R_INTRAVAIL_SHIFT,
+                               clear_bit(i + dd->ipath_r_intravail_shift,
                                          &dd->ipath_rcvctrl);
                                wake_up_interruptible(&pd->port_wait);
                                rcvdint = 1;
@@ -993,7 +1006,7 @@ static void handle_urcv(struct ipath_devdata *dd, u32 istat)
        }
        if (rcvdint) {
                /* only want to take one interrupt, so turn off the rcv
-                * interrupt for all the ports that we did the wakeup on
+                * interrupt for all the ports that we set the rcv_waiting
                 * (but never for kernel port)
                 */
                ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl,
@@ -1008,8 +1021,7 @@ irqreturn_t ipath_intr(int irq, void *data)
        ipath_err_t estat = 0;
        irqreturn_t ret;
        static unsigned unexpected = 0;
-       static const u32 port0rbits = (1U<<INFINIPATH_I_RCVAVAIL_SHIFT) |
-                (1U<<INFINIPATH_I_RCVURG_SHIFT);
+       u64 kportrbits;
 
        ipath_stats.sps_ints++;
 
@@ -1039,7 +1051,7 @@ irqreturn_t ipath_intr(int irq, void *data)
                goto bail;
        }
 
-       istat = ipath_read_kreg32(dd, dd->ipath_kregs->kr_intstatus);
+       istat = ipath_read_ireg(dd, dd->ipath_kregs->kr_intstatus);
 
        if (unlikely(!istat)) {
                ipath_stats.sps_nullintr++;
@@ -1078,9 +1090,7 @@ irqreturn_t ipath_intr(int irq, void *data)
                        ipath_dev_err(dd, "Read of error status failed "
                                      "(all bits set); ignoring\n");
                else
-                       if (handle_errors(dd, estat))
-                               /* force calling ipath_kreceive() */
-                               chk0rcv = 1;
+                       chk0rcv |= handle_errors(dd, estat);
        }
 
        if (istat & INFINIPATH_I_GPIO) {
@@ -1098,8 +1108,7 @@ irqreturn_t ipath_intr(int irq, void *data)
 
                gpiostatus = ipath_read_kreg32(
                        dd, dd->ipath_kregs->kr_gpio_status);
-               /* First the error-counter case.
-                */
+               /* First the error-counter case. */
                if ((gpiostatus & IPATH_GPIO_ERRINTR_MASK) &&
                    (dd->ipath_flags & IPATH_GPIO_ERRINTRS)) {
                        /* want to clear the bits we see asserted. */
@@ -1161,7 +1170,6 @@ irqreturn_t ipath_intr(int irq, void *data)
                                        (u64) to_clear);
                }
        }
-       chk0rcv |= istat & port0rbits;
 
        /*
         * Clear the interrupt bits we found set, unless they are receive
@@ -1174,29 +1182,31 @@ irqreturn_t ipath_intr(int irq, void *data)
        ipath_write_kreg(dd, dd->ipath_kregs->kr_intclear, istat);
 
        /*
-        * handle port0 receive  before checking for pio buffers available,
-        * since receives can overflow; piobuf waiters can afford a few
-        * extra cycles, since they were waiting anyway, and user's waiting
-        * for receive are at the bottom.
+        * Handle kernel receive queues before checking for pio buffers
+        * available since receives can overflow; piobuf waiters can afford
+        * a few extra cycles, since they were waiting anyway, and user's
+        * waiting for receive are at the bottom.
         */
-       if (chk0rcv) {
-               ipath_kreceive(dd);
-               istat &= ~port0rbits;
+       kportrbits = (1ULL << dd->ipath_i_rcvavail_shift) |
+               (1ULL << dd->ipath_i_rcvurg_shift);
+       if (chk0rcv || (istat & kportrbits)) {
+               istat &= ~kportrbits;
+               ipath_kreceive(dd->ipath_pd[0]);
        }
 
-       if (istat & ((dd->ipath_i_rcvavail_mask <<
-                     INFINIPATH_I_RCVAVAIL_SHIFT)
-                    | (dd->ipath_i_rcvurg_mask <<
-                       INFINIPATH_I_RCVURG_SHIFT)))
+       if (istat & ((dd->ipath_i_rcvavail_mask << dd->ipath_i_rcvavail_shift) |
+                    (dd->ipath_i_rcvurg_mask << dd->ipath_i_rcvurg_shift)))
                handle_urcv(dd, istat);
 
        if (istat & INFINIPATH_I_SPIOBUFAVAIL) {
-               clear_bit(IPATH_S_PIOINTBUFAVAIL, &dd->ipath_sendctrl);
+               unsigned long flags;
+
+               spin_lock_irqsave(&dd->ipath_sendctrl_lock, flags);
+               dd->ipath_sendctrl &= ~INFINIPATH_S_PIOINTBUFAVAIL;
                ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl,
                                 dd->ipath_sendctrl);
-
-               if (dd->ipath_portpiowait)
-                       handle_port_pioavail(dd);
+               ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch);
+               spin_unlock_irqrestore(&dd->ipath_sendctrl_lock, flags);
 
                handle_layer_pioavail(dd);
        }