Module Name:    src
Committed By:   msaitoh
Date:           Thu Apr 16 01:38:34 UTC 2009

Modified Files:
        src/sys/dev/pci: if_bge.c if_bgereg.h

Log Message:
Take some code from FreeBSD and OpenBSD:

  Fix some bugs that the driver unable to detect link loss (OpenBSD 1.135,
  FreeBSD 1.102)

  bge_tick(): fix some extra input error (OpenBSD 1.221 FreeBSD 1.212)

  Acknowledge link change interrupts by setting the 'link changed' bit
  in the status register (used to have no effect). FreeBSD 1.44 (OpenBSD 1.21)

  Add some fiber support (OpenBSD 1.220)

  Set BGE_MISCCFG_KEEP_GPHY_POWER as {Open,Free}BSD and linux.

  Add special case code to fix a problem with the BCM5704 in TBI mode
  (FreeBSD rev. 1.71, OpenBSD rev. 1.35)

  Force an interrupt in bge_ifmedia_upd (FreeBSD rev. 1.205, OpenBSD rev.
  1.242)

  Add missing workaround for 5705 (FreeBSD rev. 1.44, OpenBSD rev. 1.21)

  Add some known CHIPID entries.


To generate a diff of this commit:
cvs rdiff -u -r1.160 -r1.161 src/sys/dev/pci/if_bge.c
cvs rdiff -u -r1.49 -r1.50 src/sys/dev/pci/if_bgereg.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/dev/pci/if_bge.c
diff -u src/sys/dev/pci/if_bge.c:1.160 src/sys/dev/pci/if_bge.c:1.161
--- src/sys/dev/pci/if_bge.c:1.160	Thu Apr 16 01:35:24 2009
+++ src/sys/dev/pci/if_bge.c	Thu Apr 16 01:38:34 2009
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_bge.c,v 1.160 2009/04/16 01:35:24 msaitoh Exp $	*/
+/*	$NetBSD: if_bge.c,v 1.161 2009/04/16 01:38:34 msaitoh Exp $	*/
 
 /*
  * Copyright (c) 2001 Wind River Systems
@@ -79,7 +79,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if_bge.c,v 1.160 2009/04/16 01:35:24 msaitoh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_bge.c,v 1.161 2009/04/16 01:38:34 msaitoh Exp $");
 
 #include "bpfilter.h"
 #include "vlan.h"
@@ -236,6 +236,7 @@
 static int	bge_setpowerstate(struct bge_softc *, int);
 
 static void	bge_reset(struct bge_softc *);
+static void	bge_link_upd(struct bge_softc *);
 
 #define BGE_DEBUG
 #ifdef BGE_DEBUG
@@ -529,7 +530,13 @@
 	{ BGE_CHIPID_BCM5705_A3, "BCM5705 A3" },
 	{ BGE_CHIPID_BCM5750_A0, "BCM5750 A0" },
 	{ BGE_CHIPID_BCM5750_A1, "BCM5750 A1" },
-	{ BGE_CHIPID_BCM5751_A1, "BCM5751 A1" },
+	{ BGE_CHIPID_BCM5750_A3, "BCM5750 A3" },
+	{ BGE_CHIPID_BCM5750_B0, "BCM5750 B0" },
+	{ BGE_CHIPID_BCM5750_B1, "BCM5750 B1" },
+	{ BGE_CHIPID_BCM5750_C0, "BCM5750 C0" },
+	{ BGE_CHIPID_BCM5750_C1, "BCM5750 C1" },
+	{ BGE_CHIPID_BCM5750_C2, "BCM5750 C2" },
+	{ BGE_CHIPID_BCM5714_A0, "BCM5714 A0" },
 	{ BGE_CHIPID_BCM5752_A0, "BCM5752 A0" },
 	{ BGE_CHIPID_BCM5752_A1, "BCM5752 A1" },
 	{ BGE_CHIPID_BCM5752_A2, "BCM5752 A2" },
@@ -546,6 +553,8 @@
 	{ BGE_CHIPID_BCM5787_A0, "BCM5754/5787 A0" },
 	{ BGE_CHIPID_BCM5787_A1, "BCM5754/5787 A1" },
 	{ BGE_CHIPID_BCM5787_A2, "BCM5754/5787 A2" },
+	{ BGE_CHIPID_BCM5906_A1, "BCM5906 A1" },
+	{ BGE_CHIPID_BCM5906_A2, "BCM5906 A2" },
 	{ 0, NULL }
 };
 
@@ -713,7 +722,7 @@
 
 	/* Reset the EEPROM, load the clock period. */
 	CSR_WRITE_4(sc, BGE_EE_ADDR,
-	    BGE_EEADDR_RESET|BGE_EEHALFCLK(BGE_HALFCLK_384SCL));
+	    BGE_EEADDR_RESET | BGE_EEHALFCLK(BGE_HALFCLK_384SCL));
 	DELAY(20);
 
 	/* Issue the read EEPROM command. */
