if_mx.c

来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 2,495 行 · 第 1/4 页

C
2,495
字号
	 * The 100baseT4 PHY on the 3c905-T4 has the 'autoneg supported'	 * bit cleared in the status register, but has the 'autoneg enabled'	 * bit set in the control register. This is a contradiction, and	 * I'm not sure how to handle it. If you want to force an attempt	 * to autoneg for 100baseT4 PHYs, #define FORCE_AUTONEG_TFOUR	 * and see what happens.	 */#ifndef FORCE_AUTONEG_TFOUR	/*	 * First, see if autoneg is supported. If not, there's	 * no point in continuing.	 */	phy_sts = mx_phy_readreg(sc, PHY_BMSR);	if (!(phy_sts & PHY_BMSR_CANAUTONEG)) {		if (verbose)			printf("mx%d: autonegotiation not supported\n",							sc->mx_unit);		ifm->ifm_media = IFM_ETHER|IFM_10_T|IFM_HDX;			return;	}#endif	switch (flag) {	case MX_FLAG_FORCEDELAY:		/*	 	 * XXX Never use this option anywhere but in the probe	 	 * routine: making the kernel stop dead in its tracks 		 * for three whole seconds after we've gone multi-user		 * is really bad manners.	 	 */		mx_autoneg_xmit(sc);		DELAY(5000000);		break;	case MX_FLAG_SCHEDDELAY:		/*		 * Wait for the transmitter to go idle before starting		 * an autoneg session, otherwise mx_start() may clobber	 	 * our timeout, and we don't want to allow transmission		 * during an autoneg session since that can screw it up.	 	 */		if (sc->mx_cdata.mx_tx_head != NULL) {			sc->mx_want_auto = 1;			return;		}		mx_autoneg_xmit(sc);		ifp->if_timer = 5;		sc->mx_autoneg = 1;		sc->mx_want_auto = 0;		return;		break;	case MX_FLAG_DELAYTIMEO:		ifp->if_timer = 0;		sc->mx_autoneg = 0;		break;	default:		printf("mx%d: invalid autoneg flag: %d\n", sc->mx_unit, flag);		return;	}	if (mx_phy_readreg(sc, PHY_BMSR) & PHY_BMSR_AUTONEGCOMP) {		if (verbose)			printf("mx%d: autoneg complete, ", sc->mx_unit);		phy_sts = mx_phy_readreg(sc, PHY_BMSR);	} else {		if (verbose)			printf("mx%d: autoneg not complete, ", sc->mx_unit);	}	media = mx_phy_readreg(sc, PHY_BMCR);	/* Link is good. Report modes and set duplex mode. */	if (mx_phy_readreg(sc, PHY_BMSR) & PHY_BMSR_LINKSTAT) {		if (verbose)			printf("link status good ");		advert = mx_phy_readreg(sc, PHY_ANAR);		ability = mx_phy_readreg(sc, PHY_LPAR);		if (advert & PHY_ANAR_100BT4 && ability & PHY_ANAR_100BT4) {			ifm->ifm_media = IFM_ETHER|IFM_100_T4;			media |= PHY_BMCR_SPEEDSEL;			media &= ~PHY_BMCR_DUPLEX;			printf("(100baseT4)\n");		} else if (advert & PHY_ANAR_100BTXFULL &&			ability & PHY_ANAR_100BTXFULL) {			ifm->ifm_media = IFM_ETHER|IFM_100_TX|IFM_FDX;			media |= PHY_BMCR_SPEEDSEL;			media |= PHY_BMCR_DUPLEX;			printf("(full-duplex, 100Mbps)\n");		} else if (advert & PHY_ANAR_100BTXHALF &&			ability & PHY_ANAR_100BTXHALF) {			ifm->ifm_media = IFM_ETHER|IFM_100_TX|IFM_HDX;			media |= PHY_BMCR_SPEEDSEL;			media &= ~PHY_BMCR_DUPLEX;			printf("(half-duplex, 100Mbps)\n");		} else if (advert & PHY_ANAR_10BTFULL &&			ability & PHY_ANAR_10BTFULL) {			ifm->ifm_media = IFM_ETHER|IFM_10_T|IFM_FDX;			media &= ~PHY_BMCR_SPEEDSEL;			media |= PHY_BMCR_DUPLEX;			printf("(full-duplex, 10Mbps)\n");		} else if (advert & PHY_ANAR_10BTHALF &&			ability & PHY_ANAR_10BTHALF) {			ifm->ifm_media = IFM_ETHER|IFM_10_T|IFM_HDX;			media &= ~PHY_BMCR_SPEEDSEL;			media &= ~PHY_BMCR_DUPLEX;			printf("(half-duplex, 10Mbps)\n");		}		media &= ~PHY_BMCR_AUTONEGENBL;		/* Set ASIC's duplex mode to match the PHY. */		mx_setcfg(sc, media);		mx_phy_writereg(sc, PHY_BMCR, media);	} else {		if (verbose)			printf("no carrier\n");	}	mx_init(sc);	if (sc->mx_tx_pend) {		sc->mx_autoneg = 0;		sc->mx_tx_pend = 0;		mx_start(ifp);	}	return;}/* * Invoke autoneg using internal NWAY. */static void mx_autoneg(sc, flag, verbose)	struct mx_softc		*sc;	int			flag;	int			verbose;{	u_int32_t		media, ability;	struct ifnet		*ifp;	struct ifmedia		*ifm;	ifm = &sc->ifmedia;	ifp = &sc->arpcom.ac_if;	ifm->ifm_media = IFM_ETHER | IFM_AUTO;	switch (flag) {	case MX_FLAG_FORCEDELAY:		/*	 	 * XXX Never use this option anywhere but in the probe	 	 * routine: making the kernel stop dead in its tracks 		 * for three whole seconds after we've gone multi-user		 * is really bad manners.	 	 */		MX_CLRBIT(sc, MX_NETCFG, MX_NETCFG_PORTSEL);		MX_SETBIT(sc, MX_NETCFG, MX_NETCFG_FULLDUPLEX);		MX_SETBIT(sc, MX_10BTCTRL, MX_TCTL_AUTONEGENBL);		MX_SETBIT(sc, MX_10BTCTRL, MX_ASTAT_TXDISABLE);		DELAY(5000000);		break;	case MX_FLAG_SCHEDDELAY:		/*		 * Wait for the transmitter to go idle before starting		 * an autoneg session, otherwise mx_start() may clobber	 	 * our timeout, and we don't want to allow transmission		 * during an autoneg session since that can screw it up.	 	 */		if (sc->mx_cdata.mx_tx_head != NULL) {			sc->mx_want_auto = 1;			return;		}		MX_CLRBIT(sc, MX_NETCFG, MX_NETCFG_PORTSEL);		MX_SETBIT(sc, MX_NETCFG, MX_NETCFG_FULLDUPLEX);		MX_SETBIT(sc, MX_10BTCTRL, MX_TCTL_AUTONEGENBL);		MX_SETBIT(sc, MX_10BTCTRL, MX_ASTAT_TXDISABLE);		ifp->if_timer = 5;		sc->mx_autoneg = 1;		sc->mx_want_auto = 0;		return;		break;	case MX_FLAG_DELAYTIMEO:		ifp->if_timer = 0;		sc->mx_autoneg = 0;		break;	default:		printf("mx%d: invalid autoneg flag: %d\n", sc->mx_unit, flag);		return;	}	if ((CSR_READ_4(sc, MX_10BTSTAT) & MX_TSTAT_ANEGSTAT) ==						MX_ASTAT_AUTONEGCMP) {		if (verbose)			printf("mx%d: autoneg complete, ", sc->mx_unit);	} else {		if (verbose)			printf("mx%d: autoneg not complete, ", sc->mx_unit);	}	media = CSR_READ_4(sc, MX_NETCFG);	/* Link is good. Report modes and set duplex mode. */	if (!(CSR_READ_4(sc, MX_10BTSTAT) & MX_TSTAT_LS10) ||		!(CSR_READ_4(sc, MX_10BTSTAT) & MX_TSTAT_LS100)) {		if (verbose)			printf("link status good ");		ability = CSR_READ_4(sc, MX_NWAYSTAT);		if (ability & MX_NWAY_100BT4) {			ifm->ifm_media = IFM_ETHER|IFM_100_T4;			media |= MX_NETCFG_PORTSEL|MX_NETCFG_PCS|					MX_NETCFG_SCRAMBLER;			media &= ~(MX_NETCFG_FULLDUPLEX|MX_NETCFG_SPEEDSEL);			printf("(100baseT4)\n");		} else if (ability & MX_NWAY_100BTFULL) {			ifm->ifm_media = IFM_ETHER|IFM_100_TX|IFM_FDX;			media |= MX_NETCFG_PORTSEL|MX_NETCFG_PCS|					MX_NETCFG_SCRAMBLER;			media |= MX_NETCFG_FULLDUPLEX;			media &= ~MX_NETCFG_SPEEDSEL;			printf("(full-duplex, 100Mbps)\n");		} else if (ability & MX_NWAY_100BTHALF) {			ifm->ifm_media = IFM_ETHER|IFM_100_TX|IFM_HDX;			media |= MX_NETCFG_PORTSEL|MX_NETCFG_PCS|					MX_NETCFG_SCRAMBLER;			media &= ~(MX_NETCFG_FULLDUPLEX|MX_NETCFG_SPEEDSEL);			printf("(half-duplex, 100Mbps)\n");		} else if (ability & MX_NWAY_10BTFULL) {			ifm->ifm_media = IFM_ETHER|IFM_10_T|IFM_FDX;			media &= ~MX_NETCFG_PORTSEL;			media |= (MX_NETCFG_FULLDUPLEX|MX_NETCFG_SPEEDSEL);			printf("(full-duplex, 10Mbps)\n");		} else {			ifm->ifm_media = IFM_ETHER|IFM_10_T|IFM_HDX;			media &= ~MX_NETCFG_PORTSEL;			media &= ~MX_NETCFG_FULLDUPLEX;			media |= MX_NETCFG_SPEEDSEL;			printf("(half-duplex, 10Mbps)\n");		}		CSR_WRITE_4(sc, MX_NETCFG, media);		MX_CLRBIT(sc, MX_10BTCTRL, MX_TCTL_AUTONEGENBL);	} else {		if (verbose)			printf("no carrier\n");	}	mx_init(sc);	if (sc->mx_tx_pend) {		sc->mx_autoneg = 0;		sc->mx_tx_pend = 0;		mx_start(ifp);	}	return;}static void mx_getmode_mii(sc)	struct mx_softc		*sc;{	u_int16_t		bmsr;	struct ifnet		*ifp;	ifp = &sc->arpcom.ac_if;	bmsr = mx_phy_readreg(sc, PHY_BMSR);	if (bootverbose)		printf("mx%d: PHY status word: %x\n", sc->mx_unit, bmsr);	/* fallback */	sc->ifmedia.ifm_media = IFM_ETHER|IFM_10_T|IFM_HDX;	if (bmsr & PHY_BMSR_10BTHALF) {		if (bootverbose)			printf("mx%d: 10Mbps half-duplex mode supported\n",								sc->mx_unit);		ifmedia_add(&sc->ifmedia,			IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL);		ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL);	}	if (bmsr & PHY_BMSR_10BTFULL) {		if (bootverbose)			printf("mx%d: 10Mbps full-duplex mode supported\n",								sc->mx_unit);		ifmedia_add(&sc->ifmedia,			IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL);		sc->ifmedia.ifm_media = IFM_ETHER|IFM_10_T|IFM_FDX;	}	if (bmsr & PHY_BMSR_100BTXHALF) {		if (bootverbose)			printf("mx%d: 100Mbps half-duplex mode supported\n",								sc->mx_unit);		ifp->if_baudrate = 100000000;		ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_TX, 0, NULL);		ifmedia_add(&sc->ifmedia,			IFM_ETHER|IFM_100_TX|IFM_HDX, 0, NULL);		sc->ifmedia.ifm_media = IFM_ETHER|IFM_100_TX|IFM_HDX;	}	if (bmsr & PHY_BMSR_100BTXFULL) {		if (bootverbose)			printf("mx%d: 100Mbps full-duplex mode supported\n",								sc->mx_unit);		ifp->if_baudrate = 100000000;		ifmedia_add(&sc->ifmedia,			IFM_ETHER|IFM_100_TX|IFM_FDX, 0, NULL);		sc->ifmedia.ifm_media = IFM_ETHER|IFM_100_TX|IFM_FDX;	}	/* Some also support 100BaseT4. */	if (bmsr & PHY_BMSR_100BT4) {		if (bootverbose)			printf("mx%d: 100baseT4 mode supported\n", sc->mx_unit);		ifp->if_baudrate = 100000000;		ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_T4, 0, NULL);		sc->ifmedia.ifm_media = IFM_ETHER|IFM_100_T4;#ifdef FORCE_AUTONEG_TFOUR		if (bootverbose)			printf("mx%d: forcing on autoneg support for BT4\n",							 sc->mx_unit);		ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_AUTO, 0 NULL):		sc->ifmedia.ifm_media = IFM_ETHER|IFM_AUTO;#endif	}	if (bmsr & PHY_BMSR_CANAUTONEG) {		if (bootverbose)			printf("mx%d: autoneg supported\n", sc->mx_unit);		ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_AUTO, 0, NULL);		sc->ifmedia.ifm_media = IFM_ETHER|IFM_AUTO;	}	return;}/* * Set speed and duplex mode. */static void mx_setmode_mii(sc, media)	struct mx_softc		*sc;	int			media;{	u_int16_t		bmcr;	struct ifnet		*ifp;	ifp = &sc->arpcom.ac_if;	/*	 * If an autoneg session is in progress, stop it.	 */	if (sc->mx_autoneg) {		printf("mx%d: canceling autoneg session\n", sc->mx_unit);		ifp->if_timer = sc->mx_autoneg = sc->mx_want_auto = 0;		bmcr = mx_phy_readreg(sc, PHY_BMCR);		bmcr &= ~PHY_BMCR_AUTONEGENBL;		mx_phy_writereg(sc, PHY_BMCR, bmcr);	}	printf("mx%d: selecting MII, ", sc->mx_unit);	bmcr = mx_phy_readreg(sc, PHY_BMCR);	bmcr &= ~(PHY_BMCR_AUTONEGENBL|PHY_BMCR_SPEEDSEL|			PHY_BMCR_DUPLEX|PHY_BMCR_LOOPBK);	if (IFM_SUBTYPE(media) == IFM_100_T4) {		printf("100Mbps/T4, half-duplex\n");		bmcr |= PHY_BMCR_SPEEDSEL;		bmcr &= ~PHY_BMCR_DUPLEX;	}	if (IFM_SUBTYPE(media) == IFM_100_TX) {		printf("100Mbps, ");		bmcr |= PHY_BMCR_SPEEDSEL;	}	if (IFM_SUBTYPE(media) == IFM_10_T) {		printf("10Mbps, ");		bmcr &= ~PHY_BMCR_SPEEDSEL;	}	if ((media & IFM_GMASK) == IFM_FDX) {		printf("full duplex\n");		bmcr |= PHY_BMCR_DUPLEX;	} else {		printf("half duplex\n");		bmcr &= ~PHY_BMCR_DUPLEX;	}	mx_setcfg(sc, bmcr);	mx_phy_writereg(sc, PHY_BMCR, bmcr);	return;}/* * Set speed and duplex mode on internal transceiver. */static void mx_setmode(sc, media, verbose)	struct mx_softc		*sc;	int			media;	int			verbose;{	struct ifnet		*ifp;	u_int32_t		mode;	ifp = &sc->arpcom.ac_if;	/*	 * If an autoneg session is in progress, stop it.	 */	if (sc->mx_autoneg) {		printf("mx%d: canceling autoneg session\n", sc->mx_unit);		ifp->if_timer = sc->mx_autoneg = sc->mx_want_auto = 0;		MX_CLRBIT(sc, MX_10BTCTRL, MX_TCTL_AUTONEGENBL);	}	if (verbose)		printf("mx%d: selecting NWAY, ", sc->mx_unit);	mode = CSR_READ_4(sc, MX_NETCFG);	mode &= ~(MX_NETCFG_FULLDUPLEX|MX_NETCFG_PORTSEL|		MX_NETCFG_PCS|MX_NETCFG_SCRAMBLER|MX_NETCFG_SPEEDSEL);	if (IFM_SUBTYPE(media) == IFM_100_T4) {		if (verbose)			printf("100Mbps/T4, half-duplex\n");		mode |= MX_NETCFG_PORTSEL|MX_NETCFG_PCS|MX_NETCFG_SCRAMBLER;	}	if (IFM_SUBTYPE(media) == IFM_100_TX) {		if (verbose)			printf("100Mbps, ");		mode |= MX_NETCFG_PORTSEL|MX_NETCFG_PCS|MX_NETCFG_SCRAMBLER;	}	if (IFM_SUBTYPE(media) == IFM_10_T) {		if (verbose)			printf("10Mbps, ");		mode &= ~MX_NETCFG_PORTSEL;		mode |= MX_NETCFG_SPEEDSEL;	}	if ((media & IFM_GMASK) == IFM_FDX) {		if (verbose)			printf("full duplex\n");		mode |= MX_NETCFG_FULLDUPLEX;	} else {		if (verbose)			printf("half duplex\n");		mode &= ~MX_NETCFG_FULLDUPLEX;	}	CSR_WRITE_4(sc, MX_NETCFG, mode);	return;}/* * Programming the receiver filter on the tulip/PMAC is gross. You * have to construct a special setup frame and download it to the * chip via the transmit DMA engine. This routine is also somewhat * gross, as the setup frame is sent synchronously rather than putting * on the transmit queue. The transmitter has to be stopped, then we * can download the frame and wait for the 'owned' bit to clear. * * We always program the chip using 'hash perfect' mode, i.e. one perfect * address (our node address) and a 512-bit hash filter for multicast * frames. We also sneak the broadcast address into the hash filter since * we need that too. */void mx_setfilt(sc)	struct mx_softc		*sc;{	struct mx_desc		*sframe;	u_int32_t		h, *sp;	struct ifmultiaddr	*ifma;	struct ifnet		*ifp;	int			i;	ifp = &sc->arpcom.ac_if;	MX_CLRBIT(sc, MX_NETCFG, MX_NETCFG_TX_ON);	MX_SETBIT(sc, MX_ISR, MX_ISR_TX_IDLE);	sframe = &sc->mx_cdata.mx_sframe;	sp = (u_int32_t *)&sc->mx_cdata.mx_sbuf;	bzero((char *)sp, MX_SFRAME_LEN);	sframe->mx_next = vtophys(&sc->mx_ldata->mx_tx_list[0]);	sframe->mx_data = vtophys(&sc->mx_cdata.mx_sbuf);	sframe->mx_ctl = MX_SFRAME_LEN | MX_TXCTL_TLINK |			MX_TXCTL_SETUP | MX_FILTER_HASHPERF;	/* If we want promiscuous mode, set the allframes bit. */	if (ifp->if_flags & IFF_PROMISC)		MX_SETBIT(sc, MX_NETCFG, MX_NETCFG_RX_PROMISC);	else		MX_CLRBIT(sc, MX_NETCFG, MX_NETCFG_RX_PROMISC);	if (ifp->if_flags & IFF_ALLMULTI)		MX_SETBIT(sc, MX_NETCFG, MX_NETCFG_RX_ALLMULTI);	for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL;				ifma = ifma->ifma_link.le_next) {		if (ifma->ifma_addr->sa_family != AF_LINK)			continue;		h = mx_calchash(LLADDR((struct sockaddr_dl *)ifma->ifma_addr));		sp[h >> 4] |= 1 << (h & 0xF);	}	if (ifp->if_flags & IFF_BROADCAST) {		h = mx_calchash((caddr_t)&etherbroadcastaddr);		sp[h >> 4] |= 1 << (h & 0xF);	}	sp[39] = ((u_int16_t *)sc->arpcom.ac_enaddr)[0];	sp[40] = ((u_int16_t *)sc->arpcom.ac_enaddr)[1];	sp[41] = ((u_int16_t *)sc->arpcom.ac_enaddr)[2];	CSR_WRITE_4(sc, MX_TXADDR, vtophys(sframe));	MX_SETBIT(sc, MX_NETCFG, MX_NETCFG_TX_ON);	sframe->mx_status = MX_TXSTAT_OWN;	CSR_WRITE_4(sc, MX_TXSTART, 0xFFFFFFFF);	/*	 * Wait for chip to clear the 'own' bit.	 */	for (i = 0; i < MX_TIMEOUT; i++) {		DELAY(10);		if (sframe->mx_status != MX_TXSTAT_OWN)			break;	}	if (i == MX_TIMEOUT)		printf("mx%d: failed to send setup frame\n", sc->mx_unit);	MX_SETBIT(sc, MX_ISR, MX_ISR_TX_NOBUF|MX_ISR_TX_IDLE);	return;}/* * In order to fiddle with the * 'full-duplex' and '100Mbps' bits in the netconfig register, we * first have to put the transmit and/or receive logic in the idle state. */static void mx_setcfg(sc, bmcr)	struct mx_softc		*sc;	int			bmcr;{	int			i, restart = 0;	if (CSR_READ_4(sc, MX_NETCFG) & (MX_NETCFG_TX_ON|MX_NETCFG_RX_ON)) {		restart = 1;		MX_CLRBIT(sc, MX_NETCFG, (MX_NETCFG_TX_ON|MX_NETCFG_RX_ON));		for (i = 0; i < MX_TIMEOUT; i++) {			DELAY(10);			if (CSR_READ_4(sc, MX_ISR) & MX_ISR_TX_IDLE)				break;		}		if (i == MX_TIMEOUT)			printf("mx%d: failed to force tx and "				"rx to idle state\n", sc->mx_unit);	}	if (bmcr & PHY_BMCR_SPEEDSEL) {		MX_CLRBIT(sc, MX_NETCFG, MX_NETCFG_SPEEDSEL);		if (sc->mx_phy_addr == 0) {			MX_SETBIT(sc, MX_NETCFG, MX_NETCFG_PORTSEL|				MX_NETCFG_PCS|MX_NETCFG_SCRAMBLER);		}	} else		MX_SETBIT(sc, MX_NETCFG, MX_NETCFG_SPEEDSEL);	if (bmcr & PHY_BMCR_DUPLEX)		MX_SETBIT(sc, MX_NETCFG, MX_NETCFG_FULLDUPLEX);	else		MX_CLRBIT(sc, MX_NETCFG, MX_NETCFG_FULLDUPLEX);	if (restart)		MX_SETBIT(sc, MX_NETCFG, MX_NETCFG_TX_ON|MX_NETCFG_RX_ON);	return;}static void mx_reset(sc)	struct mx_softc		*sc;{	register int		i;	MX_SETBIT(sc, MX_BUSCTL, MX_BUSCTL_RESET);	for (i = 0; i < MX_TIMEOUT; i++) {		DELAY(10);		if (!(CSR_READ_4(sc, MX_BUSCTL) & MX_BUSCTL_RESET))			break;	}	if (i == MX_TIMEOUT)		printf("mx%d: reset never completed!\n", sc->mx_unit);	/* Wait a little while for the chip to get its brains in order. */	DELAY(1000);        return;}/* * Probe for a Macronix PMAC chip. Check the PCI vendor and device * IDs against our list and return a device name if we find a match. * We do a little bit of extra work to identify the exact type of * chip. The MX98713 and MX98713A have the same PCI vendor/device ID, * but different revision IDs. The same is true for 98715/98715A * chips and the 98725. This doesn't affect a whole lot, but it * lets us tell the user exactly what type of device they have * in the probe output. */static const char *mx_probe(config_id, device_id)	pcici_t			config_id;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?