/* Transfer date to/from uC Program RAM of IB or PCIe SerDes */ static int ipath_sd7220_ram_xfer(struct ipath_devdata *dd, int sdnum, u32 loc, u8 *buf, int cnt, int rd_notwr) { u16 trans; u64 transval; u64 csbit; int owned; int tries; int sofar; int addr; int ret; unsigned long flags; const char *op; /* Pick appropriate transaction reg and "Chip select" for this serdes */ switch (sdnum) { case IB_7220_SERDES : csbit = 1ULL << EPB_IB_UC_CS_SHF; trans = dd->ipath_kregs->kr_ib_epbtrans; break; case PCIE_SERDES0 : case PCIE_SERDES1 : /* PCIe SERDES has uC "chip select" in different bit, too */ csbit = 1ULL << EPB_PCIE_UC_CS_SHF; trans = dd->ipath_kregs->kr_pcie_epbtrans; break; default : return -1; } op = rd_notwr ? "Rd" : "Wr"; spin_lock_irqsave(&dd->ipath_sdepb_lock, flags); owned = epb_access(dd, sdnum, 1); if (owned < 0) { spin_unlock_irqrestore(&dd->ipath_sdepb_lock, flags); ipath_dbg("Could not get %s access to %s EPB: %X, loc %X\n", op, (sdnum == IB_7220_SERDES) ? "IB" : "PCIe", owned, loc); return -1; } /* * In future code, we may need to distinguish several address ranges, * and select various memories based on this. For now, just trim * "loc" (location including address and memory select) to * "addr" (address within memory). we will only support PRAM * The memory is 8KB. */ addr = loc & 0x1FFF; for (tries = EPB_TRANS_TRIES; tries; --tries) { transval = ipath_read_kreg32(dd, trans); if (transval & EPB_TRANS_RDY) break; udelay(5); } sofar = 0; if (tries <= 0) ipath_dbg("No initial RDY on EPB access request\n"); else { /* * Every "memory" access is doubly-indirect. * We set two bytes of address, then read/write * one or mores bytes of data. */ /* First, we set control to "Read" or "Write" */ transval = csbit | EPB_UC_CTL | (rd_notwr ? EPB_ROM_R : EPB_ROM_W); tries = epb_trans(dd, trans, transval, &transval); if (tries <= 0) ipath_dbg("No EPB response to uC %s cmd\n", op); while (tries > 0 && sofar < cnt) { if (!sofar) { /* Only set address at start of chunk */ int addrbyte = (addr + sofar) >> 8; transval = csbit | EPB_MADDRH | addrbyte; tries = epb_trans(dd, trans, transval, &transval); if (tries <= 0) { ipath_dbg("No EPB response ADDRH\n"); break; } addrbyte = (addr + sofar) & 0xFF; transval = csbit | EPB_MADDRL | addrbyte; tries = epb_trans(dd, trans, transval, &transval); if (tries <= 0) { ipath_dbg("No EPB response ADDRL\n"); break; } } if (rd_notwr) transval = csbit | EPB_ROMDATA | EPB_RD; else transval = csbit | EPB_ROMDATA | buf[sofar]; tries = epb_trans(dd, trans, transval, &transval); if (tries <= 0) { ipath_dbg("No EPB response DATA\n"); break; } if (rd_notwr) buf[sofar] = transval & EPB_DATA_MASK; ++sofar; } /* Finally, clear control-bit for Read or Write */ transval = csbit | EPB_UC_CTL; tries = epb_trans(dd, trans, transval, &transval); if (tries <= 0) ipath_dbg("No EPB response to drop of uC %s cmd\n", op); }
/** * * ipath_sd7220_reg_mod - modify SERDES register * @dd: the infinipath device * @sdnum: which SERDES to access * @loc: location - channel, element, register, as packed by EPB_LOC() macro. * @wd: Write Data - value to set in register * @mask: ones where data should be spliced into reg. * * Basic register read/modify/write, with un-needed accesses elided. That is, * a mask of zero will prevent write, while a mask of 0xFF will prevent read. * returns current (presumed, if a write was done) contents of selected * register, or <0 if errors. */ static int ipath_sd7220_reg_mod(struct ipath_devdata *dd, int sdnum, u32 loc, u32 wd, u32 mask) { u16 trans; u64 transval; int owned; int tries, ret; unsigned long flags; switch (sdnum) { case IB_7220_SERDES : trans = dd->ipath_kregs->kr_ib_epbtrans; break; case PCIE_SERDES0 : case PCIE_SERDES1 : trans = dd->ipath_kregs->kr_pcie_epbtrans; break; default : return -1; } /* * All access is locked in software (vs other host threads) and * hardware (vs uC access). */ spin_lock_irqsave(&dd->ipath_sdepb_lock, flags); owned = epb_access(dd, sdnum, 1); if (owned < 0) { spin_unlock_irqrestore(&dd->ipath_sdepb_lock, flags); return -1; } ret = 0; for (tries = EPB_TRANS_TRIES; tries; --tries) { transval = ipath_read_kreg32(dd, trans); if (transval & EPB_TRANS_RDY) break; udelay(5); } if (tries > 0) { tries = 1; /* to make read-skip work */ if (mask != 0xFF) { /* * Not a pure write, so need to read. * loc encodes chip-select as well as address */ transval = loc | EPB_RD; tries = epb_trans(dd, trans, transval, &transval); } if (tries > 0 && mask != 0) { /* * Not a pure read, so need to write. */ wd = (wd & mask) | (transval & ~mask); transval = loc | (wd & EPB_DATA_MASK); tries = epb_trans(dd, trans, transval, &transval); } } /* else, failed to see ready, what error-handling? */ /* * Release bus. Failure is an error. */ if (epb_access(dd, sdnum, -1) < 0) ret = -1; else ret = transval & EPB_DATA_MASK; spin_unlock_irqrestore(&dd->ipath_sdepb_lock, flags); if (tries <= 0) ret = -1; return ret; }
/* * Localize the stuff that should be done to change IB uC reset * returns <0 for errors. */ static int ipath_ibsd_reset(struct ipath_devdata *dd, int assert_rst) { u64 rst_val; int ret = 0; unsigned long flags; rst_val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_ibserdesctrl); if (assert_rst) { /* * Vendor recommends "interrupting" uC before reset, to * minimize possible glitches. */ spin_lock_irqsave(&dd->ipath_sdepb_lock, flags); epb_access(dd, IB_7220_SERDES, 1); rst_val |= 1ULL; /* Squelch possible parity error from _asserting_ reset */ ipath_write_kreg(dd, dd->ipath_kregs->kr_hwerrmask, dd->ipath_hwerrmask & ~INFINIPATH_HWE_IB_UC_MEMORYPARITYERR); ipath_write_kreg(dd, dd->ipath_kregs->kr_ibserdesctrl, rst_val); /* flush write, delay to ensure it took effect */ ipath_read_kreg32(dd, dd->ipath_kregs->kr_scratch); udelay(2); /* once it's reset, can remove interrupt */ epb_access(dd, IB_7220_SERDES, -1); spin_unlock_irqrestore(&dd->ipath_sdepb_lock, flags); } else { /* * Before we de-assert reset, we need to deal with * possible glitch on the Parity-error line. * Suppress it around the reset, both in chip-level * hwerrmask and in IB uC control reg. uC will allow * it again during startup. */ u64 val; rst_val &= ~(1ULL); ipath_write_kreg(dd, dd->ipath_kregs->kr_hwerrmask, dd->ipath_hwerrmask & ~INFINIPATH_HWE_IB_UC_MEMORYPARITYERR); ret = ipath_resync_ibepb(dd); if (ret < 0) ipath_dev_err(dd, "unable to re-sync IB EPB\n"); /* set uC control regs to suppress parity errs */ ret = ipath_sd7220_reg_mod(dd, IB_7220_SERDES, IB_MPREG5, 1, 1); if (ret < 0) goto bail; /* IB uC code past Version 1.32.17 allow suppression of wdog */ ret = ipath_sd7220_reg_mod(dd, IB_7220_SERDES, IB_MPREG6, 0x80, 0x80); if (ret < 0) { ipath_dev_err(dd, "Failed to set WDOG disable\n"); goto bail; } ipath_write_kreg(dd, dd->ipath_kregs->kr_ibserdesctrl, rst_val); /* flush write, delay for startup */ ipath_read_kreg32(dd, dd->ipath_kregs->kr_scratch); udelay(1); /* clear, then re-enable parity errs */ ipath_sd7220_clr_ibpar(dd); val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_hwerrstatus); if (val & INFINIPATH_HWE_IB_UC_MEMORYPARITYERR) { ipath_dev_err(dd, "IBUC Parity still set after RST\n"); dd->ipath_hwerrmask &= ~INFINIPATH_HWE_IB_UC_MEMORYPARITYERR; } ipath_write_kreg(dd, dd->ipath_kregs->kr_hwerrmask, dd->ipath_hwerrmask); } bail: return ret; }
static int qib_sd7220_ram_xfer(struct qib_devdata *dd, int sdnum, u32 loc, u8 *buf, int cnt, int rd_notwr) { u16 trans; u64 transval; u64 csbit; int owned; int tries; int sofar; int addr; int ret; unsigned long flags; const char *op; switch (sdnum) { case IB_7220_SERDES: csbit = 1ULL << EPB_IB_UC_CS_SHF; trans = kr_ibsd_epb_transaction_reg; break; case PCIE_SERDES0: case PCIE_SERDES1: csbit = 1ULL << EPB_PCIE_UC_CS_SHF; trans = kr_pciesd_epb_transaction_reg; break; default: return -1; } op = rd_notwr ? "Rd" : "Wr"; spin_lock_irqsave(&dd->cspec->sdepb_lock, flags); owned = epb_access(dd, sdnum, 1); if (owned < 0) { spin_unlock_irqrestore(&dd->cspec->sdepb_lock, flags); return -1; } addr = loc & 0x1FFF; for (tries = EPB_TRANS_TRIES; tries; --tries) { transval = qib_read_kreg32(dd, trans); if (transval & EPB_TRANS_RDY) break; udelay(5); } sofar = 0; if (tries > 0) { transval = csbit | EPB_UC_CTL | (rd_notwr ? EPB_ROM_R : EPB_ROM_W); tries = epb_trans(dd, trans, transval, &transval); while (tries > 0 && sofar < cnt) { if (!sofar) { int addrbyte = (addr + sofar) >> 8; transval = csbit | EPB_MADDRH | addrbyte; tries = epb_trans(dd, trans, transval, &transval); if (tries <= 0) break; addrbyte = (addr + sofar) & 0xFF; transval = csbit | EPB_MADDRL | addrbyte; tries = epb_trans(dd, trans, transval, &transval); if (tries <= 0) break; } if (rd_notwr) transval = csbit | EPB_ROMDATA | EPB_RD; else transval = csbit | EPB_ROMDATA | buf[sofar]; tries = epb_trans(dd, trans, transval, &transval); if (tries <= 0) break; if (rd_notwr) buf[sofar] = transval & EPB_DATA_MASK; ++sofar; }
static int qib_sd7220_reg_mod(struct qib_devdata *dd, int sdnum, u32 loc, u32 wd, u32 mask) { u16 trans; u64 transval; int owned; int tries, ret; unsigned long flags; switch (sdnum) { case IB_7220_SERDES: trans = kr_ibsd_epb_transaction_reg; break; case PCIE_SERDES0: case PCIE_SERDES1: trans = kr_pciesd_epb_transaction_reg; break; default: return -1; } spin_lock_irqsave(&dd->cspec->sdepb_lock, flags); owned = epb_access(dd, sdnum, 1); if (owned < 0) { spin_unlock_irqrestore(&dd->cspec->sdepb_lock, flags); return -1; } ret = 0; for (tries = EPB_TRANS_TRIES; tries; --tries) { transval = qib_read_kreg32(dd, trans); if (transval & EPB_TRANS_RDY) break; udelay(5); } if (tries > 0) { tries = 1; if (mask != 0xFF) { transval = loc | EPB_RD; tries = epb_trans(dd, trans, transval, &transval); } if (tries > 0 && mask != 0) { wd = (wd & mask) | (transval & ~mask); transval = loc | (wd & EPB_DATA_MASK); tries = epb_trans(dd, trans, transval, &transval); } } if (epb_access(dd, sdnum, -1) < 0) ret = -1; else ret = transval & EPB_DATA_MASK; spin_unlock_irqrestore(&dd->cspec->sdepb_lock, flags); if (tries <= 0) ret = -1; return ret; }
static int qib_ibsd_reset(struct qib_devdata *dd, int assert_rst) { u64 rst_val; int ret = 0; unsigned long flags; rst_val = qib_read_kreg64(dd, kr_ibserdesctrl); if (assert_rst) { spin_lock_irqsave(&dd->cspec->sdepb_lock, flags); epb_access(dd, IB_7220_SERDES, 1); rst_val |= 1ULL; qib_write_kreg(dd, kr_hwerrmask, dd->cspec->hwerrmask & ~QLOGIC_IB_HWE_IB_UC_MEMORYPARITYERR); qib_write_kreg(dd, kr_ibserdesctrl, rst_val); qib_read_kreg32(dd, kr_scratch); udelay(2); epb_access(dd, IB_7220_SERDES, -1); spin_unlock_irqrestore(&dd->cspec->sdepb_lock, flags); } else { u64 val; rst_val &= ~(1ULL); qib_write_kreg(dd, kr_hwerrmask, dd->cspec->hwerrmask & ~QLOGIC_IB_HWE_IB_UC_MEMORYPARITYERR); ret = qib_resync_ibepb(dd); if (ret < 0) qib_dev_err(dd, "unable to re-sync IB EPB\n"); ret = qib_sd7220_reg_mod(dd, IB_7220_SERDES, IB_MPREG5, 1, 1); if (ret < 0) goto bail; ret = qib_sd7220_reg_mod(dd, IB_7220_SERDES, IB_MPREG6, 0x80, 0x80); if (ret < 0) { qib_dev_err(dd, "Failed to set WDOG disable\n"); goto bail; } qib_write_kreg(dd, kr_ibserdesctrl, rst_val); qib_read_kreg32(dd, kr_scratch); udelay(1); qib_sd7220_clr_ibpar(dd); val = qib_read_kreg64(dd, kr_hwerrstatus); if (val & QLOGIC_IB_HWE_IB_UC_MEMORYPARITYERR) { qib_dev_err(dd, "IBUC Parity still set after RST\n"); dd->cspec->hwerrmask &= ~QLOGIC_IB_HWE_IB_UC_MEMORYPARITYERR; } qib_write_kreg(dd, kr_hwerrmask, dd->cspec->hwerrmask); } bail: return ret; }