@@ -782,6 +791,7 @@
 	/* Reading with autopolling on may trigger PCI errors */
 	saved_autopoll = CSR_READ_4(sc, BGE_MI_MODE);
 	if (saved_autopoll & BGE_MIMODE_AUTOPOLL) {
+		BGE_STS_CLRBIT(sc, BGE_STS_AUTOPOLL);
 		CSR_WRITE_4(sc, BGE_MI_MODE,
 		    saved_autopoll &~ BGE_MIMODE_AUTOPOLL);
 		DELAY(40);
@@ -807,6 +817,7 @@
 
 done:
 	if (saved_autopoll & BGE_MIMODE_AUTOPOLL) {
+		BGE_STS_SETBIT(sc, BGE_STS_AUTOPOLL);
 		CSR_WRITE_4(sc, BGE_MI_MODE, saved_autopoll);
 		DELAY(40);
 	}
@@ -833,17 +844,18 @@
 		return;
 	}
 
-	/* Touching the PHY while autopolling is on may trigger PCI errors */
+	/* Reading with autopolling on may trigger PCI errors */
 	saved_autopoll = CSR_READ_4(sc, BGE_MI_MODE);
 	if (saved_autopoll & BGE_MIMODE_AUTOPOLL) {
 		delay(40);
+		BGE_STS_CLRBIT(sc, BGE_STS_AUTOPOLL);
 		CSR_WRITE_4(sc, BGE_MI_MODE,
 		    saved_autopoll & (~BGE_MIMODE_AUTOPOLL));
 		delay(10); /* 40 usec is supposed to be adequate */
 	}
 
-	CSR_WRITE_4(sc, BGE_MI_COMM, BGE_MICMD_WRITE|BGE_MICOMM_BUSY|
-	    BGE_MIPHY(phy)|BGE_MIREG(reg)|val);
+	CSR_WRITE_4(sc, BGE_MI_COMM, BGE_MICMD_WRITE | BGE_MICOMM_BUSY |
+	    BGE_MIPHY(phy) | BGE_MIREG(reg)|val);
 
 	for (i = 0; i < BGE_TIMEOUT; i++) {
 		delay(10);
@@ -855,6 +867,7 @@
 	}
 
 	if (saved_autopoll & BGE_MIMODE_AUTOPOLL) {
+		BGE_STS_SETBIT(sc, BGE_STS_AUTOPOLL);
 		CSR_WRITE_4(sc, BGE_MI_MODE, saved_autopoll);
 		delay(40);
 	}
@@ -879,9 +892,10 @@
 	}
 
 	BGE_CLRBIT(sc, BGE_MAC_MODE, BGE_MACMODE_PORTMODE);
-	if (IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T) {
+	if (IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T ||
+	    IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_SX)
 		BGE_SETBIT(sc, BGE_MAC_MODE, BGE_PORTMODE_GMII);
-	} else
+	else
 		BGE_SETBIT(sc, BGE_MAC_MODE, BGE_PORTMODE_MII);
 
 	if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX)
@@ -1622,14 +1636,23 @@
 			dma_rw_ctl |= 0x0F;
 	}
 
+	if (BGE_ASICREV(sc->bge_chipid) == BGE_ASICREV_BCM5700 ||
+	    BGE_ASICREV(sc->bge_chipid) == BGE_ASICREV_BCM5701)
+		dma_rw_ctl |= BGE_PCIDMARWCTL_USE_MRM |
+		    BGE_PCIDMARWCTL_ASRT_ALL_BE;
+ 
+	if (BGE_ASICREV(sc->bge_chipid) == BGE_ASICREV_BCM5703 ||
+	    BGE_ASICREV(sc->bge_chipid) == BGE_ASICREV_BCM5704)
+		dma_rw_ctl &= ~BGE_PCIDMARWCTL_MINDMA;
+
 	pci_conf_write(sc->sc_pc, sc->sc_pcitag, BGE_PCI_DMA_RW_CTL, dma_rw_ctl);
 
 	/*
 	 * Set up general mode register.
 	 */
-	CSR_WRITE_4(sc, BGE_MODE_CTL, BGE_DMA_SWAP_OPTIONS|
-		    BGE_MODECTL_MAC_ATTN_INTR|BGE_MODECTL_HOST_SEND_BDS|
-		    BGE_MODECTL_TX_NO_PHDR_CSUM|BGE_MODECTL_RX_NO_PHDR_CSUM);
+	CSR_WRITE_4(sc, BGE_MODE_CTL, BGE_DMA_SWAP_OPTIONS |
+		    BGE_MODECTL_MAC_ATTN_INTR | BGE_MODECTL_HOST_SEND_BDS |
+		    BGE_MODECTL_TX_NO_PHDR_CSUM | BGE_MODECTL_RX_NO_PHDR_CSUM);
 
 	/* Get cache line size. */
 	cachesize = pci_conf_read(sc->sc_pc, sc->sc_pcitag, BGE_PCI_CACHESZ);
@@ -1722,6 +1745,7 @@
 	int			i;
 	struct ifnet		*ifp = &sc->ethercom.ec_if;
 	bge_hostaddr		taddr;
