/drivers/infiniband/hw/ipath/ipath_sd7220.c
C | 1462 lines | 1003 code | 132 blank | 327 comment | 167 complexity | 6e745b86c6f1373b808279428084a52e MD5 | raw file
Possible License(s): CC-BY-SA-3.0, GPL-2.0, LGPL-2.0, AGPL-1.0
- /*
- * 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
- * licenses. You may choose to be licensed under the terms of the GNU
- * General Public License (GPL) Version 2, available from the file
- * COPYING in the main directory of this source tree, or the
- * OpenIB.org BSD license below:
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above
- * copyright notice, this list of conditions and the following
- * disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials
- * provided with the distribution.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
- /*
- * This file contains all of the code that is specific to the SerDes
- * on the InfiniPath 7220 chip.
- */
- #include <linux/pci.h>
- #include <linux/delay.h>
- #include "ipath_kernel.h"
- #include "ipath_registers.h"
- #include "ipath_7220.h"
- /*
- * The IBSerDesMappTable is a memory that holds values to be stored in
- * various SerDes registers by IBC. It is not part of the normal kregs
- * map and is used in exactly one place, hence the #define below.
- */
- #define KR_IBSerDesMappTable (0x94000 / (sizeof(uint64_t)))
- /*
- * Below used for sdnum parameter, selecting one of the two sections
- * used for PCIe, or the single SerDes used for IB.
- */
- #define PCIE_SERDES0 0
- #define PCIE_SERDES1 1
- /*
- * The EPB requires addressing in a particular form. EPB_LOC() is intended
- * to make #definitions a little more readable.
- */
- #define EPB_ADDR_SHF 8
- #define EPB_LOC(chn, elt, reg) \
- (((elt & 0xf) | ((chn & 7) << 4) | ((reg & 0x3f) << 9)) << \
- EPB_ADDR_SHF)
- #define EPB_IB_QUAD0_CS_SHF (25)
- #define EPB_IB_QUAD0_CS (1U << EPB_IB_QUAD0_CS_SHF)
- #define EPB_IB_UC_CS_SHF (26)
- #define EPB_PCIE_UC_CS_SHF (27)
- #define EPB_GLOBAL_WR (1U << (EPB_ADDR_SHF + 8))
- /* Forward declarations. */
- static int ipath_sd7220_reg_mod(struct ipath_devdata *dd, int sdnum, u32 loc,
- u32 data, u32 mask);
- static int ibsd_mod_allchnls(struct ipath_devdata *dd, int loc, int val,
- int mask);
- static int ipath_sd_trimdone_poll(struct ipath_devdata *dd);
- static void ipath_sd_trimdone_monitor(struct ipath_devdata *dd,
- const char *where);
- static int ipath_sd_setvals(struct ipath_devdata *dd);
- static int ipath_sd_early(struct ipath_devdata *dd);
- static int ipath_sd_dactrim(struct ipath_devdata *dd);
- /* Set the registers that IBC may muck with to their default "preset" values */
- int ipath_sd7220_presets(struct ipath_devdata *dd);
- static int ipath_internal_presets(struct ipath_devdata *dd);
- /* Tweak the register (CMUCTRL5) that contains the TRIMSELF controls */
- static int ipath_sd_trimself(struct ipath_devdata *dd, int val);
- static int epb_access(struct ipath_devdata *dd, int sdnum, int claim);
- void ipath_set_relock_poll(struct ipath_devdata *dd, int ibup);
- /*
- * Below keeps track of whether the "once per power-on" initialization has
- * been done, because uC code Version 1.32.17 or higher allows the uC to
- * be reset at will, and Automatic Equalization may require it. So the
- * state of the reset "pin", as reflected in was_reset parameter to
- * ipath_sd7220_init() is no longer valid. Instead, we check for the
- * actual uC code having been loaded.
- */
- static int ipath_ibsd_ucode_loaded(struct ipath_devdata *dd)
- {
- if (!dd->serdes_first_init_done && (ipath_sd7220_ib_vfy(dd) > 0))
- dd->serdes_first_init_done = 1;
- return dd->serdes_first_init_done;
- }
- /* repeat #define for local use. "Real" #define is in ipath_iba7220.c */
- #define INFINIPATH_HWE_IB_UC_MEMORYPARITYERR 0x0000004000000000ULL
- #define IB_MPREG5 (EPB_LOC(6, 0, 0xE) | (1L << EPB_IB_UC_CS_SHF))
- #define IB_MPREG6 (EPB_LOC(6, 0, 0xF) | (1U << EPB_IB_UC_CS_SHF))
- #define UC_PAR_CLR_D 8
- #define UC_PAR_CLR_M 0xC
- #define IB_CTRL2(chn) (EPB_LOC(chn, 7, 3) | EPB_IB_QUAD0_CS)
- #define START_EQ1(chan) EPB_LOC(chan, 7, 0x27)
- void ipath_sd7220_clr_ibpar(struct ipath_devdata *dd)
- {
- int ret;
- /* clear, then re-enable parity errs */
- ret = ipath_sd7220_reg_mod(dd, IB_7220_SERDES, IB_MPREG6,
- UC_PAR_CLR_D, UC_PAR_CLR_M);
- if (ret < 0) {
- ipath_dev_err(dd, "Failed clearing IBSerDes Parity err\n");
- goto bail;
- }
- ret = ipath_sd7220_reg_mod(dd, IB_7220_SERDES, IB_MPREG6, 0,
- UC_PAR_CLR_M);
- ipath_read_kreg32(dd, dd->ipath_kregs->kr_scratch);
- udelay(4);
- ipath_write_kreg(dd, dd->ipath_kregs->kr_hwerrclear,
- INFINIPATH_HWE_IB_UC_MEMORYPARITYERR);
- ipath_read_kreg32(dd, dd->ipath_kregs->kr_scratch);
- bail:
- return;
- }
- /*
- * After a reset or other unusual event, the epb interface may need
- * to be re-synchronized, between the host and the uC.
- * returns <0 for failure to resync within IBSD_RESYNC_TRIES (not expected)
- */
- #define IBSD_RESYNC_TRIES 3
- #define IB_PGUDP(chn) (EPB_LOC((chn), 2, 1) | EPB_IB_QUAD0_CS)
- #define IB_CMUDONE(chn) (EPB_LOC((chn), 7, 0xF) | EPB_IB_QUAD0_CS)
- static int ipath_resync_ibepb(struct ipath_devdata *dd)
- {
- int ret, pat, tries, chn;
- u32 loc;
- ret = -1;
- chn = 0;
- for (tries = 0; tries < (4 * IBSD_RESYNC_TRIES); ++tries) {
- loc = IB_PGUDP(chn);
- ret = ipath_sd7220_reg_mod(dd, IB_7220_SERDES, loc, 0, 0);
- if (ret < 0) {
- ipath_dev_err(dd, "Failed read in resync\n");
- continue;
- }
- if (ret != 0xF0 && ret != 0x55 && tries == 0)
- ipath_dev_err(dd, "unexpected pattern in resync\n");
- pat = ret ^ 0xA5; /* alternate F0 and 55 */
- ret = ipath_sd7220_reg_mod(dd, IB_7220_SERDES, loc, pat, 0xFF);
- if (ret < 0) {
- ipath_dev_err(dd, "Failed write in resync\n");
- continue;
- }
- ret = ipath_sd7220_reg_mod(dd, IB_7220_SERDES, loc, 0, 0);
- if (ret < 0) {
- ipath_dev_err(dd, "Failed re-read in resync\n");
- continue;
- }
- if (ret != pat) {
- ipath_dev_err(dd, "Failed compare1 in resync\n");
- continue;
- }
- loc = IB_CMUDONE(chn);
- ret = ipath_sd7220_reg_mod(dd, IB_7220_SERDES, loc, 0, 0);
- if (ret < 0) {
- ipath_dev_err(dd, "Failed CMUDONE rd in resync\n");
- continue;
- }
- if ((ret & 0x70) != ((chn << 4) | 0x40)) {
- ipath_dev_err(dd, "Bad CMUDONE value %02X, chn %d\n",
- ret, chn);
- continue;
- }
- if (++chn == 4)
- break; /* Success */
- }
- ipath_cdbg(VERBOSE, "Resync in %d tries\n", tries);
- return (ret > 0) ? 0 : 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);
-