IB/qib: Don't mark VL15 bufs as WC to avoid a rare 7322 chip problem
authorDave Olson <dave.olson@qlogic.com>
Thu, 17 Jun 2010 23:13:44 +0000 (23:13 +0000)
committerRoland Dreier <rolandd@cisco.com>
Tue, 6 Jul 2010 21:13:20 +0000 (14:13 -0700)
Don't set write combining via PAT on the VL15 buffers to avoid a rare
problem with unaligned writes from interrupt-flushed store buffers.

Signed-off-by: Dave Olson <dave.olson@qlogic.com>
Signed-off-by: Roland Dreier <rolandd@cisco.com>
drivers/infiniband/hw/qib/qib.h
drivers/infiniband/hw/qib/qib_diag.c
drivers/infiniband/hw/qib/qib_iba7322.c
drivers/infiniband/hw/qib/qib_init.c
drivers/infiniband/hw/qib/qib_pcie.c
drivers/infiniband/hw/qib/qib_tx.c

index 32d9208efcff343b94e5cdb2882fbdf7ae4a8def..3593983df7bafc657c26f19635bf50edf49c4cd9 100644 (file)
@@ -686,6 +686,7 @@ struct qib_devdata {
        void __iomem *piobase;
        /* mem-mapped pointer to base of user chip regs (if using WC PAT) */
        u64 __iomem *userbase;
+       void __iomem *piovl15base; /* base of VL15 buffers, if not WC */
        /*
         * points to area where PIOavail registers will be DMA'ed.
         * Has to be on a page of it's own, because the page will be
index ca98dd52375241d2236ae2e48d5127614c00ce97..05dcf0d9a7d31a253267010e51d4a3e20fb02edc 100644 (file)
@@ -233,6 +233,7 @@ static u32 __iomem *qib_remap_ioaddr32(struct qib_devdata *dd, u32 offset,
        u32 __iomem *krb32 = (u32 __iomem *)dd->kregbase;
        u32 __iomem *map = NULL;
        u32 cnt = 0;
+       u32 tot4k, offs4k;
 
        /* First, simplest case, offset is within the first map. */
        kreglen = (dd->kregend - dd->kregbase) * sizeof(u64);
@@ -250,7 +251,8 @@ static u32 __iomem *qib_remap_ioaddr32(struct qib_devdata *dd, u32 offset,
        if (dd->userbase) {
                /* If user regs mapped, they are after send, so set limit. */
                u32 ulim = (dd->cfgctxts * dd->ureg_align) + dd->uregbase;
-               snd_lim = dd->uregbase;
+               if (!dd->piovl15base)
+                       snd_lim = dd->uregbase;
                krb32 = (u32 __iomem *)dd->userbase;
                if (offset >= dd->uregbase && offset < ulim) {
                        map = krb32 + (offset - dd->uregbase) / sizeof(u32);
@@ -277,14 +279,14 @@ static u32 __iomem *qib_remap_ioaddr32(struct qib_devdata *dd, u32 offset,
        /* If 4k buffers exist, account for them by bumping
         * appropriate limit.
         */
+       tot4k = dd->piobcnt4k * dd->align4k;
+       offs4k = dd->piobufbase >> 32;
        if (dd->piobcnt4k) {
-               u32 tot4k = dd->piobcnt4k * dd->align4k;
-               u32 offs4k = dd->piobufbase >> 32;
                if (snd_bottom > offs4k)
                        snd_bottom = offs4k;
                else {
                        /* 4k above 2k. Bump snd_lim, if needed*/
-                       if (!dd->userbase)
+                       if (!dd->userbase || dd->piovl15base)
                                snd_lim = offs4k + tot4k;
                }
        }
@@ -298,6 +300,15 @@ static u32 __iomem *qib_remap_ioaddr32(struct qib_devdata *dd, u32 offset,
                cnt = snd_lim - offset;
        }
 
+       if (!map && offs4k && dd->piovl15base) {
+               snd_lim = offs4k + tot4k + 2 * dd->align4k;
+               if (offset >= (offs4k + tot4k) && offset < snd_lim) {
+                       map = (u32 __iomem *)dd->piovl15base +
+                               ((offset - (offs4k + tot4k)) / sizeof(u32));
+                       cnt = snd_lim - offset;
+               }
+       }
+
 mapped:
        if (cntp)
                *cntp = cnt;
index 503992d9c5ce578378ccf32b8997130ba3b6c8cd..3e9828be5010af8661356f0ce4671486cba18c68 100644 (file)
@@ -6119,9 +6119,25 @@ static int qib_init_7322_variables(struct qib_devdata *dd)
        qib_set_ctxtcnt(dd);
 
        if (qib_wc_pat) {
-               ret = init_chip_wc_pat(dd, NUM_VL15_BUFS * dd->align4k);
+               resource_size_t vl15off;
+               /*
+                * We do not set WC on the VL15 buffers to avoid
+                * a rare problem with unaligned writes from
+                * interrupt-flushed store buffers, so we need
+                * to map those separately here.  We can't solve
+                * this for the rarely used mtrr case.
+                */
+               ret = init_chip_wc_pat(dd, 0);
                if (ret)
                        goto bail;
+
+               /* vl15 buffers start just after the 4k buffers */
+               vl15off = dd->physaddr + (dd->piobufbase >> 32) +
+                       dd->piobcnt4k * dd->align4k;
+               dd->piovl15base = ioremap_nocache(vl15off,
+                                                 NUM_VL15_BUFS * dd->align4k);
+               if (!dd->piovl15base)
+                       goto bail;
        }
        qib_7322_set_baseaddrs(dd); /* set chip access pointers now */
 
index 9b40f345ac3f201dee3f6e9d9a2017e8cc58dc86..25895991dc527922e4210ff6d7629f88bb6789b9 100644 (file)
@@ -1499,6 +1499,12 @@ bail:
        return -ENOMEM;
 }
 
+/*
+ * Note: Changes to this routine should be mirrored
+ * for the diagnostics routine qib_remap_ioaddr32().
+ * There is also related code for VL15 buffers in qib_init_7322_variables().
+ * The teardown code that unmaps is in qib_pcie_ddcleanup()
+ */
 int init_chip_wc_pat(struct qib_devdata *dd, u32 vl15buflen)
 {
        u64 __iomem *qib_kregbase = NULL;
index c926bf4541df3fe0a5d8a66e66305411403ccced..7fa6e5592630247b7208f22205e980c574c3af83 100644 (file)
@@ -179,6 +179,8 @@ void qib_pcie_ddcleanup(struct qib_devdata *dd)
                iounmap(dd->piobase);
        if (dd->userbase)
                iounmap(dd->userbase);
+       if (dd->piovl15base)
+               iounmap(dd->piovl15base);
 
        pci_disable_device(dd->pcidev);
        pci_release_regions(dd->pcidev);
index f7eb1ddff5f3da92490b96569b6c2a1b5c4400ad..af30232b68311e73144c609fa556c4e6e64e3059 100644 (file)
@@ -340,9 +340,13 @@ rescan:
                if (i < dd->piobcnt2k)
                        buf = (u32 __iomem *)(dd->pio2kbase +
                                i * dd->palign);
-               else
+               else if (i < dd->piobcnt2k + dd->piobcnt4k || !dd->piovl15base)
                        buf = (u32 __iomem *)(dd->pio4kbase +
                                (i - dd->piobcnt2k) * dd->align4k);
+               else
+                       buf = (u32 __iomem *)(dd->piovl15base +
+                               (i - (dd->piobcnt2k + dd->piobcnt4k)) *
+                               dd->align4k);
                if (pbufnum)
                        *pbufnum = i;
                dd->upd_pio_shadow = 0;