+	u_int32_t		val;
 
 	/*
 	 * Initialize the memory window pointer register so that
@@ -1792,7 +1816,7 @@
 	/* Enable buffer manager */
 	if (!BGE_IS_5705_OR_BEYOND(sc)) {
 		CSR_WRITE_4(sc, BGE_BMAN_MODE,
-		    BGE_BMANMODE_ENABLE|BGE_BMANMODE_LOMBUF_ATTN);
+		    BGE_BMANMODE_ENABLE | BGE_BMANMODE_LOMBUF_ATTN);
 
 		/* Poll for buffer manager start indication */
 		for (i = 0; i < BGE_TIMEOUT; i++) {
@@ -1908,7 +1932,7 @@
 		i = 8;
 
 	CSR_WRITE_4(sc, BGE_RBDI_STD_REPL_THRESH, i);
-	CSR_WRITE_4(sc, BGE_RBDI_JUMBO_REPL_THRESH, BGE_JUMBO_RX_RING_CNT/8);
+	CSR_WRITE_4(sc, BGE_RBDI_JUMBO_REPL_THRESH, BGE_JUMBO_RX_RING_CNT / 8);
 
 	/*
 	 * Disable all unused send rings by setting the 'ring disabled'
@@ -2044,7 +2068,7 @@
 
 	/* Turn on RX BD completion state machine and enable attentions */
 	CSR_WRITE_4(sc, BGE_RBDC_MODE,
-	    BGE_RBDCMODE_ENABLE|BGE_RBDCMODE_ATTN);
+	    BGE_RBDCMODE_ENABLE | BGE_RBDCMODE_ATTN);
 
 	/* Turn on RX list placement state machine */
 	CSR_WRITE_4(sc, BGE_RXLP_MODE, BGE_RXLPMODE_ENABLE);
@@ -2053,13 +2077,21 @@
 	if (!(BGE_IS_5705_OR_BEYOND(sc)))
 		CSR_WRITE_4(sc, BGE_RXLS_MODE, BGE_RXLSMODE_ENABLE);
 
+	val = BGE_MACMODE_TXDMA_ENB | BGE_MACMODE_RXDMA_ENB |
+	    BGE_MACMODE_RX_STATS_CLEAR | BGE_MACMODE_TX_STATS_CLEAR |
+	    BGE_MACMODE_RX_STATS_ENB | BGE_MACMODE_TX_STATS_ENB |
+	    BGE_MACMODE_FRMHDR_DMA_ENB;
+
+	if (sc->bge_flags & BGE_PHY_FIBER_TBI)
+	    val |= BGE_PORTMODE_TBI;
+	else if (sc->bge_flags & BGE_PHY_FIBER_MII)
+	    val |= BGE_PORTMODE_GMII;
+	else
+	    val |= BGE_PORTMODE_MII;
+
 	/* Turn on DMA, clear stats */
-	CSR_WRITE_4(sc, BGE_MAC_MODE, BGE_MACMODE_TXDMA_ENB|
-	    BGE_MACMODE_RXDMA_ENB|BGE_MACMODE_RX_STATS_CLEAR|
-	    BGE_MACMODE_TX_STATS_CLEAR|BGE_MACMODE_RX_STATS_ENB|
-	    BGE_MACMODE_TX_STATS_ENB|BGE_MACMODE_FRMHDR_DMA_ENB|
-	    ((sc->bge_flags & BGE_PHY_FIBER_TBI) ? BGE_PORTMODE_TBI
-		: BGE_PORTMODE_MII));
+	CSR_WRITE_4(sc, BGE_MAC_MODE, val);
+
 
 	/* Set misc. local control, enable interrupts on attentions */
 	sc->bge_local_ctrl_reg = BGE_MLC_INTR_ONATTN | BGE_MLC_AUTO_EEPROM;
@@ -2159,10 +2191,11 @@
 
 	CSR_WRITE_4(sc, BGE_SDI_STATS_ENABLE_MASK, 0x007FFFFF);
 	CSR_WRITE_4(sc, BGE_SDI_STATS_CTL,
-	    BGE_SDISTATSCTL_ENABLE|BGE_SDISTATSCTL_FASTER);
+	    BGE_SDISTATSCTL_ENABLE | BGE_SDISTATSCTL_FASTER);
 
 	/* ack/clear link change events */
-	CSR_WRITE_4(sc, BGE_MAC_STS, BGE_MACSTAT_SYNC_CHANGED|
+	CSR_WRITE_4(sc, BGE_MAC_STS, BGE_MACSTAT_SYNC_CHANGED |
+	    BGE_MACSTAT_CFG_CHANGED | BGE_MACSTAT_MI_COMPLETE |
 	    BGE_MACSTAT_CFG_CHANGED);
 	CSR_WRITE_4(sc, BGE_MI_STS, 0);
 
@@ -2170,12 +2203,24 @@
 	if (sc->bge_flags & BGE_PHY_FIBER_TBI) {
 		CSR_WRITE_4(sc, BGE_MI_STS, BGE_MISTS_LINK);
  	} else {
-		BGE_SETBIT(sc, BGE_MI_MODE, BGE_MIMODE_AUTOPOLL|10<<16);
+		BGE_STS_SETBIT(sc, BGE_STS_AUTOPOLL);
+		BGE_SETBIT(sc, BGE_MI_MODE, BGE_MIMODE_AUTOPOLL | (10 << 16));
 		if (BGE_ASICREV(sc->bge_chipid) == BGE_ASICREV_BCM5700)
 			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);
 
@@ -2296,6 +2341,7 @@
 	u_int32_t		hwcfg = 0;
 	u_int32_t		command;
 	struct ifnet		*ifp;
+	u_int32_t		misccfg;
 	void *			kva;
 	u_char			eaddr[ETHER_ADDR_LEN];
 	pcireg_t		memtype;
@@ -2553,13 +2599,14 @@
 		    BGE_EE_HWCFG_OFFSET, sizeof(hwcfg));
 		hwcfg = be32toh(hwcfg);
 	}
