Esempio n. 1
0
void*
etc_attach(void *et, uint vendor, uint device, uint unit, void *osh, void *regsva)
{
	etc_info_t *etc;

	ET_TRACE(("et%d: etc_attach: vendor 0x%x device 0x%x\n", unit, vendor, device));

	/* some code depends on packed structures */
	ASSERT(sizeof(struct ether_addr) == ETHER_ADDR_LEN);
	ASSERT(sizeof(struct ether_header) == ETHER_HDR_LEN);

	/* allocate etc_info_t state structure */
	if ((etc = (etc_info_t*) MALLOC(osh, sizeof(etc_info_t))) == NULL) {
		ET_ERROR(("et%d: etc_attach: out of memory, malloced %d bytes\n", unit,
		          MALLOCED(osh)));
		return (NULL);
	}
	bzero((char*)etc, sizeof(etc_info_t));

	etc->et = et;
	etc->unit = unit;
	etc->osh = osh;
	etc->vendorid = (uint16) vendor;
	etc->deviceid = (uint16) device;
	etc->forcespeed = ET_AUTO;
	etc->linkstate = FALSE;

	/* set chip opsvec */
	etc->chops = etc_chipmatch(vendor, device);
	ASSERT(etc->chops);

	/* chip attach */
	if ((etc->ch = (*etc->chops->attach)(etc, osh, regsva)) == NULL) {
		ET_ERROR(("et%d: chipattach error\n", unit));
		goto fail;
	}

	return ((void*)etc);

fail:
	etc_detach(etc);
	return (NULL);
}
Esempio n. 2
0
static void
chipphyreset(struct bcm4xxx *ch, uint phyaddr)
{
	ASSERT(phyaddr < MAXEPHY);

	if (phyaddr == EPHY_NOREG)
		return;

	chipphywr(ch, phyaddr, 0, CTL_RESET);
	OSL_DELAY(100);
	if (chipphyrd(ch, phyaddr, 0) & CTL_RESET) {
		ET_ERROR(("et%d: chipphyreset: reset not complete\n", ch->etc->unit));
	}

	chipphyinit(ch, phyaddr);
}
Esempio n. 3
0
/* dma transmit */
static bool BCMFASTPATH
chiptx(struct bcm4xxx *ch, void *p0)
{
	int error;

	ET_TRACE(("et%d: chiptx\n", ch->etc->unit));
	ET_LOG("et%d: chiptx", ch->etc->unit, 0);

	error = dma_txfast(ch->di, p0, TRUE);

	if (error) {
		ET_ERROR(("et%d: chiptx: out of txds\n", ch->etc->unit));
		ch->etc->txnobuf++;
		return FALSE;
	}
	return TRUE;
}
Esempio n. 4
0
static uint16
chipphyrd(struct bcm4xxx *ch, uint phyaddr, uint reg)
{
	bcmenetregs_t *regs;

	ASSERT(phyaddr < MAXEPHY);

	/*
	 * BCM5222 dualphy shared mdio contortion.
	 * remote phy: another emac controls our phy.
	 */
	if (ch->etc->mdcport != ch->etc->coreunit) {
		if (ch->etphy == NULL) {
			ch->etphy = et_phyfind(ch->et, ch->etc->mdcport);

			/* first time reset */
			if (ch->etphy)
				chipphyreset(ch, ch->etc->phyaddr);
		}
		if (ch->etphy)
			return (et_phyrd(ch->etphy, phyaddr, reg));
		else
			return (0xffff);
	}

	/* local phy: our emac controls our phy */

	regs = ch->regs;

	/* clear mii_int */
	W_REG(ch->osh, &regs->emacintstatus, EI_MII);

	/* issue the read */
	W_REG(ch->osh, &regs->mdiodata,  (MD_SB_START | MD_OP_READ | (phyaddr << MD_PMD_SHIFT)
		| (reg << MD_RA_SHIFT) | MD_TA_VALID));

	/* wait for it to complete */
	SPINWAIT(((R_REG(ch->osh, &regs->emacintstatus) & EI_MII) == 0), 100);
	if ((R_REG(ch->osh, &regs->emacintstatus) & EI_MII) == 0) {
		ET_ERROR(("et%d: chipphyrd: did not complete\n", ch->etc->unit));
	}

	return (R_REG(ch->osh, &regs->mdiodata) & MD_DATA_MASK);
}
Esempio n. 5
0
/* Allocate private resource */
adm_info_t *
adm_attach(si_t *sih, char *vars)
{
	adm_info_t *adm;
	int gpio;

	/* Allocate private data */
	if (!(adm = MALLOC(si_osh(sih), sizeof(adm_info_t)))) {
		ET_ERROR(("adm_attach: out of memory, malloc %d bytes", MALLOCED(si_osh(sih))));
		return NULL;
	}
	bzero((char *) adm, sizeof(adm_info_t));
	adm->sih = sih;
	adm->vars = vars;

	/* Init GPIO mapping. Default GPIO: 2, 3, 4 */
	gpio = getgpiopin(vars, "adm_eecs", 2);
	ET_ERROR(("adm_attach: got %d as adm_eecs", gpio));
	if (gpio == GPIO_PIN_NOTDEFINED) {
		ET_ERROR(("adm_attach: adm_eecs gpio fail: GPIO 2 in use"));
		goto error;
	}
	adm->eecs = 1 << gpio;

	gpio = getgpiopin(vars, "adm_eesk", 3);
	ET_ERROR(("adm_attach: got %d as adm_eesk", gpio));
	if (gpio == GPIO_PIN_NOTDEFINED) {
		ET_ERROR(("adm_attach: adm_eesk gpio fail: GPIO 3 in use"));
		goto error;
	}
	adm->eesk = 1 << gpio;

	gpio = getgpiopin(vars, "adm_eedi", 4);
	ET_ERROR(("adm_attach: got %d as adm_eedi", gpio));
	if (gpio == GPIO_PIN_NOTDEFINED) {
		ET_ERROR(("adm_attach: adm_eedi gpio fail: GPIO 4 in use"));
		goto error;
	}
	adm->eedi = 1 << gpio;

	return adm;

error:
	adm_detach(adm);
	return NULL;
}
Esempio n. 6
0
/* return true of caller should re-initialize, otherwise false */
static bool BCMFASTPATH
chiperrors(struct bcm4xxx *ch)
{
	uint32 intstatus;
	etc_info_t *etc;

	etc = ch->etc;

	intstatus = ch->intstatus;
	ch->intstatus &= ~(I_ERRORS);

	ET_TRACE(("et%d: chiperrors: intstatus 0x%x\n", etc->unit, intstatus));

	if (intstatus & I_PC) {
		ET_ERROR(("et%d: descriptor error\n", etc->unit));
		etc->dmade++;
	}

	if (intstatus & I_PD) {
		ET_ERROR(("et%d: data error\n", etc->unit));
		etc->dmada++;
	}

	if (intstatus & I_DE) {
		ET_ERROR(("et%d: descriptor protocol error\n", etc->unit));
		etc->dmape++;
	}
	/* NOTE : this ie NOT an error. It becomes an error only
	 * when the rx fifo overflows
	 */
	if (intstatus & I_RU) {
		ET_ERROR(("et%d: receive descriptor underflow\n", etc->unit));
		etc->rxdmauflo++;
	}

	if (intstatus & I_RO) {
		ET_ERROR(("et%d: receive fifo overflow\n", etc->unit));
		etc->rxoflo++;
	}

	if (intstatus & I_XU) {
		ET_ERROR(("et%d: transmit fifo underflow\n", etc->unit));
		etc->txuflo++;
	}
	/* if overflows or decriptors underflow, don't report it
	 * as an error and  provoque a reset
	 */
	if (intstatus & ~(I_RU) & I_ERRORS)
		return (TRUE);
	return FALSE;
}
Esempio n. 7
0
static void
chipphywr(struct bcm4xxx *ch, uint phyaddr, uint reg, uint16 v)
{
	bcmenetregs_t *regs;

	ASSERT(phyaddr < MAXEPHY);

	/*
	 * BCM5222 dualphy shared mdio contortion.
	 * remote phy: another emac controls our phy.
	 */
	if (ch->etc->mdcport != ch->etc->coreunit) {
		if (ch->etphy == NULL)
			ch->etphy = et_phyfind(ch->et, ch->etc->mdcport);
		if (ch->etphy)
			et_phywr(ch->etphy, phyaddr, reg, v);
		return;
	}

	/* local phy: our emac controls our phy */

	regs = ch->regs;

	/* clear mii_int */
	W_REG(ch->osh, &regs->emacintstatus, EI_MII);
	ASSERT((R_REG(ch->osh, &regs->emacintstatus) & EI_MII) == 0);

	/* issue the write */
	W_REG(ch->osh, &regs->mdiodata,  (MD_SB_START | MD_OP_WRITE | (phyaddr << MD_PMD_SHIFT)
		| (reg << MD_RA_SHIFT) | MD_TA_VALID | v));

	/* wait for it to complete */
	SPINWAIT(((R_REG(ch->osh, &regs->emacintstatus) & EI_MII) == 0), 100);
	if ((R_REG(ch->osh, &regs->emacintstatus) & EI_MII) == 0) {
		ET_ERROR(("et%d: chipphywr: did not complete\n", ch->etc->unit));
	}
}
Esempio n. 8
0
static void
chipreset(struct bcm4xxx *ch)
{
	bcmenetregs_t *regs;
	uint32 clk, mdc;

	ET_TRACE(("et%d: chipreset\n", ch->etc->unit));

	regs = ch->regs;

	if (!si_iscoreup(ch->sih)) {
		if (!ch->etc->nicmode)
			si_pci_setup(ch->sih, (1 << si_coreidx(ch->sih)));
		/* power on reset: reset the enet core */
		si_core_reset(ch->sih, 0, 0);
		goto chipinreset;
	}

	/* read counters before resetting the chip */
	if (ch->mibgood)
		chipstatsupd(ch);

	/* reset the tx dma engine */
	if (ch->di)
		dma_txreset(ch->di);

	/* set emac into loopback mode to ensure no rx traffic */
	W_REG(ch->osh, &regs->rxconfig, ERC_LE);
	OSL_DELAY(1);

	/* reset the rx dma engine */
	if (ch->di)
		dma_rxreset(ch->di);

	/* reset core */
	si_core_reset(ch->sih, 0, 0);

chipinreset:

	/* must clear mib registers by hand */
	W_REG(ch->osh, &regs->mibcontrol, EMC_RZ);
	(void) R_REG(ch->osh, &regs->mib.tx_broadcast_pkts);
	(void) R_REG(ch->osh, &regs->mib.tx_multicast_pkts);
	(void) R_REG(ch->osh, &regs->mib.tx_len_64);
	(void) R_REG(ch->osh, &regs->mib.tx_len_65_to_127);
	(void) R_REG(ch->osh, &regs->mib.tx_len_128_to_255);
	(void) R_REG(ch->osh, &regs->mib.tx_len_256_to_511);
	(void) R_REG(ch->osh, &regs->mib.tx_len_512_to_1023);
	(void) R_REG(ch->osh, &regs->mib.tx_len_1024_to_max);
	(void) R_REG(ch->osh, &regs->mib.tx_jabber_pkts);
	(void) R_REG(ch->osh, &regs->mib.tx_oversize_pkts);
	(void) R_REG(ch->osh, &regs->mib.tx_fragment_pkts);
	(void) R_REG(ch->osh, &regs->mib.tx_underruns);
	(void) R_REG(ch->osh, &regs->mib.tx_total_cols);
	(void) R_REG(ch->osh, &regs->mib.tx_single_cols);
	(void) R_REG(ch->osh, &regs->mib.tx_multiple_cols);
	(void) R_REG(ch->osh, &regs->mib.tx_excessive_cols);
	(void) R_REG(ch->osh, &regs->mib.tx_late_cols);
	(void) R_REG(ch->osh, &regs->mib.tx_defered);
	(void) R_REG(ch->osh, &regs->mib.tx_carrier_lost);
	(void) R_REG(ch->osh, &regs->mib.tx_pause_pkts);
	(void) R_REG(ch->osh, &regs->mib.rx_broadcast_pkts);
	(void) R_REG(ch->osh, &regs->mib.rx_multicast_pkts);
	(void) R_REG(ch->osh, &regs->mib.rx_len_64);
	(void) R_REG(ch->osh, &regs->mib.rx_len_65_to_127);
	(void) R_REG(ch->osh, &regs->mib.rx_len_128_to_255);
	(void) R_REG(ch->osh, &regs->mib.rx_len_256_to_511);
	(void) R_REG(ch->osh, &regs->mib.rx_len_512_to_1023);
	(void) R_REG(ch->osh, &regs->mib.rx_len_1024_to_max);
	(void) R_REG(ch->osh, &regs->mib.rx_jabber_pkts);
	(void) R_REG(ch->osh, &regs->mib.rx_oversize_pkts);
	(void) R_REG(ch->osh, &regs->mib.rx_fragment_pkts);
	(void) R_REG(ch->osh, &regs->mib.rx_missed_pkts);
	(void) R_REG(ch->osh, &regs->mib.rx_crc_align_errs);
	(void) R_REG(ch->osh, &regs->mib.rx_undersize);
	(void) R_REG(ch->osh, &regs->mib.rx_crc_errs);
	(void) R_REG(ch->osh, &regs->mib.rx_align_errs);
	(void) R_REG(ch->osh, &regs->mib.rx_symbol_errs);
	(void) R_REG(ch->osh, &regs->mib.rx_pause_pkts);
	(void) R_REG(ch->osh, &regs->mib.rx_nonpause_pkts);
	ch->mibgood = TRUE;

	/*
	 * We want the phy registers to be accessible even when
	 * the driver is "downed" so initialize MDC preamble, frequency,
	 * and whether internal or external phy here.
	 */
	/* default:  100Mhz SI clock and external phy */
	W_REG(ch->osh, &regs->mdiocontrol, 0x94);
	if (ch->etc->deviceid == BCM47XX_ENET_ID) {
		/* 47xx chips: find out the clock */
		if ((clk = si_clock(ch->sih)) != 0) {
			mdc = 0x80 | ((clk + (MDC_RATIO / 2)) / MDC_RATIO);
			W_REG(ch->osh, &regs->mdiocontrol, mdc);
		} else {
			ET_ERROR(("et%d: chipreset: Could not figure out backplane clock, "
			          "using 100Mhz\n",
			          ch->etc->unit));
		}
	}

	/* some chips have internal phy, some don't */
	if (!(R_REG(ch->osh, &regs->devcontrol) & DC_IP)) {
		W_REG(ch->osh, &regs->enetcontrol, EC_EP);
	} else if (R_REG(ch->osh, &regs->devcontrol) & DC_ER) {
		AND_REG(ch->osh, &regs->devcontrol, ~DC_ER);
		OSL_DELAY(100);
		chipphyinit(ch, ch->etc->phyaddr);
	}

	/* clear persistent sw intstatus */
	ch->intstatus = 0;
}
Esempio n. 9
0
static void *
chipattach(etc_info_t *etc, void *osh, void *regsva)
{
	struct bcm4xxx *ch;
	bcmenetregs_t *regs;
	char name[16];
	char *var;
	uint boardflags, boardtype;

	ET_TRACE(("et%d: chipattach: regsva 0x%lx\n", etc->unit, (ulong)regsva));

	if ((ch = (struct bcm4xxx *)MALLOC(osh, sizeof(struct bcm4xxx))) == NULL) {
		ET_ERROR(("et%d: chipattach: out of memory, malloced %d bytes\n", etc->unit,
		          MALLOCED(osh)));
		return (NULL);
	}
	bzero((char *)ch, sizeof(struct bcm4xxx));

	ch->etc = etc;
	ch->et = etc->et;
	ch->osh = osh;

	/* store the pointer to the sw mib */
	etc->mib = (void *)&ch->mib;

	/* get si handle */
	if ((ch->sih = si_attach(etc->deviceid, ch->osh, regsva, PCI_BUS, NULL, &ch->vars,
	                         &ch->vars_size)) == NULL) {
		ET_ERROR(("et%d: chipattach: si_attach error\n", etc->unit));
		goto fail;
	}

	/* We used to have an assert here like:
	 *	si_coreid(ch->sih) == ENET_CORE_ID
	 * but srom-less systems and simulators don't have a way to
	 * provide a default bar0window so we were relying on nvram
	 * variables. At some point we decided that we could do away
	 * with that since the wireless driver was simply doing a
	 * setcore in attach. So we need to do the same here for
	 * the ethernet.
	 */
	if ((regs = (bcmenetregs_t *)si_setcore(ch->sih, ENET_CORE_ID, etc->unit)) == NULL) {
		ET_ERROR(("et%d: chipattach: Could not setcore to the ENET core\n", etc->unit));
		goto fail;
	}

	ch->regs = regs;
	etc->chip = ch->sih->chip;
	etc->chiprev = ch->sih->chiprev;
	etc->coreid = si_coreid(ch->sih);
	etc->corerev = si_corerev(ch->sih);
	etc->nicmode = !(ch->sih->bustype == SI_BUS);
	etc->coreunit = si_coreunit(ch->sih);
	etc->boardflags = getintvar(ch->vars, "boardflags");

	etc->hwrxoff = HWRXOFF;

	boardflags = etc->boardflags;
	boardtype = ch->sih->boardtype;

	/* Backplane clock ticks per microsecs: used by gptimer, intrecvlazy */
	etc->bp_ticks_usec = si_clock(ch->sih) / 1000000;

	/* get our local ether addr */
	sprintf(name, "et%dmacaddr", etc->coreunit);
	var = getvar(ch->vars, name);
	if (var == NULL) {
		ET_ERROR(("et%d: chipattach: NVRAM_GET(%s) not found\n", etc->unit, name));
		goto fail;
	}
	bcm_ether_atoe(var, &etc->perm_etheraddr);

	if (ETHER_ISNULLADDR(&etc->perm_etheraddr)) {
		ET_ERROR(("et%d: chipattach: invalid format: %s=%s\n", etc->unit, name, var));
		goto fail;
	}
	bcopy((char *)&etc->perm_etheraddr, (char *)&etc->cur_etheraddr, ETHER_ADDR_LEN);

	/*
	 * Too much can go wrong in scanning MDC/MDIO playing "whos my phy?" .
	 * Instead, explicitly require the environment var "et<coreunit>phyaddr=<val>".
	 */

	/* get our phyaddr value */
	sprintf(name, "et%dphyaddr", etc->coreunit);
	var = getvar(ch->vars, name);
	if (var == NULL) {
		ET_ERROR(("et%d: chipattach: NVRAM_GET(%s) not found\n", etc->unit, name));
		goto fail;
	}
	etc->phyaddr = bcm_atoi(var) & EPHY_MASK;

	/* nvram says no phy is present */
	if (etc->phyaddr == EPHY_NONE) {
		ET_ERROR(("et%d: chipattach: phy not present\n", etc->unit));
		goto fail;
	}

	/* get our mdc/mdio port number */
	sprintf(name, "et%dmdcport", etc->coreunit);
	var = getvar(ch->vars, name);
	if (var == NULL) {
		ET_ERROR(("et%d: chipattach: NVRAM_GET(%s) not found\n", etc->unit, name));
		goto fail;
	}
	etc->mdcport = bcm_atoi(var);

	/* configure pci core */
	si_pci_setup(ch->sih, (1 << si_coreidx(ch->sih)));

	/* reset the enet core */
	chipreset(ch);

	/* dma attach */
	sprintf(name, "et%d", etc->coreunit);
	if ((ch->di = dma_attach(osh, name, ch->sih,
	                         (void *)&regs->dmaregs.xmt, (void *)&regs->dmaregs.rcv,
	                         NTXD, NRXD, RXBUFSZ, -1, NRXBUFPOST, HWRXOFF,
	                         &et_msg_level)) == NULL) {
		ET_ERROR(("et%d: chipattach: dma_attach failed\n", etc->unit));
		goto fail;
	}
	etc->txavail[TX_Q0] = (uint *)&ch->di->txavail;

	/* set default sofware intmask */
	ch->intmask = DEF_INTMASK;

	/*
	 * For the 5222 dual phy shared mdio contortion, our phy is
	 * on someone elses mdio pins.  This other enet enet
	 * may not yet be attached so we must defer the et_phyfind().
	 */
	/* if local phy: reset it once now */
	if (etc->mdcport == etc->coreunit)
		chipphyreset(ch, etc->phyaddr);

#ifdef ETROBO
	/*
	 * Broadcom Robo ethernet switch.
	 */
	if ((boardflags & BFL_ENETROBO) &&
	    (etc->phyaddr == EPHY_NOREG)) {
		/* Attach to the switch */
		if (!(etc->robo = bcm_robo_attach(ch->sih, ch, ch->vars,
		                                  (miird_f)bcm47xx_et_chops.phyrd,
		                                  (miiwr_f)bcm47xx_et_chops.phywr))) {
			ET_ERROR(("et%d: chipattach: robo_attach failed\n", etc->unit));
			goto fail;
		}
		/* Enable the switch and set it to a known good state */
		if (bcm_robo_enable_device(etc->robo)) {
			ET_ERROR(("et%d: chipattach: robo_enable_device failed\n", etc->unit));
			goto fail;
		}
		/* Configure the switch to do VLAN */
		if ((boardflags & BFL_ENETVLAN) &&
		    bcm_robo_config_vlan(etc->robo, etc->perm_etheraddr.octet)) {
			ET_ERROR(("et%d: chipattach: robo_config_vlan failed\n", etc->unit));
			goto fail;
		}
		/* Enable switching/forwarding */
		if (bcm_robo_enable_switch(etc->robo)) {
			ET_ERROR(("et%d: chipattach: robo_enable_switch failed\n", etc->unit));
			goto fail;
		}
	}
#endif /* ETROBO */

#ifdef ETADM
	/*
	 * ADMtek ethernet switch.
	 */
	if (boardflags & BFL_ENETADM) {
		/* Attach to the device */
		if (!(ch->adm = adm_attach(ch->sih, ch->vars))) {
			ET_ERROR(("et%d: chipattach: adm_attach failed\n", etc->unit));
			goto fail;
		}
		/* Enable the external switch and set it to a known good state */
		if (adm_enable_device(ch->adm)) {
			ET_ERROR(("et%d: chipattach: adm_enable_device failed\n", etc->unit));
			goto fail;
		}
		/* Configure the switch */
		if ((boardflags & BFL_ENETVLAN) && adm_config_vlan(ch->adm)) {
			ET_ERROR(("et%d: chipattach: adm_config_vlan failed\n", etc->unit));
			goto fail;
		}
	}
#endif /* ETADM */

	return ((void *)ch);

fail:
	chipdetach(ch);
	return (NULL);
}
Esempio n. 10
0
File: etc.c Progetto: cilynx/dd-wrt
/* called once per second */
void
etc_watchdog(etc_info_t *etc)
{
	uint16 control;
	uint16 status;
	uint16 adv;
	uint16 lpa;

	etc->now++;

	/* no local phy registers */
	if (etc->phyaddr == EPHY_NOREG) {
		control = CTL_SPEED | CTL_DUPLEX;
		status = STAT_LINK;
	} else {
		control = (*etc->chops->phyrd)(etc->ch, etc->phyaddr, 0);
		status = (*etc->chops->phyrd)(etc->ch, etc->phyaddr, 1);
	}

	/* check for bad mdio read */
	if (control == 0xffff || status == 0xffff) {
		ET_ERROR(("et%d: etc_watchdog: bad mdio read: phyaddr %d mdcport %d\n",
			etc->unit, etc->phyaddr, etc->mdcport));
		return;
	}

	/* update current speed and duplex */
	if (control & CTL_ANENAB) {
		adv = (*etc->chops->phyrd)(etc->ch, etc->phyaddr, 4);
		lpa = (*etc->chops->phyrd)(etc->ch, etc->phyaddr, 5);
	
		if ((adv & ADV_100FULL) && (lpa & LPA_100FULL)) {
			etc->speed = 100;
			etc->duplex = 1;
		} else if ((adv & ADV_100HALF) && (lpa & LPA_100HALF)) {
			etc->speed = 100;
			etc->duplex = 0;
		} else if ((adv & ADV_10FULL) && (lpa & LPA_10FULL)) {
			etc->speed = 10;
			etc->duplex = 1;
		} else {
			etc->speed = 10;
			etc->duplex = 0;
		}
	} else {
		etc->speed = (control & CTL_SPEED) ? 100 : 10;
		etc->duplex = (control & CTL_DUPLEX) ? 1 : 0;
	}

	/* monitor link state */
	if (!etc->linkstate && (status & STAT_LINK)) {
		etc->linkstate = TRUE;

		if (etc->pm_modechange)
			etc->pm_modechange = FALSE;
		else
		{
			et_link_up(etc->et);
		}
	}
	else if (etc->linkstate && !(status & STAT_LINK)) {
		etc->linkstate = FALSE;
		if (!etc->pm_modechange)
		{
			et_link_down(etc->et);
		}	
	}

	/* keep emac txcontrol duplex bit consistent with current phy duplex */
	(*etc->chops->duplexupd)(etc->ch);

	/* check for remote fault error */
	if (status & STAT_REMFAULT) {
		ET_ERROR(("et%d: remote fault\n", etc->unit));
	}

	/* check for jabber error */
	if (status & STAT_JAB) {
		ET_ERROR(("et%d: jabber\n", etc->unit));
	}

	/*
	 * Read chip mib counters occationally before the 16bit ones can wrap.
	 * We don't use the high-rate mib counters.
	 */
	if ((etc->now % 30) == 0)
		(*etc->chops->statsupd)(etc->ch);
}