IB/qib: New SERDES init routine and improvements to SI quality
Implement new SERDES initialization routine and improvements to signal integrity -- disable LE1 adaptation, disable LOS after link-up, set better SERDES parameters. Signed-off-by: Mike Marciniszyn <mike.marciniszyn@qlogic.com> Signed-off-by: Roland Dreier <rolandd@cisco.com>
This commit is contained in:
parent
16028f2777
commit
a0a234d47d
|
@ -71,6 +71,9 @@ static void qib_7322_mini_pcs_reset(struct qib_pportdata *);
|
|||
|
||||
static u32 ahb_mod(struct qib_devdata *, int, int, int, u32, u32);
|
||||
static void ibsd_wr_allchans(struct qib_pportdata *, int, unsigned, unsigned);
|
||||
static void serdes_7322_los_enable(struct qib_pportdata *, int);
|
||||
static int serdes_7322_init_old(struct qib_pportdata *);
|
||||
static int serdes_7322_init_new(struct qib_pportdata *);
|
||||
|
||||
#define BMASK(msb, lsb) (((1 << ((msb) + 1 - (lsb))) - 1) << (lsb))
|
||||
|
||||
|
@ -1692,6 +1695,8 @@ static void handle_serdes_issues(struct qib_pportdata *ppd, u64 ibcst)
|
|||
(ibcst & SYM_MASK(IBCStatusA_0, LinkSpeedQDR))) {
|
||||
force_h1(ppd);
|
||||
ppd->cpspec->qdr_reforce = 1;
|
||||
if (!ppd->dd->cspec->r1)
|
||||
serdes_7322_los_enable(ppd, 0);
|
||||
} else if (ppd->cpspec->qdr_reforce &&
|
||||
(ibcst & SYM_MASK(IBCStatusA_0, LinkSpeedQDR)) &&
|
||||
(ibclt == IB_7322_LT_STATE_CFGENH ||
|
||||
|
@ -1707,15 +1712,32 @@ static void handle_serdes_issues(struct qib_pportdata *ppd, u64 ibcst)
|
|||
ibclt <= IB_7322_LT_STATE_SLEEPQUIET)))
|
||||
adj_tx_serdes(ppd);
|
||||
|
||||
if (!ppd->cpspec->qdr_dfe_on && ibclt != IB_7322_LT_STATE_LINKUP &&
|
||||
ibclt <= IB_7322_LT_STATE_SLEEPQUIET) {
|
||||
ppd->cpspec->qdr_dfe_on = 1;
|
||||
ppd->cpspec->qdr_dfe_time = 0;
|
||||
/* On link down, reenable QDR adaptation */
|
||||
qib_write_kreg_port(ppd, krp_static_adapt_dis(2),
|
||||
ppd->dd->cspec->r1 ?
|
||||
QDR_STATIC_ADAPT_DOWN_R1 :
|
||||
QDR_STATIC_ADAPT_DOWN);
|
||||
if (ibclt != IB_7322_LT_STATE_LINKUP) {
|
||||
u8 ltstate = qib_7322_phys_portstate(ibcst);
|
||||
u8 pibclt = (u8)SYM_FIELD(ppd->lastibcstat, IBCStatusA_0,
|
||||
LinkTrainingState);
|
||||
if (!ppd->dd->cspec->r1 &&
|
||||
pibclt == IB_7322_LT_STATE_LINKUP &&
|
||||
ltstate != IB_PHYSPORTSTATE_LINK_ERR_RECOVER &&
|
||||
ltstate != IB_PHYSPORTSTATE_RECOVERY_RETRAIN &&
|
||||
ltstate != IB_PHYSPORTSTATE_RECOVERY_WAITRMT &&
|
||||
ltstate != IB_PHYSPORTSTATE_RECOVERY_IDLE)
|
||||
/* If the link went down (but no into recovery,
|
||||
* turn LOS back on */
|
||||
serdes_7322_los_enable(ppd, 1);
|
||||
if (!ppd->cpspec->qdr_dfe_on &&
|
||||
ibclt <= IB_7322_LT_STATE_SLEEPQUIET) {
|
||||
ppd->cpspec->qdr_dfe_on = 1;
|
||||
ppd->cpspec->qdr_dfe_time = 0;
|
||||
/* On link down, reenable QDR adaptation */
|
||||
qib_write_kreg_port(ppd, krp_static_adapt_dis(2),
|
||||
ppd->dd->cspec->r1 ?
|
||||
QDR_STATIC_ADAPT_DOWN_R1 :
|
||||
QDR_STATIC_ADAPT_DOWN);
|
||||
printk(KERN_INFO QIB_DRV_NAME
|
||||
" IB%u:%u re-enabled QDR adaptation "
|
||||
"ibclt %x\n", ppd->dd->unit, ppd->port, ibclt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5544,7 +5566,7 @@ static void qsfp_7322_event(struct work_struct *work)
|
|||
u64 now = get_jiffies_64();
|
||||
if (time_after64(now, pwrup))
|
||||
break;
|
||||
msleep(1);
|
||||
msleep(20);
|
||||
}
|
||||
ret = qib_refresh_qsfp_cache(ppd, &qd->cache);
|
||||
/*
|
||||
|
@ -6519,7 +6541,7 @@ static void qib_7322_txchk_change(struct qib_devdata *dd, u32 start,
|
|||
/* make sure we see an updated copy next time around */
|
||||
sendctrl_7322_mod(dd->pport, QIB_SENDCTRL_AVAIL_BLIP);
|
||||
sleeps++;
|
||||
msleep(1);
|
||||
msleep(20);
|
||||
}
|
||||
|
||||
switch (which) {
|
||||
|
@ -7234,9 +7256,30 @@ static void ibsd_wr_allchans(struct qib_pportdata *ppd, int addr, unsigned data,
|
|||
}
|
||||
}
|
||||
|
||||
static void serdes_7322_los_enable(struct qib_pportdata *ppd, int enable)
|
||||
{
|
||||
u64 data = qib_read_kreg_port(ppd, krp_serdesctrl);
|
||||
printk(KERN_INFO QIB_DRV_NAME " Turning LOS %s for port %d\n",
|
||||
(enable ? "on" : "off"), ppd->port);
|
||||
if (enable)
|
||||
data |= SYM_MASK(IBSerdesCtrl_0, RXLOSEN);
|
||||
else
|
||||
data &= ~SYM_MASK(IBSerdesCtrl_0, RXLOSEN);
|
||||
qib_write_kreg_port(ppd, krp_serdesctrl, data);
|
||||
}
|
||||
|
||||
static int serdes_7322_init(struct qib_pportdata *ppd)
|
||||
{
|
||||
u64 data;
|
||||
int ret = 0;
|
||||
if (ppd->dd->cspec->r1)
|
||||
ret = serdes_7322_init_old(ppd);
|
||||
else
|
||||
ret = serdes_7322_init_new(ppd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int serdes_7322_init_old(struct qib_pportdata *ppd)
|
||||
{
|
||||
u32 le_val;
|
||||
|
||||
/*
|
||||
|
@ -7294,9 +7337,7 @@ static int serdes_7322_init(struct qib_pportdata *ppd)
|
|||
ibsd_wr_allchans(ppd, 20, (2 << 10), BMASK(12, 10)); /* DDR */
|
||||
ibsd_wr_allchans(ppd, 20, (4 << 13), BMASK(15, 13)); /* SDR */
|
||||
|
||||
data = qib_read_kreg_port(ppd, krp_serdesctrl);
|
||||
qib_write_kreg_port(ppd, krp_serdesctrl, data |
|
||||
SYM_MASK(IBSerdesCtrl_0, RXLOSEN));
|
||||
serdes_7322_los_enable(ppd, 1);
|
||||
|
||||
/* rxbistena; set 0 to avoid effects of it switch later */
|
||||
ibsd_wr_allchans(ppd, 9, 0 << 15, 1 << 15);
|
||||
|
@ -7336,6 +7377,205 @@ static int serdes_7322_init(struct qib_pportdata *ppd)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int serdes_7322_init_new(struct qib_pportdata *ppd)
|
||||
{
|
||||
u64 tstart;
|
||||
u32 le_val, rxcaldone;
|
||||
int chan, chan_done = (1 << SERDES_CHANS) - 1;
|
||||
|
||||
/*
|
||||
* Initialize the Tx DDS tables. Also done every QSFP event,
|
||||
* for adapters with QSFP
|
||||
*/
|
||||
init_txdds_table(ppd, 0);
|
||||
|
||||
/* Clear cmode-override, may be set from older driver */
|
||||
ahb_mod(ppd->dd, IBSD(ppd->hw_pidx), 5, 10, 0 << 14, 1 << 14);
|
||||
|
||||
/* ensure no tx overrides from earlier driver loads */
|
||||
qib_write_kreg_port(ppd, krp_tx_deemph_override,
|
||||
SYM_MASK(IBSD_TX_DEEMPHASIS_OVERRIDE_0,
|
||||
reset_tx_deemphasis_override));
|
||||
|
||||
/* START OF LSI SUGGESTED SERDES BRINGUP */
|
||||
/* Reset - Calibration Setup */
|
||||
/* Stop DFE adaptaion */
|
||||
ibsd_wr_allchans(ppd, 1, 0, BMASK(9, 1));
|
||||
/* Disable LE1 */
|
||||
ibsd_wr_allchans(ppd, 13, 0, BMASK(5, 5));
|
||||
/* Disable autoadapt for LE1 */
|
||||
ibsd_wr_allchans(ppd, 1, 0, BMASK(15, 15));
|
||||
/* Disable LE2 */
|
||||
ibsd_wr_allchans(ppd, 13, 0, BMASK(6, 6));
|
||||
/* Disable VGA */
|
||||
ibsd_wr_allchans(ppd, 5, 0, BMASK(0, 0));
|
||||
/* Disable AFE Offset Cancel */
|
||||
ibsd_wr_allchans(ppd, 12, 0, BMASK(12, 12));
|
||||
/* Disable Timing Loop */
|
||||
ibsd_wr_allchans(ppd, 2, 0, BMASK(3, 3));
|
||||
/* Disable Frequency Loop */
|
||||
ibsd_wr_allchans(ppd, 2, 0, BMASK(4, 4));
|
||||
/* Disable Baseline Wander Correction */
|
||||
ibsd_wr_allchans(ppd, 13, 0, BMASK(13, 13));
|
||||
/* Disable RX Calibration */
|
||||
ibsd_wr_allchans(ppd, 4, 0, BMASK(10, 10));
|
||||
/* Disable RX Offset Calibration */
|
||||
ibsd_wr_allchans(ppd, 12, 0, BMASK(4, 4));
|
||||
/* Select BB CDR */
|
||||
ibsd_wr_allchans(ppd, 2, (1 << 15), BMASK(15, 15));
|
||||
/* CDR Step Size */
|
||||
ibsd_wr_allchans(ppd, 5, 0, BMASK(9, 8));
|
||||
/* Enable phase Calibration */
|
||||
ibsd_wr_allchans(ppd, 12, (1 << 5), BMASK(5, 5));
|
||||
/* DFE Bandwidth [2:14-12] */
|
||||
ibsd_wr_allchans(ppd, 2, (4 << 12), BMASK(14, 12));
|
||||
/* DFE Config (4 taps only) */
|
||||
ibsd_wr_allchans(ppd, 16, 0, BMASK(1, 0));
|
||||
/* Gain Loop Bandwidth */
|
||||
if (!ppd->dd->cspec->r1) {
|
||||
ibsd_wr_allchans(ppd, 12, 1 << 12, BMASK(12, 12));
|
||||
ibsd_wr_allchans(ppd, 12, 2 << 8, BMASK(11, 8));
|
||||
} else {
|
||||
ibsd_wr_allchans(ppd, 19, (3 << 11), BMASK(13, 11));
|
||||
}
|
||||
/* Baseline Wander Correction Gain [13:4-0] (leave as default) */
|
||||
/* Baseline Wander Correction Gain [3:7-5] (leave as default) */
|
||||
/* Data Rate Select [5:7-6] (leave as default) */
|
||||
/* RX Parralel Word Width [3:10-8] (leave as default) */
|
||||
|
||||
/* RX REST */
|
||||
/* Single- or Multi-channel reset */
|
||||
/* RX Analog reset */
|
||||
/* RX Digital reset */
|
||||
ibsd_wr_allchans(ppd, 0, 0, BMASK(15, 13));
|
||||
msleep(20);
|
||||
/* RX Analog reset */
|
||||
ibsd_wr_allchans(ppd, 0, (1 << 14), BMASK(14, 14));
|
||||
msleep(20);
|
||||
/* RX Digital reset */
|
||||
ibsd_wr_allchans(ppd, 0, (1 << 13), BMASK(13, 13));
|
||||
msleep(20);
|
||||
|
||||
/* setup LoS params; these are subsystem, so chan == 5 */
|
||||
/* LoS filter threshold_count on, ch 0-3, set to 8 */
|
||||
ahb_mod(ppd->dd, IBSD(ppd->hw_pidx), 5, 5, 8 << 11, BMASK(14, 11));
|
||||
ahb_mod(ppd->dd, IBSD(ppd->hw_pidx), 5, 7, 8 << 4, BMASK(7, 4));
|
||||
ahb_mod(ppd->dd, IBSD(ppd->hw_pidx), 5, 8, 8 << 11, BMASK(14, 11));
|
||||
ahb_mod(ppd->dd, IBSD(ppd->hw_pidx), 5, 10, 8 << 4, BMASK(7, 4));
|
||||
|
||||
/* LoS filter threshold_count off, ch 0-3, set to 4 */
|
||||
ahb_mod(ppd->dd, IBSD(ppd->hw_pidx), 5, 6, 4 << 0, BMASK(3, 0));
|
||||
ahb_mod(ppd->dd, IBSD(ppd->hw_pidx), 5, 7, 4 << 8, BMASK(11, 8));
|
||||
ahb_mod(ppd->dd, IBSD(ppd->hw_pidx), 5, 9, 4 << 0, BMASK(3, 0));
|
||||
ahb_mod(ppd->dd, IBSD(ppd->hw_pidx), 5, 10, 4 << 8, BMASK(11, 8));
|
||||
|
||||
/* LoS filter select enabled */
|
||||
ahb_mod(ppd->dd, IBSD(ppd->hw_pidx), 5, 9, 1 << 15, 1 << 15);
|
||||
|
||||
/* LoS target data: SDR=4, DDR=2, QDR=1 */
|
||||
ibsd_wr_allchans(ppd, 14, (1 << 3), BMASK(5, 3)); /* QDR */
|
||||
ibsd_wr_allchans(ppd, 20, (2 << 10), BMASK(12, 10)); /* DDR */
|
||||
ibsd_wr_allchans(ppd, 20, (4 << 13), BMASK(15, 13)); /* SDR */
|
||||
|
||||
/* Turn on LOS on initial SERDES init */
|
||||
serdes_7322_los_enable(ppd, 1);
|
||||
/* FLoop LOS gate: PPM filter enabled */
|
||||
ibsd_wr_allchans(ppd, 38, 0 << 10, 1 << 10);
|
||||
|
||||
/* RX LATCH CALIBRATION */
|
||||
/* Enable Eyefinder Phase Calibration latch */
|
||||
ibsd_wr_allchans(ppd, 15, 1, BMASK(0, 0));
|
||||
/* Enable RX Offset Calibration latch */
|
||||
ibsd_wr_allchans(ppd, 12, (1 << 4), BMASK(4, 4));
|
||||
msleep(20);
|
||||
/* Start Calibration */
|
||||
ibsd_wr_allchans(ppd, 4, (1 << 10), BMASK(10, 10));
|
||||
tstart = get_jiffies_64();
|
||||
while (chan_done &&
|
||||
!time_after64(tstart, tstart + msecs_to_jiffies(500))) {
|
||||
msleep(20);
|
||||
for (chan = 0; chan < SERDES_CHANS; ++chan) {
|
||||
rxcaldone = ahb_mod(ppd->dd, IBSD(ppd->hw_pidx),
|
||||
(chan + (chan >> 1)),
|
||||
25, 0, 0);
|
||||
if ((~rxcaldone & (u32)BMASK(9, 9)) == 0 &&
|
||||
(~chan_done & (1 << chan)) == 0)
|
||||
chan_done &= ~(1 << chan);
|
||||
}
|
||||
}
|
||||
if (chan_done) {
|
||||
printk(KERN_INFO QIB_DRV_NAME
|
||||
" Serdes %d calibration not done after .5 sec: 0x%x\n",
|
||||
IBSD(ppd->hw_pidx), chan_done);
|
||||
} else {
|
||||
for (chan = 0; chan < SERDES_CHANS; ++chan) {
|
||||
rxcaldone = ahb_mod(ppd->dd, IBSD(ppd->hw_pidx),
|
||||
(chan + (chan >> 1)),
|
||||
25, 0, 0);
|
||||
if ((~rxcaldone & (u32)BMASK(10, 10)) == 0)
|
||||
printk(KERN_INFO QIB_DRV_NAME
|
||||
" Serdes %d chan %d calibration "
|
||||
"failed\n", IBSD(ppd->hw_pidx), chan);
|
||||
}
|
||||
}
|
||||
|
||||
/* Turn off Calibration */
|
||||
ibsd_wr_allchans(ppd, 4, 0, BMASK(10, 10));
|
||||
msleep(20);
|
||||
|
||||
/* BRING RX UP */
|
||||
/* Set LE2 value (May be overridden in qsfp_7322_event) */
|
||||
le_val = IS_QME(ppd->dd) ? LE2_QME : LE2_DEFAULT;
|
||||
ibsd_wr_allchans(ppd, 13, (le_val << 7), BMASK(9, 7));
|
||||
/* Set LE2 Loop bandwidth */
|
||||
ibsd_wr_allchans(ppd, 3, (7 << 5), BMASK(7, 5));
|
||||
/* Enable LE2 */
|
||||
ibsd_wr_allchans(ppd, 13, (1 << 6), BMASK(6, 6));
|
||||
msleep(20);
|
||||
/* Enable H0 only */
|
||||
ibsd_wr_allchans(ppd, 1, 1, BMASK(9, 1));
|
||||
/* gain hi stop 32 (22) (6:1) lo stop 7 (10:7) target 22 (13) (15:11) */
|
||||
le_val = (ppd->dd->cspec->r1 || IS_QME(ppd->dd)) ? 0xb6c0 : 0x6bac;
|
||||
ibsd_wr_allchans(ppd, 21, le_val, 0xfffe);
|
||||
/* Enable VGA */
|
||||
ibsd_wr_allchans(ppd, 5, 0, BMASK(0, 0));
|
||||
msleep(20);
|
||||
/* Set Frequency Loop Bandwidth */
|
||||
ibsd_wr_allchans(ppd, 2, (7 << 5), BMASK(8, 5));
|
||||
/* Enable Frequency Loop */
|
||||
ibsd_wr_allchans(ppd, 2, (1 << 4), BMASK(4, 4));
|
||||
/* Set Timing Loop Bandwidth */
|
||||
ibsd_wr_allchans(ppd, 2, 0, BMASK(11, 9));
|
||||
/* Enable Timing Loop */
|
||||
ibsd_wr_allchans(ppd, 2, (1 << 3), BMASK(3, 3));
|
||||
msleep(50);
|
||||
/* Enable DFE
|
||||
* Set receive adaptation mode. SDR and DDR adaptation are
|
||||
* always on, and QDR is initially enabled; later disabled.
|
||||
*/
|
||||
qib_write_kreg_port(ppd, krp_static_adapt_dis(0), 0ULL);
|
||||
qib_write_kreg_port(ppd, krp_static_adapt_dis(1), 0ULL);
|
||||
qib_write_kreg_port(ppd, krp_static_adapt_dis(2),
|
||||
ppd->dd->cspec->r1 ?
|
||||
QDR_STATIC_ADAPT_DOWN_R1 : QDR_STATIC_ADAPT_DOWN);
|
||||
ppd->cpspec->qdr_dfe_on = 1;
|
||||
/* Disable LE1 */
|
||||
ibsd_wr_allchans(ppd, 13, (0 << 5), (1 << 5));
|
||||
/* Disable auto adapt for LE1 */
|
||||
ibsd_wr_allchans(ppd, 1, (0 << 15), BMASK(15, 15));
|
||||
msleep(20);
|
||||
/* Enable AFE Offset Cancel */
|
||||
ibsd_wr_allchans(ppd, 12, (1 << 12), BMASK(12, 12));
|
||||
/* Enable Baseline Wander Correction */
|
||||
ibsd_wr_allchans(ppd, 12, (1 << 13), BMASK(13, 13));
|
||||
/* Termination: rxtermctrl_r2d addr 11 bits [12:11] = 1 */
|
||||
ibsd_wr_allchans(ppd, 11, (1 << 11), BMASK(12, 11));
|
||||
/* VGA output common mode */
|
||||
ibsd_wr_allchans(ppd, 12, (3 << 2), BMASK(3, 2));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* start adjust QMH serdes parameters */
|
||||
|
||||
static void set_man_code(struct qib_pportdata *ppd, int chan, int code)
|
||||
|
|
Loading…
Reference in New Issue