-	if ((hwcfg & BGE_HWCFG_MEDIA) == BGE_MEDIA_FIBER)
-		sc->bge_flags |= BGE_PHY_FIBER_TBI;
-
 	/* The SysKonnect SK-9D41 is a 1000baseSX card. */
-	if ((pci_conf_read(sc->sc_pc, sc->sc_pcitag, BGE_PCI_SUBSYS) >> 16) ==
-	    SK_SUBSYSID_9D41)
-		sc->bge_flags |= BGE_PHY_FIBER_TBI;
+	if (PCI_PRODUCT(pa->pa_id) == SK_SUBSYSID_9D41 ||
+	    (hwcfg & BGE_HWCFG_MEDIA) == BGE_MEDIA_FIBER) {
+		if (BGE_IS_5714_FAMILY(sc))
+		    sc->bge_flags |= BGE_PHY_FIBER_MII;
+		else
+		    sc->bge_flags |= BGE_PHY_FIBER_TBI;
+	}
 
 	if (sc->bge_flags & BGE_PHY_FIBER_TBI) {
 		ifmedia_init(&sc->bge_ifmedia, IFM_IMASK, bge_ifmedia_upd,
@@ -2605,6 +2652,14 @@
 		sc->bge_flags & BGE_PCIX)
 		sc->bge_flags |= BGE_RX_ALIGNBUG;
 
+	misccfg = CSR_READ_4(sc, BGE_MISC_CFG);
+	misccfg &= BGE_MISCCFG_BOARD_ID_MASK;
+
+	if (BGE_ASICREV(sc->bge_chipid) == BGE_ASICREV_BCM5705 &&
+	    (misccfg == BGE_MISCCFG_BOARD_ID_5788 ||
+	     misccfg == BGE_MISCCFG_BOARD_ID_5788M))
+		sc->bge_flags |= BGE_IS_5788;
+
 	/*
 	 * Call MI attach routine.
 	 */
@@ -2709,6 +2764,13 @@
 		}
 	}
 
+	/*
+	 * Set GPHY Power Down Override to leave GPHY
+	 * powered up in D0 uninitialized.
+	 */
+	if (BGE_IS_5705_OR_BEYOND(sc))
+		val |= BGE_MISCCFG_KEEP_GPHY_POWER;
+
 	/* Issue global reset */
 	write_op(sc, BGE_MISC_CFG, val);
 
@@ -2721,8 +2783,6 @@
 		    i & ~BGE_VCPU_EXT_CTRL_HALT_CPU);
 	}
 
-
-
 	DELAY(1000);
 
 	/*
@@ -2832,11 +2892,6 @@
 		aprint_error_dev(sc->bge_dev, "pcistate failed to revert\n");
 	}
 
-	/* XXX: from FreeBSD/Linux; no documentation */
-	if (sc->bge_flags & BGE_PCIE &&
-	    sc->bge_chipid != BGE_CHIPID_BCM5750_A0)
-		CSR_WRITE_4(sc, BGE_PCIE_CTL0, CSR_READ_4(sc, BGE_PCIE_CTL0) | (1<<25));
-
 	/* Enable memory arbiter. */
 	/* XXX why do this twice? */
 	{
@@ -2852,6 +2907,24 @@
 
 	CSR_WRITE_4(sc, BGE_MAC_MODE, 0);
 
+	/*
+	 * The 5704 in TBI mode apparently needs some special
+	 * adjustment to insure the SERDES drive level is set
+	 * to 1.2V.
+	 */
+	if (sc->bge_flags & BGE_PHY_FIBER_TBI &&
+	    BGE_ASICREV(sc->bge_chipid) == BGE_ASICREV_BCM5704) {
+		u_int32_t serdescfg;
+
+		serdescfg = CSR_READ_4(sc, BGE_SERDES_CFG);
+		serdescfg = (serdescfg & ~0xFFF) | 0x880;
+		CSR_WRITE_4(sc, BGE_SERDES_CFG, serdescfg);
+	}
+
+	/* XXX: from FreeBSD/Linux; no documentation */
+	if (sc->bge_flags & BGE_PCIE &&
+	    sc->bge_chipid != BGE_CHIPID_BCM5750_A0)
+		CSR_WRITE_4(sc, BGE_PCIE_CTL0, CSR_READ_4(sc, BGE_PCIE_CTL0) | (1<<25));
 	DELAY(10000);
 }
 
