summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbrad <brad@openbsd.org>2006-02-21 01:45:47 +0000
committerbrad <brad@openbsd.org>2006-02-21 01:45:47 +0000
commit7744ae7541f3f2463c38d4539c19dacaf421dbdd (patch)
tree59fb2b49de8beb6337c564adb65551f8cebd356c
parentAdjust debugging levels to something more reasonable. (diff)
downloadwireguard-openbsd-7744ae7541f3f2463c38d4539c19dacaf421dbdd.tar.xz
wireguard-openbsd-7744ae7541f3f2463c38d4539c19dacaf421dbdd.zip
- Overhaul link state detection code.
- Make use of if_link_state_change() so CARP will now see link state changes for fibre cards. revs 1.102, 1.104, 1.113, 1.120, and 1.124. From FreeBSD Tested with 5700/5701/5703/5704/5750 and a 5752.
-rw-r--r--sys/dev/pci/if_bge.c246
-rw-r--r--sys/dev/pci/if_bgereg.h5
2 files changed, 143 insertions, 108 deletions
diff --git a/sys/dev/pci/if_bge.c b/sys/dev/pci/if_bge.c
index 20d7399aa2b..603040de2c2 100644
--- a/sys/dev/pci/if_bge.c
+++ b/sys/dev/pci/if_bge.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_bge.c,v 1.134 2006/02/13 00:57:10 brad Exp $ */
+/* $OpenBSD: if_bge.c,v 1.135 2006/02/21 01:45:47 brad Exp $ */
/*
* Copyright (c) 2001 Wind River Systems
@@ -177,6 +177,7 @@ void bge_miibus_writereg(struct device *, int, int, int);
void bge_miibus_statchg(struct device *);
void bge_reset(struct bge_softc *);
+void bge_link_upd(struct bge_softc *);
#define BGE_DEBUG
#ifdef BGE_DEBUG
@@ -1669,12 +1670,23 @@ bge_blockinit(struct bge_softc *sc)
CSR_WRITE_4(sc, BGE_MI_STS, BGE_MISTS_LINK);
} else {
BGE_SETBIT(sc, BGE_MI_MODE, BGE_MIMODE_AUTOPOLL|10<<16);
- if (BGE_CHIPREV(sc->bge_chipid) == BGE_CHIPREV_5700_AX ||
- BGE_CHIPREV(sc->bge_chipid) == BGE_CHIPREV_5700_BX)
+ if (BGE_ASICREV(sc->bge_chipid) == BGE_ASICREV_BCM5700 &&
+ sc->bge_chipid != BGE_CHIPID_BCM5700_B1)
CSR_WRITE_4(sc, BGE_MAC_EVT_ENB,
BGE_EVTENB_MI_INTERRUPT);
}
+ /*
+ * Clear any pending link state attention.
+ * Otherwise some link state change events may be lost until attention
+ * is cleared by bge_intr() -> bge_link_upd() sequence.
+ * It's not necessary on newer BCM chips - perhaps enabling link
+ * state change attentions implies clearing pending attention.
+ */
+ CSR_WRITE_4(sc, BGE_MAC_STS, BGE_MACSTAT_SYNC_CHANGED|
+ BGE_MACSTAT_CFG_CHANGED|BGE_MACSTAT_MI_COMPLETE|
+ BGE_MACSTAT_LINK_CHANGED);
+
/* Enable link state change attentions. */
BGE_SETBIT(sc, BGE_MAC_EVT_ENB, BGE_EVTENB_LINK_CHANGED);
@@ -2404,7 +2416,6 @@ bge_intr(void *xsc)
{
struct bge_softc *sc;
struct ifnet *ifp;
- u_int32_t status, mimode;
sc = xsc;
ifp = &sc->arpcom.ac_if;
@@ -2417,76 +2428,11 @@ bge_intr(void *xsc)
/* Ack interrupt and stop others from occurring. */
CSR_WRITE_4(sc, BGE_MBX_IRQ0_LO, 1);
- /*
- * Process link state changes.
- * Grrr. The link status word in the status block does
- * not work correctly on the BCM5700 rev AX and BX chips,
- * according to all available information. Hence, we have
- * to enable MII interrupts in order to properly obtain
- * async link changes. Unfortunately, this also means that
- * we have to read the MAC status register to detect link
- * changes, thereby adding an additional register access to
- * the interrupt handler.
- */
- if (BGE_CHIPREV(sc->bge_chipid) == BGE_CHIPREV_5700_AX ||
- BGE_CHIPREV(sc->bge_chipid) == BGE_CHIPREV_5700_BX) {
- status = CSR_READ_4(sc, BGE_MAC_STS);
- if (status & BGE_MACSTAT_MI_INTERRUPT) {
- sc->bge_link = 0;
- timeout_del(&sc->bge_timeout);
- bge_tick(sc);
- /* Clear the interrupt */
- CSR_WRITE_4(sc, BGE_MAC_EVT_ENB,
- BGE_EVTENB_MI_INTERRUPT);
- bge_miibus_readreg(&sc->bge_dev, 1, BRGPHY_MII_ISR);
- bge_miibus_writereg(&sc->bge_dev, 1, BRGPHY_MII_IMR,
- BRGPHY_INTRS);
- }
- } else {
- if ((sc->bge_rdata->bge_status_block.bge_status &
- BGE_STATFLAG_UPDATED) &&
- (sc->bge_rdata->bge_status_block.bge_status &
- BGE_STATFLAG_LINKSTATE_CHANGED)) {
- sc->bge_rdata->bge_status_block.bge_status &=
- ~(BGE_STATFLAG_UPDATED |
- BGE_STATFLAG_LINKSTATE_CHANGED);
- /*
- * Sometimes PCS encoding errors are detected in
- * TBI mode (on fiber NICs), and for some reason
- * the chip will signal them as link changes.
- * If we get a link change event, but the 'PCS
- * encoding bit' in the MAC status register
- * is set, don't bother doing a link check.
- * This avoids spurious "gigabit link up" messages
- * that sometimes appear on fiber NICs during
- * periods of heavy traffic. (There should be no
- * effect on copper NICs).
- *
- * If we do have a copper NIC (bge_tbi == 0) then
- * check that the AUTOPOLL bit is set before
- * processing the event as a real link change.
- * Turning AUTOPOLL on and off in the MII read/write
- * functions will often trigger a link status
- * interrupt for no reason.
- */
- status = CSR_READ_4(sc, BGE_MAC_STS);
- mimode = CSR_READ_4(sc, BGE_MI_MODE);
- if (!(status & (BGE_MACSTAT_PORT_DECODE_ERROR |
- BGE_MACSTAT_MI_COMPLETE)) && (!sc->bge_tbi &&
- (mimode & BGE_MIMODE_AUTOPOLL))) {
- sc->bge_link = 0;
- timeout_del(&sc->bge_timeout);
- bge_tick(sc);
- }
- /* Clear the interrupt */
- CSR_WRITE_4(sc, BGE_MAC_STS, BGE_MACSTAT_SYNC_CHANGED|
- BGE_MACSTAT_CFG_CHANGED|BGE_MACSTAT_MI_COMPLETE|
- BGE_MACSTAT_LINK_CHANGED);
-
- /* Force flush the status block cached by PCI bridge */
- CSR_READ_4(sc, BGE_MBX_IRQ0_LO);
- }
- }
+ if ((BGE_ASICREV(sc->bge_chipid) == BGE_ASICREV_BCM5700 &&
+ sc->bge_chipid != BGE_CHIPID_BCM5700_B1) ||
+ sc->bge_rdata->bge_status_block.bge_status & BGE_STATFLAG_LINKSTATE_CHANGED ||
+ sc->bge_link_evt)
+ bge_link_upd(sc);
if (ifp->if_flags & IFF_RUNNING) {
/* Check RX return ring producer/consumer */
@@ -2510,8 +2456,6 @@ bge_tick(void *xsc)
{
struct bge_softc *sc = xsc;
struct mii_data *mii = &sc->bge_mii;
- struct ifmedia *ifm = NULL;
- struct ifnet *ifp = &sc->arpcom.ac_if;
int s;
s = splnet();
@@ -2520,36 +2464,20 @@ bge_tick(void *xsc)
bge_stats_update_regs(sc);
else
bge_stats_update(sc);
- timeout_add(&sc->bge_timeout, hz);
- if (sc->bge_link) {
- splx(s);
- return;
- }
- if (sc->bge_tbi) {
- ifm = &sc->bge_ifmedia;
- if (CSR_READ_4(sc, BGE_MAC_STS) &
- BGE_MACSTAT_TBI_PCS_SYNCHED) {
- sc->bge_link++;
- if (BGE_ASICREV(sc->bge_chipid) == BGE_ASICREV_BCM5704)
- BGE_CLRBIT(sc, BGE_MAC_MODE,
- BGE_MACMODE_TBI_SEND_CFGS);
- CSR_WRITE_4(sc, BGE_MAC_STS, 0xFFFFFFFF);
- if (!IFQ_IS_EMPTY(&ifp->if_snd))
- bge_start(ifp);
- }
- splx(s);
- return;
+ if (!sc->bge_tbi) {
+ mii_tick(mii);
+ } else {
+ /*
+ * Since in TBI mode auto-polling can't be used we should poll
+ * link status manually. Here we register pending link event
+ * and trigger interrupt.
+ */
+ sc->bge_link_evt++;
+ BGE_SETBIT(sc, BGE_MISC_LOCAL_CTL, BGE_MLC_INTR_SET);
}
- mii_tick(mii);
-
- if (!sc->bge_link && mii->mii_media_status & IFM_ACTIVE &&
- IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) {
- sc->bge_link++;
- if (!IFQ_IS_EMPTY(&ifp->if_snd))
- bge_start(ifp);
- }
+ timeout_add(&sc->bge_timeout, hz);
splx(s);
}
@@ -3088,7 +3016,7 @@ bge_ifmedia_upd(struct ifnet *ifp)
return (0);
}
- sc->bge_link = 0;
+ sc->bge_link_evt++;
if (mii->mii_instance) {
struct mii_softc *miisc;
for (miisc = LIST_FIRST(&mii->mii_phys); miisc != NULL;
@@ -3359,9 +3287,15 @@ bge_stop(struct bge_softc *sc)
ifp->if_flags = itmp;
}
- sc->bge_link = 0;
-
sc->bge_tx_saved_considx = BGE_TXCONS_UNSET;
+
+ /*
+ * We can't just call bge_link_upd() cause chip is almost stopped so
+ * bge_link_upd -> bge_tick_locked -> bge_stats_update sequence may
+ * lead to hardware deadlock. So we just clearing MAC's link state
+ * (PHY may still have link UP).
+ */
+ sc->bge_link = 0;
}
/*
@@ -3376,3 +3310,103 @@ bge_shutdown(void *xsc)
bge_stop(sc);
bge_reset(sc);
}
+
+void
+bge_link_upd(struct bge_softc *sc)
+{
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ struct mii_data *mii = &sc->bge_mii;
+ u_int32_t link, status;
+
+ /* Clear 'pending link event' flag */
+ sc->bge_link_evt = 0;
+
+ /*
+ * Process link state changes.
+ * Grrr. The link status word in the status block does
+ * not work correctly on the BCM5700 rev AX and BX chips,
+ * according to all available information. Hence, we have
+ * to enable MII interrupts in order to properly obtain
+ * async link changes. Unfortunately, this also means that
+ * we have to read the MAC status register to detect link
+ * changes, thereby adding an additional register access to
+ * the interrupt handler.
+ *
+ * XXX: perhaps link state detection procedure used for
+ * BGE_CHIPID_BCM5700_B1 can be used for other BCM5700 revisions.
+ */
+
+ if (BGE_ASICREV(sc->bge_chipid) == BGE_ASICREV_BCM5700 &&
+ sc->bge_chipid != BGE_CHIPID_BCM5700_B1) {
+ status = CSR_READ_4(sc, BGE_MAC_STS);
+ if (status & BGE_MACSTAT_MI_INTERRUPT) {
+ timeout_del(&sc->bge_timeout);
+ bge_tick(sc);
+
+ if (!sc->bge_link &&
+ mii->mii_media_status & IFM_ACTIVE &&
+ IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) {
+ sc->bge_link++;
+ } else if (sc->bge_link &&
+ (!(mii->mii_media_status & IFM_ACTIVE) ||
+ IFM_SUBTYPE(mii->mii_media_active) == IFM_NONE)) {
+ sc->bge_link = 0;
+ }
+
+ /* Clear the interrupt */
+ CSR_WRITE_4(sc, BGE_MAC_EVT_ENB,
+ BGE_EVTENB_MI_INTERRUPT);
+ bge_miibus_readreg(&sc->bge_dev, 1, BRGPHY_MII_ISR);
+ bge_miibus_writereg(&sc->bge_dev, 1, BRGPHY_MII_IMR,
+ BRGPHY_INTRS);
+ }
+ return;
+ }
+
+ if (sc->bge_tbi) {
+ status = CSR_READ_4(sc, BGE_MAC_STS);
+ if (status & BGE_MACSTAT_TBI_PCS_SYNCHED) {
+ if (!sc->bge_link) {
+ sc->bge_link++;
+ if (BGE_ASICREV(sc->bge_chipid) == BGE_ASICREV_BCM5704)
+ BGE_CLRBIT(sc, BGE_MAC_MODE,
+ BGE_MACMODE_TBI_SEND_CFGS);
+ CSR_WRITE_4(sc, BGE_MAC_STS, 0xFFFFFFFF);
+ ifp->if_link_state = LINK_STATE_UP;
+ if_link_state_change(ifp);
+ }
+ } else if (sc->bge_link) {
+ sc->bge_link = 0;
+ ifp->if_link_state = LINK_STATE_DOWN;
+ if_link_state_change(ifp);
+ }
+ /* Discard link events for MII/GMII cards if MI auto-polling disabled */
+ } else if (CSR_READ_4(sc, BGE_MI_MODE) & BGE_MIMODE_AUTOPOLL) {
+ /*
+ * Some broken BCM chips have BGE_STATFLAG_LINKSTATE_CHANGED bit
+ * in status word always set. Workaround this bug by reading
+ * PHY link status directly.
+ */
+ link = (CSR_READ_4(sc, BGE_MI_STS) & BGE_MISTS_LINK) ? 1 : 0;
+
+ if (link != sc->bge_link ||
+ BGE_ASICREV(sc->bge_chipid) == BGE_ASICREV_BCM5700) {
+ timeout_del(&sc->bge_timeout);
+ bge_tick(sc);
+
+ if (!sc->bge_link &&
+ mii->mii_media_status & IFM_ACTIVE &&
+ IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE)
+ sc->bge_link++;
+ else if (sc->bge_link &&
+ (!(mii->mii_media_status & IFM_ACTIVE) ||
+ IFM_SUBTYPE(mii->mii_media_active) == IFM_NONE))
+ sc->bge_link = 0;
+ }
+ }
+
+ /* Clear the attention */
+ CSR_WRITE_4(sc, BGE_MAC_STS, BGE_MACSTAT_SYNC_CHANGED|
+ BGE_MACSTAT_CFG_CHANGED|BGE_MACSTAT_MI_COMPLETE|
+ BGE_MACSTAT_LINK_CHANGED);
+}
diff --git a/sys/dev/pci/if_bgereg.h b/sys/dev/pci/if_bgereg.h
index c206575f122..a3e0a04f3dc 100644
--- a/sys/dev/pci/if_bgereg.h
+++ b/sys/dev/pci/if_bgereg.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_bgereg.h,v 1.41 2006/02/11 09:01:30 brad Exp $ */
+/* $OpenBSD: if_bgereg.h,v 1.42 2006/02/21 01:45:48 brad Exp $ */
/*
* Copyright (c) 2001 Wind River Systems
@@ -2375,7 +2375,8 @@ struct bge_softc {
int bge_if_flags;
int bge_flags;
int bge_txcnt;
- int bge_link;
+ int bge_link; /* link state */
+ int bge_link_evt; /* pending link event */
struct timeout bge_timeout;
u_long bge_rx_discards;
u_long bge_tx_discards;