/*
- * Copyright (c) 2006, 2007 QLogic Corporation. All rights reserved.
+ * Copyright (c) 2006, 2007, 2008 QLogic Corporation. All rights reserved.
* Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved.
*
* This software is available to you under a choice of one of two
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/delay.h>
-
+#include <rdma/ib_verbs.h>
#include "ipath_kernel.h"
#include "ipath_registers.h"
.cr_ibsymbolerrcnt = IPATH_CREG_OFFSET(IBSymbolErrCnt)
};
+/* kr_control bits */
+#define INFINIPATH_C_RESET 1U
+
/* kr_intstatus, kr_intclear, kr_intmask bits */
#define INFINIPATH_I_RCVURG_MASK ((1U<<5)-1)
+#define INFINIPATH_I_RCVURG_SHIFT 0
#define INFINIPATH_I_RCVAVAIL_MASK ((1U<<5)-1)
+#define INFINIPATH_I_RCVAVAIL_SHIFT 12
/* kr_hwerrclear, kr_hwerrmask, kr_hwerrstatus, bits */
#define INFINIPATH_HWE_PCIEMEMPARITYERR_MASK 0x000000000000003fULL
#define INFINIPATH_EXTS_MEMBIST_ENDTEST 0x0000000000004000
#define INFINIPATH_EXTS_MEMBIST_FOUND 0x0000000000008000
+/* kr_xgxsconfig bits */
+#define INFINIPATH_XGXS_RESET 0x5ULL
+
#define _IPATH_GPIO_SDA_NUM 1
#define _IPATH_GPIO_SCL_NUM 0
#define IPATH_GPIO_SCL (1ULL << \
(_IPATH_GPIO_SCL_NUM+INFINIPATH_EXTC_GPIOOE_SHIFT))
+#define INFINIPATH_RT_BUFSIZE_MASK 0xe0000000ULL
+#define INFINIPATH_RT_BUFSIZE_SHIFTVAL(tid) \
+ ((((tid) & INFINIPATH_RT_BUFSIZE_MASK) >> 29) + 11 - 1)
+#define INFINIPATH_RT_BUFSIZE(tid) (1 << INFINIPATH_RT_BUFSIZE_SHIFTVAL(tid))
+#define INFINIPATH_RT_IS_VALID(tid) \
+ (((tid) & INFINIPATH_RT_BUFSIZE_MASK) && \
+ ((((tid) & INFINIPATH_RT_BUFSIZE_MASK) != INFINIPATH_RT_BUFSIZE_MASK)))
+#define INFINIPATH_RT_ADDR_MASK 0x1FFFFFFFULL /* 29 bits valid */
+#define INFINIPATH_RT_ADDR_SHIFT 10
+
#define INFINIPATH_R_INTRAVAIL_SHIFT 16
#define INFINIPATH_R_TAILUPD_SHIFT 31
#define TXE_PIO_PARITY ((INFINIPATH_HWE_TXEMEMPARITYERR_PIOBUF | \
INFINIPATH_HWE_TXEMEMPARITYERR_PIOPBC) \
<< INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT)
+#define RXE_EAGER_PARITY (INFINIPATH_HWE_RXEMEMPARITYERR_EAGERTID \
+ << INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT)
static void ipath_pe_put_tid_2(struct ipath_devdata *, u64 __iomem *,
u32, unsigned long);
* make sure we get this much out, unless told to be quiet,
* or it's occurred within the last 5 seconds
*/
- if ((hwerrs & ~(dd->ipath_lasthwerror |
- ((INFINIPATH_HWE_TXEMEMPARITYERR_PIOBUF |
- INFINIPATH_HWE_TXEMEMPARITYERR_PIOPBC)
- << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT))) ||
+ if ((hwerrs & ~(dd->ipath_lasthwerror | TXE_PIO_PARITY |
+ RXE_EAGER_PARITY)) ||
(ipath_debug & __IPATH_VERBDBG))
dev_info(&dd->pcidev->dev, "Hardware error: hwerr=0x%llx "
"(cleared)\n", (unsigned long long) hwerrs);
(hwerrs & ~dd->ipath_hwe_bitsextant));
ctrl = ipath_read_kreg32(dd, dd->ipath_kregs->kr_control);
- if (ctrl & INFINIPATH_C_FREEZEMODE) {
+ if ((ctrl & INFINIPATH_C_FREEZEMODE) && !ipath_diag_inuse) {
/*
* parity errors in send memory are recoverable,
* just cancel the send (if indicated in * sendbuffererror),
dd->ipath_hwerrmask);
}
- if (*msg)
+ if (hwerrs) {
+ /*
+ * if any set that we aren't ignoring; only
+ * make the complaint once, in case it's stuck
+ * or recurring, and we get here multiple
+ * times.
+ */
ipath_dev_err(dd, "%s hardware error\n", msg);
- if (isfatal && !ipath_diag_inuse && dd->ipath_freezemsg) {
+ if (dd->ipath_flags & IPATH_INITTED) {
+ ipath_set_linkstate(dd, IPATH_IB_LINKDOWN);
+ ipath_setup_pe_setextled(dd,
+ INFINIPATH_IBCS_L_STATE_DOWN,
+ INFINIPATH_IBCS_LT_STATE_DISABLED);
+ ipath_dev_err(dd, "Fatal Hardware Error (freeze "
+ "mode), no longer usable, SN %.16s\n",
+ dd->ipath_serial);
+ isfatal = 1;
+ }
+ *dd->ipath_statusp &= ~IPATH_STATUS_IB_READY;
+ /* mark as having had error */
+ *dd->ipath_statusp |= IPATH_STATUS_HWERROR;
/*
- * for /sys status file ; if no trailing } is copied, we'll
- * know it was truncated.
+ * mark as not usable, at a minimum until driver
+ * is reloaded, probably until reboot, since no
+ * other reset is possible.
+ */
+ dd->ipath_flags &= ~IPATH_INITTED;
+ } else
+ *msg = 0; /* recovered from all of them */
+
+ if (isfatal && !ipath_diag_inuse && dd->ipath_freezemsg && msg) {
+ /*
+ * for /sys status file ; if no trailing brace is copied,
+ * we'll know it was truncated.
*/
snprintf(dd->ipath_freezemsg, dd->ipath_freezelen,
"{%s}", msg);
dd->ipath_f_put_tid = ipath_pe_put_tid_2;
}
-
/*
* set here, not in ipath_init_*_funcs because we have to do
* it after we can read chip registers.
INFINIPATH_HWE_SERDESPLLFAILED);
}
+ dd->ibdeltainprog = 1;
+ dd->ibsymsnap =
+ ipath_read_creg32(dd, dd->ipath_cregs->cr_ibsymbolerrcnt);
+ dd->iblnkerrsnap =
+ ipath_read_creg32(dd, dd->ipath_cregs->cr_iblinkerrrecovcnt);
+
val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_serdesconfig0);
config1 = ipath_read_kreg64(dd, dd->ipath_kregs->kr_serdesconfig1);
{
u64 val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_serdesconfig0);
+ if (dd->ibsymdelta || dd->iblnkerrdelta ||
+ dd->ibdeltainprog) {
+ u64 diagc;
+ /* enable counter writes */
+ diagc = ipath_read_kreg64(dd, dd->ipath_kregs->kr_hwdiagctrl);
+ ipath_write_kreg(dd, dd->ipath_kregs->kr_hwdiagctrl,
+ diagc | INFINIPATH_DC_COUNTERWREN);
+
+ if (dd->ibsymdelta || dd->ibdeltainprog) {
+ val = ipath_read_creg32(dd,
+ dd->ipath_cregs->cr_ibsymbolerrcnt);
+ if (dd->ibdeltainprog)
+ val -= val - dd->ibsymsnap;
+ val -= dd->ibsymdelta;
+ ipath_write_creg(dd,
+ dd->ipath_cregs->cr_ibsymbolerrcnt, val);
+ }
+ if (dd->iblnkerrdelta || dd->ibdeltainprog) {
+ val = ipath_read_creg32(dd,
+ dd->ipath_cregs->cr_iblinkerrrecovcnt);
+ if (dd->ibdeltainprog)
+ val -= val - dd->iblnkerrsnap;
+ val -= dd->iblnkerrdelta;
+ ipath_write_creg(dd,
+ dd->ipath_cregs->cr_iblinkerrrecovcnt, val);
+ }
+
+ /* and disable counter writes */
+ ipath_write_kreg(dd, dd->ipath_kregs->kr_hwdiagctrl, diagc);
+ }
val |= INFINIPATH_SERDC0_TXIDLE;
ipath_dbg("Setting TxIdleEn on serdes (config0 = %llx)\n",
(unsigned long long) val);
extctl = dd->ipath_extctrl & ~(INFINIPATH_EXTC_LED1PRIPORT_ON |
INFINIPATH_EXTC_LED2PRIPORT_ON);
- if (ltst & INFINIPATH_IBCS_LT_STATE_LINKUP)
+ if (ltst == INFINIPATH_IBCS_LT_STATE_LINKUP)
extctl |= INFINIPATH_EXTC_LED2PRIPORT_ON;
if (lst == INFINIPATH_IBCS_L_STATE_ACTIVE)
extctl |= INFINIPATH_EXTC_LED1PRIPORT_ON;
pci_disable_msi(dd->pcidev);
}
+static void ipath_6120_pcie_params(struct ipath_devdata *dd)
+{
+ u16 linkstat, speed;
+ int pos;
+
+ pos = pci_find_capability(dd->pcidev, PCI_CAP_ID_EXP);
+ if (!pos) {
+ ipath_dev_err(dd, "Can't find PCI Express capability!\n");
+ goto bail;
+ }
+
+ pci_read_config_word(dd->pcidev, pos + PCI_EXP_LNKSTA,
+ &linkstat);
+ /*
+ * speed is bits 0-4, linkwidth is bits 4-8
+ * no defines for them in headers
+ */
+ speed = linkstat & 0xf;
+ linkstat >>= 4;
+ linkstat &= 0x1f;
+ dd->ipath_lbus_width = linkstat;
+
+ switch (speed) {
+ case 1:
+ dd->ipath_lbus_speed = 2500; /* Gen1, 2.5GHz */
+ break;
+ case 2:
+ dd->ipath_lbus_speed = 5000; /* Gen1, 5GHz */
+ break;
+ default: /* not defined, assume gen1 */
+ dd->ipath_lbus_speed = 2500;
+ break;
+ }
+
+ if (linkstat < 8)
+ ipath_dev_err(dd,
+ "PCIe width %u (x8 HCA), performance reduced\n",
+ linkstat);
+ else
+ ipath_cdbg(VERBOSE, "PCIe speed %u width %u (x8 HCA)\n",
+ dd->ipath_lbus_speed, linkstat);
+
+ if (speed != 1)
+ ipath_dev_err(dd,
+ "PCIe linkspeed %u is incorrect; "
+ "should be 1 (2500)!\n", speed);
+bail:
+ /* fill in string, even on errors */
+ snprintf(dd->ipath_lbus_info, sizeof(dd->ipath_lbus_info),
+ "PCIe,%uMHz,x%u\n",
+ dd->ipath_lbus_speed,
+ dd->ipath_lbus_width);
+
+ return;
+}
+
/**
* ipath_setup_pe_config - setup PCIe config related stuff
* @dd: the infinipath device
} else
ipath_dev_err(dd, "Can't find MSI capability, "
"can't save MSI settings for reset\n");
- if ((pos = pci_find_capability(dd->pcidev, PCI_CAP_ID_EXP))) {
- u16 linkstat;
- pci_read_config_word(dd->pcidev, pos + PCI_EXP_LNKSTA,
- &linkstat);
- linkstat >>= 4;
- linkstat &= 0x1f;
- if (linkstat != 8)
- ipath_dev_err(dd, "PCIe width %u, "
- "performance reduced\n", linkstat);
- }
- else
- ipath_dev_err(dd, "Can't find PCI Express "
- "capability!\n");
+
+ ipath_6120_pcie_params(dd);
dd->ipath_link_width_supported = IB_WIDTH_1X | IB_WIDTH_4X;
dd->ipath_link_speed_supported = IPATH_IB_SDR;
INFINIPATH_HWE_RXEMEMPARITYERR_MASK <<
INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT;
- dd->ipath_eep_st_masks[2].errs_to_log =
- INFINIPATH_E_INVALIDADDR | INFINIPATH_E_RESET;
-
-
+ dd->ipath_eep_st_masks[2].errs_to_log = INFINIPATH_E_RESET;
dd->delay_mult = 2; /* SDR, 4X, can't change */
}
u64 val;
int i;
int ret;
+ u16 cmdval;
+
+ pci_read_config_word(dd->pcidev, PCI_COMMAND, &cmdval);
/* Use ERROR so it shows up in logs, etc. */
ipath_dev_err(dd, "Resetting InfiniPath unit %u\n", dd->ipath_unit);
ipath_dev_err(dd, "rewrite of BAR1 failed: %d\n",
r);
/* now re-enable memory access */
+ pci_write_config_word(dd->pcidev, PCI_COMMAND, cmdval);
if ((r = pci_enable_device(dd->pcidev)))
ipath_dev_err(dd, "pci_enable_device failed after "
"reset: %d\n", r);
- /* whether it worked or not, mark as present, again */
+ /*
+ * whether it fully enabled or not, mark as present,
+ * again (but not INITTED)
+ */
dd->ipath_flags |= IPATH_PRESENT;
val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_revision);
if (val == dd->ipath_revision) {
ret = 0; /* failed */
bail:
+ if (ret)
+ ipath_6120_pcie_params(dd);
return ret;
}
{
u32 __iomem *tidp32 = (u32 __iomem *)tidptr;
unsigned long flags = 0; /* keep gcc quiet */
+ int tidx;
+ spinlock_t *tidlockp;
+
+ if (!dd->ipath_kregbase)
+ return;
if (pa != dd->ipath_tidinvalid) {
if (pa & ((1U << 11) - 1)) {
dev_info(&dd->pcidev->dev, "BUG: physaddr %lx "
- "not 4KB aligned!\n", pa);
+ "not 2KB aligned!\n", pa);
return;
}
pa >>= 11;
/* paranoia check */
- if (pa & (7<<29))
+ if (pa & ~INFINIPATH_RT_ADDR_MASK)
ipath_dev_err(dd,
"BUG: Physical page address 0x%lx "
"has bits set in 31-29\n", pa);
* call can be done from interrupt level for the port 0 eager TIDs,
* so we have to use irqsave locks.
*/
- spin_lock_irqsave(&dd->ipath_tid_lock, flags);
+ /*
+ * Assumes tidptr always > ipath_egrtidbase
+ * if type == RCVHQ_RCV_TYPE_EAGER.
+ */
+ tidx = tidptr - dd->ipath_egrtidbase;
+
+ tidlockp = (type == RCVHQ_RCV_TYPE_EAGER && tidx < dd->ipath_rcvegrcnt)
+ ? &dd->ipath_kernel_tid_lock : &dd->ipath_user_tid_lock;
+ spin_lock_irqsave(tidlockp, flags);
ipath_write_kreg(dd, dd->ipath_kregs->kr_scratch, 0xfeeddeaf);
- if (dd->ipath_kregbase)
- writel(pa, tidp32);
+ writel(pa, tidp32);
ipath_write_kreg(dd, dd->ipath_kregs->kr_scratch, 0xdeadbeef);
mmiowb();
- spin_unlock_irqrestore(&dd->ipath_tid_lock, flags);
+ spin_unlock_irqrestore(tidlockp, flags);
}
+
/**
* ipath_pe_put_tid_2 - write a TID in chip, Revision 2 or higher
* @dd: the infinipath device
u32 type, unsigned long pa)
{
u32 __iomem *tidp32 = (u32 __iomem *)tidptr;
+ u32 tidx;
+
+ if (!dd->ipath_kregbase)
+ return;
if (pa != dd->ipath_tidinvalid) {
if (pa & ((1U << 11) - 1)) {
}
pa >>= 11;
/* paranoia check */
- if (pa & (7<<29))
+ if (pa & ~INFINIPATH_RT_ADDR_MASK)
ipath_dev_err(dd,
"BUG: Physical page address 0x%lx "
"has bits set in 31-29\n", pa);
else /* for now, always full 4KB page */
pa |= 2 << 29;
}
- if (dd->ipath_kregbase)
- writel(pa, tidp32);
+ tidx = tidptr - dd->ipath_egrtidbase;
+ writel(pa, tidp32);
mmiowb();
}
dd->ipath_egrtidbase = (u64 __iomem *)
((char __iomem *) dd->ipath_kregbase + dd->ipath_rcvegrbase);
- /*
- * To truly support a 4KB MTU (for usermode), we need to
- * bump this to a larger value. For now, we use them for
- * the kernel only.
- */
- dd->ipath_rcvegrbufsize = 2048;
+ dd->ipath_rcvegrbufsize = ipath_mtu4096 ? 4096 : 2048;
/*
* the min() check here is currently a nop, but it may not always
* be, depending on just how we do ipath_rcvegrbufsize
*/
- dd->ipath_ibmaxlen = min(dd->ipath_piosize2k,
+ dd->ipath_ibmaxlen = min(ipath_mtu4096 ? dd->ipath_piosize4k :
+ dd->ipath_piosize2k,
dd->ipath_rcvegrbufsize +
(dd->ipath_rcvhdrentsize << 2));
dd->ipath_init_ibmaxlen = dd->ipath_ibmaxlen;
static int ipath_pe_ib_updown(struct ipath_devdata *dd, int ibup, u64 ibcs)
{
+ if (ibup) {
+ if (dd->ibdeltainprog) {
+ dd->ibdeltainprog = 0;
+ dd->ibsymdelta +=
+ ipath_read_creg32(dd,
+ dd->ipath_cregs->cr_ibsymbolerrcnt) -
+ dd->ibsymsnap;
+ dd->iblnkerrdelta +=
+ ipath_read_creg32(dd,
+ dd->ipath_cregs->cr_iblinkerrrecovcnt) -
+ dd->iblnkerrsnap;
+ }
+ } else {
+ dd->ipath_lli_counter = 0;
+ if (!dd->ibdeltainprog) {
+ dd->ibdeltainprog = 1;
+ dd->ibsymsnap =
+ ipath_read_creg32(dd,
+ dd->ipath_cregs->cr_ibsymbolerrcnt);
+ dd->iblnkerrsnap =
+ ipath_read_creg32(dd,
+ dd->ipath_cregs->cr_iblinkerrrecovcnt);
+ }
+ }
+
ipath_setup_pe_setextled(dd, ipath_ib_linkstate(dd, ibcs),
ipath_ib_linktrstate(dd, ibcs));
return 0;