@@ -3101,113 +3174,69 @@
 {
 	struct bge_softc *sc;
 	struct ifnet *ifp;
+	uint32_t statusword;
 
 	sc = xsc;
 	ifp = &sc->ethercom.ec_if;
 
-	/* 
-	 * Ascertain whether the interrupt is from this bge device.
-	 * Do the cheap test first.
-	 */  
-	if ((sc->bge_rdata->bge_status_block.bge_status &
-	    BGE_STATFLAG_UPDATED) == 0) {
-		/*
-		 * Sometimes, the interrupt comes in before the
-		 * DMA update of the status block (performed prior
-		 * to the  interrupt itself) has completed.
-		 * In that case, do the (extremely expensive!)
-		 * PCI-config-space register read.
-		 */
-		uint32_t pcistate =
-		    pci_conf_read(sc->sc_pc, sc->sc_pcitag, BGE_PCI_PCISTATE);
-
-		if (pcistate & BGE_PCISTATE_INTR_STATE)
-			return (0);
-
-	}
-	/*
-	 *  If we reach here, then the interrupt is for us.
+	/* It is possible for the interrupt to arrive before
+	 * the status block is updated prior to the interrupt.
+	 * Reading the PCI State register will confirm whether the
+	 * interrupt is ours and will flush the status block.
 	 */
 
-	/* Ack interrupt and stop others from occuring. */
-	bge_writembx(sc, BGE_MBX_IRQ0_LO, 1);
+	/* read status word from status block */
+	statusword = sc->bge_rdata->bge_status_block.bge_status;
 
-	BGE_EVCNT_INCR(sc->bge_ev_intr);
+	if ((statusword & BGE_STATFLAG_UPDATED) ||
+	    (!(CSR_READ_4(sc, BGE_PCI_PCISTATE) & BGE_PCISTATE_INTR_NOT_ACTIVE))) {
+		/* Ack interrupt and stop others from occuring. */
+		bge_writembx(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_ASICREV(sc->bge_chipid) == BGE_ASICREV_BCM5700) {
-		u_int32_t		status;
+		BGE_EVCNT_INCR(sc->bge_ev_intr);
 
-		status = CSR_READ_4(sc, BGE_MAC_STS);
-		if (status & BGE_MACSTAT_MI_INTERRUPT) {
-			sc->bge_link = 0;
-			callout_stop(&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 {
-		u_int32_t		status;
+		/* clear status word */
+		sc->bge_rdata->bge_status_block.bge_status = 0;
 
-		status = CSR_READ_4(sc, BGE_MAC_STS);
-		if (status & BGE_MACSTAT_LINK_CHANGED) {
-			sc->bge_link = 0;
-			callout_stop(&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);
-		}
-	}
+		if (BGE_ASICREV(sc->bge_chipid) == BGE_ASICREV_BCM5700 ||
+		    statusword & BGE_STATFLAG_LINKSTATE_CHANGED ||
+		    BGE_STS_BIT(sc, BGE_STS_LINK_EVT))
+			bge_link_upd(sc);
 
-	if (ifp->if_flags & IFF_RUNNING) {
-		/* Check RX return ring producer/consumer */
-		bge_rxeof(sc);
+		if (ifp->if_flags & IFF_RUNNING) {
+			/* Check RX return ring producer/consumer */
+			bge_rxeof(sc);
 
-		/* Check TX ring producer/consumer */
-		bge_txeof(sc);
-	}
+			/* Check TX ring producer/consumer */
+			bge_txeof(sc);
+		}
 
-	if (sc->bge_pending_rxintr_change) {
-		uint32_t rx_ticks = sc->bge_rx_coal_ticks;
-		uint32_t rx_bds = sc->bge_rx_max_coal_bds;
-		uint32_t junk;
+		if (sc->bge_pending_rxintr_change) {
+			uint32_t rx_ticks = sc->bge_rx_coal_ticks;
+			uint32_t rx_bds = sc->bge_rx_max_coal_bds;
+			uint32_t junk;
 
-		CSR_WRITE_4(sc, BGE_HCC_RX_COAL_TICKS, rx_ticks);
-		DELAY(10);
-		junk = CSR_READ_4(sc, BGE_HCC_RX_COAL_TICKS);
+			CSR_WRITE_4(sc, BGE_HCC_RX_COAL_TICKS, rx_ticks);
+			DELAY(10);
+			junk = CSR_READ_4(sc, BGE_HCC_RX_COAL_TICKS);
 
-		CSR_WRITE_4(sc, BGE_HCC_RX_MAX_COAL_BDS, rx_bds);
-		DELAY(10);
-		junk = CSR_READ_4(sc, BGE_HCC_RX_MAX_COAL_BDS);
+			CSR_WRITE_4(sc, BGE_HCC_RX_MAX_COAL_BDS, rx_bds);
+			DELAY(10);
+			junk = CSR_READ_4(sc, BGE_HCC_RX_MAX_COAL_BDS);
 
-		sc->bge_pending_rxintr_change = 0;
-	}
-	bge_handle_events(sc);
+			sc->bge_pending_rxintr_change = 0;
+		}
+		bge_handle_events(sc);
 
-	/* Re-enable interrupts. */
-	bge_writembx(sc, BGE_MBX_IRQ0_LO, 0);
+		/* Re-enable interrupts. */
+		bge_writembx(sc, BGE_MBX_IRQ0_LO, 0);
 
-	if (ifp->if_flags & IFF_RUNNING && !IFQ_IS_EMPTY(&ifp->if_snd))
-		bge_start(ifp);
+		if (ifp->if_flags & IFF_RUNNING && !IFQ_IS_EMPTY(&ifp->if_snd))
+			bge_start(ifp);
 
-	return (1);
+		return (1);
+	} else
+		return (0);
 }
 
 static void
@@ -3220,15 +3249,26 @@
 	s = splnet();
 
 	bge_stats_update(sc);
-	callout_reset(&sc->bge_timeout, hz, bge_tick, sc);
 
 	if (sc->bge_flags & BGE_PHY_FIBER_TBI) {
-		if (CSR_READ_4(sc, BGE_MAC_STS) &
-		    BGE_MACSTAT_TBI_PCS_SYNCHED) {
-			CSR_WRITE_4(sc, BGE_MAC_STS, 0xFFFFFFFF);
-		}
-	} else
-		mii_tick(mii);
+		/*
+		 * 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.
+		 */
+		BGE_STS_SETBIT(sc, BGE_STS_LINK_EVT);
+		BGE_SETBIT(sc, BGE_MISC_LOCAL_CTL, BGE_MLC_INTR_SET);
+	} else {
+		/*
+		 * Do not touch PHY if we have link up. This could break
+		 * IPMI/ASF mode or produce extra input errors.
+		 * (extra input errors was reported for bcm5701 & bcm5704).
+		 */
+		if (!BGE_STS_BIT(sc, BGE_STS_LINK))
+			mii_tick(mii);
+	}
+
+	callout_reset(&sc->bge_timeout, hz, bge_tick, sc);
 
 	splx(s);
 }
@@ -3899,6 +3939,24 @@
 	/* Init RX ring. */
 	bge_init_rx_ring_std(sc);
 
+	/*
+	 * Workaround for a bug in 5705 ASIC rev A0. Poll the NIC's
+	 * memory to insure that the chip has in fact read the first
+	 * entry of the ring.
+	 */
+	if (sc->bge_chipid == BGE_CHIPID_BCM5705_A0) {
+		u_int32_t		v, i;
+		for (i = 0; i < 10; i++) {
+			DELAY(20);
+			v = bge_readmem_ind(sc, BGE_STD_RX_RINGS + 8);
+			if (v == (MCLBYTES - ETHER_ALIGN))
+				break;
+		}
+		if (i == 10)
+			aprint_error_dev(sc->bge_dev,
+			    "5705 A0 chip failed to load RX ring\n");
+	}
+
 	/* Init jumbo RX ring. */
 	if (ifp->if_mtu > (ETHERMTU + ETHER_HDR_LEN + ETHER_CRC_LEN))
 		bge_init_rx_ring_jumbo(sc);
@@ -3956,6 +4014,26 @@
 			return (EINVAL);
 		switch(IFM_SUBTYPE(ifm->ifm_media)) {
 		case IFM_AUTO:
+			/*
+			 * The BCM5704 ASIC appears to have a special
+			 * mechanism for programming the autoneg
+			 * advertisement registers in TBI mode.
+			 */
+			if (BGE_ASICREV(sc->bge_chipid) == BGE_ASICREV_BCM5704) {
+				u_int32_t sgdig;
+				sgdig = CSR_READ_4(sc, BGE_SGDIG_STS);
+				if (sgdig & BGE_SGDIGSTS_DONE) {
+					CSR_WRITE_4(sc, BGE_TX_TBI_AUTONEG, 0);
+					sgdig = CSR_READ_4(sc, BGE_SGDIG_CFG);
+					sgdig |= BGE_SGDIGCFG_AUTO |
+					    BGE_SGDIGCFG_PAUSE_CAP |
+					    BGE_SGDIGCFG_ASYM_PAUSE;
+					CSR_WRITE_4(sc, BGE_SGDIG_CFG,
+					    sgdig | BGE_SGDIGCFG_SEND);
+					DELAY(5);
+					CSR_WRITE_4(sc, BGE_SGDIG_CFG, sgdig);
+				}
+			}
 			break;
 		case IFM_1000_SX:
 			if ((ifm->ifm_media & IFM_GMASK) == IFM_FDX) {
@@ -3973,9 +4051,24 @@
 		return (0);
 	}
 
-	sc->bge_link = 0;
+	BGE_STS_SETBIT(sc, BGE_STS_LINK_EVT);
 	if ((rc = mii_mediachg(mii)) == ENXIO)
 		return 0;
+
+	/*
+	 * Force an interrupt so that we will call bge_link_upd
+	 * if needed and clear any pending link state attention.
+	 * Without this we are not getting any further interrupts
+	 * for link state changes and thus will not UP the link and
+	 * not be able to send in bge_start. The only way to get
+	 * things working was to receive a packet and get a RX intr.
+	 */
+	if (BGE_ASICREV(sc->bge_chipid) == BGE_ASICREV_BCM5700 ||
+	    sc->bge_flags & BGE_IS_5788)
+		BGE_SETBIT(sc, BGE_MISC_LOCAL_CTL, BGE_MLC_INTR_SET);
+	else
+		BGE_SETBIT(sc, BGE_HCC_MODE, BGE_HCCMODE_COAL_NOW);
+
 	return rc;
 }
 
@@ -4209,13 +4302,110 @@
 	if (!(sc->bge_flags & BGE_PHY_FIBER_TBI))
 		mii_down(&sc->bge_mii);
 
-	sc->bge_link = 0;
-
 	sc->bge_tx_saved_considx = BGE_TXCONS_UNSET;
 
+	/* Clear MAC's link state (PHY may still have link UP). */
+	BGE_STS_CLRBIT(sc, BGE_STS_LINK);
+
 	ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
 }
 
+static void
+bge_link_upd(struct bge_softc *sc)
+{
+	struct ifnet *ifp = &sc->ethercom.ec_if;
+	struct mii_data *mii = &sc->bge_mii;
+	u_int32_t status;
+	int link;
+
+	/* Clear 'pending link event' flag */
+	BGE_STS_CLRBIT(sc, BGE_STS_LINK_EVT);
+
+	/*
+	 * 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_ASICREV(sc->bge_chipid) == BGE_ASICREV_BCM5700) {
+		status = CSR_READ_4(sc, BGE_MAC_STS);
+		if (status & BGE_MACSTAT_MI_INTERRUPT) {
+			mii_pollstat(mii);
+
+			if (!BGE_STS_BIT(sc, BGE_STS_LINK) &&
+			    mii->mii_media_status & IFM_ACTIVE &&
+			    IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE)
+				BGE_STS_SETBIT(sc, BGE_STS_LINK);
+			else if (BGE_STS_BIT(sc, BGE_STS_LINK) &&
+			    (!(mii->mii_media_status & IFM_ACTIVE) ||
+			    IFM_SUBTYPE(mii->mii_media_active) == IFM_NONE))
+				BGE_STS_CLRBIT(sc, BGE_STS_LINK);
+
+			/* 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_flags & BGE_PHY_FIBER_TBI) {
+		status = CSR_READ_4(sc, BGE_MAC_STS);
+		if (status & BGE_MACSTAT_TBI_PCS_SYNCHED) {
+			if (!BGE_STS_BIT(sc, BGE_STS_LINK)) {
+				BGE_STS_SETBIT(sc, BGE_STS_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_link_state_change(ifp, LINK_STATE_UP);
+			}
+		} else if (BGE_STS_BIT(sc, BGE_STS_LINK)) {
+			BGE_STS_CLRBIT(sc, BGE_STS_LINK);
+			if_link_state_change(ifp, LINK_STATE_DOWN);
+		}
+        /*
+	 * Discard link events for MII/GMII cards if MI auto-polling disabled.
+	 * This should not happen since mii callouts are locked now, but
+	 * we keep this check for debug.
+	 */
+	} else if (BGE_STS_BIT(sc, BGE_STS_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)?
+		    BGE_STS_LINK : 0;
+
+		if (BGE_STS_BIT(sc, BGE_STS_LINK) != link) {
+			mii_pollstat(mii);
+
+			if (!BGE_STS_BIT(sc, BGE_STS_LINK) &&
+			    mii->mii_media_status & IFM_ACTIVE &&
+			    IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE)
+				BGE_STS_SETBIT(sc, BGE_STS_LINK);
+			else if (BGE_STS_BIT(sc, BGE_STS_LINK) &&
+			    (!(mii->mii_media_status & IFM_ACTIVE) ||
+			    IFM_SUBTYPE(mii->mii_media_active) == IFM_NONE))
+				BGE_STS_CLRBIT(sc, BGE_STS_LINK);
+		}
+	}
+
+	/* 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);
+}
+
 static int
 sysctl_bge_verify(SYSCTLFN_ARGS)
 {

Index: src/sys/dev/pci/if_bgereg.h
diff -u src/sys/dev/pci/if_bgereg.h:1.49 src/sys/dev/pci/if_bgereg.h:1.50
--- src/sys/dev/pci/if_bgereg.h:1.49	Sun Mar 22 16:20:06 2009
+++ src/sys/dev/pci/if_bgereg.h	Thu Apr 16 01:38:34 2009
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_bgereg.h,v 1.49 2009/03/22 16:20:06 msaitoh Exp $	*/
+/*	$NetBSD: if_bgereg.h,v 1.50 2009/04/16 01:38:34 msaitoh Exp $	*/
 /*
  * Copyright (c) 2001 Wind River Systems
  * Copyright (c) 1997, 1998, 1999, 2001
@@ -262,7 +262,7 @@
 #define BGE_CHIPID_BCM5750_A1		0x40010000
 #define BGE_CHIPID_BCM5750_A3		0x40030000
 #define BGE_CHIPID_BCM5750_B0		0x40100000
-#define BGE_CHIPID_BCM5751_A1		0x41010000
+#define BGE_CHIPID_BCM5750_B1		0x41010000
 #define BGE_CHIPID_BCM5750_C0		0x42000000
 #define BGE_CHIPID_BCM5750_C1		0x42010000
 #define BGE_CHIPID_BCM5750_C2		0x42020000
@@ -300,9 +300,6 @@
 #define BGE_ASICREV_BCM5714		0x09
 #define BGE_ASICREV_BCM5755		0x0a
 #define BGE_ASICREV_BCM5787		0x0b
-/* is this one mistyped ??? */
-#define BGE_ASICREV_BCM5706		0x0c
-
 #define BGE_ASICREV_BCM5906		0x0c
 
 /* chip revisions */
@@ -361,7 +358,7 @@
  * register is set.
  */
 #define BGE_PCISTATE_FORCE_RESET	0x00000001
-#define BGE_PCISTATE_INTR_STATE		0x00000002
+#define BGE_PCISTATE_INTR_NOT_ACTIVE	0x00000002
 #define BGE_PCISTATE_PCI_BUSMODE	0x00000004 /* 1 = PCI, 0 = PCI-X */
 #define BGE_PCISTATE_PCI_BUSSPEED	0x00000008 /* 1 = 33/66, 0 = 66/133 */
 #define BGE_PCISTATE_32BIT_BUS		0x00000010 /* 1 = 32bit, 0 = 64bit */
@@ -607,6 +604,9 @@
 #define BGE_RX_BD_RULES_MASKVAL15	0x04FC
 #define BGE_RX_RULES_CFG		0x0500
 #define BGE_MAX_RX_FRAME_LOWAT		0x0504
+#define BGE_SERDES_CFG			0x0590
+#define BGE_SGDIG_CFG			0x05B0
+#define BGE_SGDIG_STS			0x05B4
 #define BGE_RX_STATS			0x0800
 #define BGE_TX_STATS			0x0880
 
@@ -737,6 +737,15 @@
 #define BGE_RXRULEMASK_VALUE		0x0000FFFF
 #define BGE_RXRULEMASK_MASKVAL		0xFFFF0000
 
+/* SGDIG config (not documented) */
+#define BGE_SGDIGCFG_PAUSE_CAP		0x00000800
+#define BGE_SGDIGCFG_ASYM_PAUSE		0x00001000
+#define BGE_SGDIGCFG_SEND		0x40000000
+#define BGE_SGDIGCFG_AUTO		0x80000000
+
+/* SGDIG status (not documented) */
+#define BGE_SGDIGSTS_DONE		0x00000002
+
 /* MI communication register */
 #define BGE_MICOMM_DATA			0x0000FFFF
 #define BGE_MICOMM_REG			0x001F0000
@@ -1785,7 +1794,11 @@
 /* Misc. config register */
 #define BGE_MISCCFG_RESET_CORE_CLOCKS	0x00000001
 #define BGE_MISCCFG_TIMER_PRESCALER	0x000000FE
+#define BGE_MISCCFG_BOARD_ID_5788	0x00010000
+#define BGE_MISCCFG_BOARD_ID_5788M	0x00018000
+#define BGE_MISCCFG_BOARD_ID_MASK	0x0001e000
 #define BGE_MISCCFG_EPHY_IDDQ		0x00200000
+#define BGE_MISCCFG_KEEP_GPHY_POWER	0x04000000
 
 #define BGE_32BITTIME_66MHZ		(0x41 << 1)
 
@@ -2424,8 +2437,6 @@
 	char			*bge_name;
 };
 
-#define BGE_HWREV_TIGON		0x01
-#define BGE_HWREV_TIGON_II	0x02
 #define BGE_TIMEOUT		1000
 #define BGE_TXCONS_UNSET		0xFFFF	/* impossible value */
 
@@ -2454,6 +2465,8 @@
 #define BGE_PCIX		0x00000020
 #define BGE_PCIE		0x00000040
 #define BGE_PHY_FIBER_TBI	0x00000800
+#define BGE_PHY_FIBER_MII	0x00001000
+#define BGE_IS_5788		0x00100000
 
 struct bge_softc {
 	device_t		bge_dev;
@@ -2488,6 +2501,13 @@
 	u_int32_t		bge_rx_max_coal_bds;
 	u_int32_t		bge_tx_max_coal_bds;
 	u_int32_t		bge_tx_buf_ratio;
+	uint32_t		bge_sts;
+#define BGE_STS_LINK		0x00000001	/* MAC link status */
+#define BGE_STS_LINK_EVT	0x00000002	/* pending link event */
+#define BGE_STS_AUTOPOLL	0x00000004	/* PHY auto-polling  */
+#define BGE_STS_BIT(sc, x)	((sc)->bge_sts & (x))
+#define BGE_STS_SETBIT(sc, x)	((sc)->bge_sts |= (x))
+#define BGE_STS_CLRBIT(sc, x)	((sc)->bge_sts &= ~(x))
 	int			bge_if_flags;
 	int			bge_flags;
 	int			bge_flowflags;
@@ -2504,7 +2524,6 @@
 	struct evcnt bge_ev_xoffentered;/* XOFF state entered */
 #endif /* BGE_EVENT_COUNTERS */
 	int			bge_txcnt;
-	int			bge_link;
 	struct callout		bge_timeout;
 	char			*bge_vpd_prodname;
 	char			*bge_vpd_readonly;

Reply via email to