if_tl.c

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

C
2,302
字号
	/*	 * Make sure we're in TX mode.	 */	tl_dio_setbit(sc, TL_NETSIO, TL_SIO_ETXEN);	/*	 * Feed in each bit and stobe the clock.	 */	for (i = 0x80; i; i >>= 1) {		if (byte & i) {			tl_dio_setbit(sc, TL_NETSIO, TL_SIO_EDATA);		} else {			tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_EDATA);		}		DELAY(1);		tl_dio_setbit(sc, TL_NETSIO, TL_SIO_ECLOK);		DELAY(1);		tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ECLOK);	}	/*	 * Turn off TX mode.	 */	tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ETXEN);	/*	 * Check for ack.	 */	tl_dio_setbit(sc, TL_NETSIO, TL_SIO_ECLOK);	ack = tl_dio_read8(sc, TL_NETSIO) & TL_SIO_EDATA;	tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ECLOK);	return(ack);}/* * Read a byte of data stored in the EEPROM at address 'addr.' */static u_int8_t tl_eeprom_getbyte(sc, addr, dest)	struct tl_softc		*sc;	int			addr;	u_int8_t		*dest;{	register int		i;	u_int8_t		byte = 0;	tl_dio_write8(sc, TL_NETSIO, 0);	EEPROM_START;	/*	 * Send write control code to EEPROM.	 */	if (tl_eeprom_putbyte(sc, EEPROM_CTL_WRITE)) {		printf("tl%d: failed to send write command, status: %x\n",				sc->tl_unit, tl_dio_read8(sc, TL_NETSIO));		return(1);	}	/*	 * Send address of byte we want to read.	 */	if (tl_eeprom_putbyte(sc, addr)) {		printf("tl%d: failed to send address, status: %x\n",				sc->tl_unit, tl_dio_read8(sc, TL_NETSIO));		return(1);	}	EEPROM_STOP;	EEPROM_START;	/*	 * Send read control code to EEPROM.	 */	if (tl_eeprom_putbyte(sc, EEPROM_CTL_READ)) {		printf("tl%d: failed to send write command, status: %x\n",				sc->tl_unit, tl_dio_read8(sc, TL_NETSIO));		return(1);	}	/*	 * Start reading bits from EEPROM.	 */	tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ETXEN);	for (i = 0x80; i; i >>= 1) {		tl_dio_setbit(sc, TL_NETSIO, TL_SIO_ECLOK);		DELAY(1);		if (tl_dio_read8(sc, TL_NETSIO) & TL_SIO_EDATA)			byte |= i;		tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ECLOK);		DELAY(1);	}	EEPROM_STOP;	/*	 * No ACK generated for read, so just return byte.	 */	*dest = byte;	return(0);}/* * Read a sequence of bytes from the EEPROM. */static int tl_read_eeprom(sc, dest, off, cnt)	struct tl_softc		*sc;	caddr_t			dest;	int			off;	int			cnt;{	int			err = 0, i;	u_int8_t		byte = 0;	for (i = 0; i < cnt; i++) {		err = tl_eeprom_getbyte(sc, off + i, &byte);		if (err)			break;		*(dest + i) = byte;	}	return(err ? 1 : 0);}static void tl_mii_sync(sc)	struct tl_softc		*sc;{	register int		i;	tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MTXEN);	for (i = 0; i < 32; i++) {		tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK);		tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK);	}	return;}static void tl_mii_send(sc, bits, cnt)	struct tl_softc		*sc;	u_int32_t		bits;	int			cnt;{	int			i;	for (i = (0x1 << (cnt - 1)); i; i >>= 1) {		tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK);		if (bits & i) {			tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MDATA);		} else {			tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MDATA);		}		tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK);	}}static int tl_mii_readreg(sc, frame)	struct tl_softc		*sc;	struct tl_mii_frame	*frame;	{	int			i, ack, s;	int			minten = 0;	s = splimp();	tl_mii_sync(sc);	/*	 * Set up frame for RX.	 */	frame->mii_stdelim = TL_MII_STARTDELIM;	frame->mii_opcode = TL_MII_READOP;	frame->mii_turnaround = 0;	frame->mii_data = 0;		/*	 * Turn off MII interrupt by forcing MINTEN low.	 */	minten = tl_dio_read8(sc, TL_NETSIO) & TL_SIO_MINTEN;	if (minten) {		tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MINTEN);	}	/* 	 * Turn on data xmit.	 */	tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MTXEN);	/*	 * Send command/address info.	 */	tl_mii_send(sc, frame->mii_stdelim, 2);	tl_mii_send(sc, frame->mii_opcode, 2);	tl_mii_send(sc, frame->mii_phyaddr, 5);	tl_mii_send(sc, frame->mii_regaddr, 5);	/*	 * Turn off xmit.	 */	tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MTXEN);	/* Idle bit */	tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK);	tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK);	/* Check for ack */	tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK);	ack = tl_dio_read8(sc, TL_NETSIO) & TL_SIO_MDATA;	/* Complete the cycle */	tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK);	/*	 * Now try reading data bits. If the ack failed, we still	 * need to clock through 16 cycles to keep the PHYs in sync.	 */	if (ack) {		for(i = 0; i < 16; i++) {			tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK);			tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK);		}		goto fail;	}	for (i = 0x8000; i; i >>= 1) {		tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK);		if (!ack) {			if (tl_dio_read8(sc, TL_NETSIO) & TL_SIO_MDATA)				frame->mii_data |= i;		}		tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK);	}fail:	tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK);	tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK);	/* Reenable interrupts */	if (minten) {		tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MINTEN);	}	splx(s);	if (ack)		return(1);	return(0);}static int tl_mii_writereg(sc, frame)	struct tl_softc		*sc;	struct tl_mii_frame	*frame;	{	int			s;	int			minten;	tl_mii_sync(sc);	s = splimp();	/*	 * Set up frame for TX.	 */	frame->mii_stdelim = TL_MII_STARTDELIM;	frame->mii_opcode = TL_MII_WRITEOP;	frame->mii_turnaround = TL_MII_TURNAROUND;		/*	 * Turn off MII interrupt by forcing MINTEN low.	 */	minten = tl_dio_read8(sc, TL_NETSIO) & TL_SIO_MINTEN;	if (minten) {		tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MINTEN);	}	/* 	 * Turn on data output.	 */	tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MTXEN);	tl_mii_send(sc, frame->mii_stdelim, 2);	tl_mii_send(sc, frame->mii_opcode, 2);	tl_mii_send(sc, frame->mii_phyaddr, 5);	tl_mii_send(sc, frame->mii_regaddr, 5);	tl_mii_send(sc, frame->mii_turnaround, 2);	tl_mii_send(sc, frame->mii_data, 16);	tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK);	tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK);	/*	 * Turn off xmit.	 */	tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MTXEN);	/* Reenable interrupts */	if (minten)		tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MINTEN);	splx(s);	return(0);}static u_int16_t tl_phy_readreg(sc, reg)	struct tl_softc		*sc;	int			reg;{	struct tl_mii_frame	frame;	bzero((char *)&frame, sizeof(frame));	frame.mii_phyaddr = sc->tl_phy_addr;	frame.mii_regaddr = reg;	tl_mii_readreg(sc, &frame);	/* Reenable MII interrupts, just in case. */	tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MINTEN);	return(frame.mii_data);}static void tl_phy_writereg(sc, reg, data)	struct tl_softc		*sc;	int			reg;	int			data;{	struct tl_mii_frame	frame;	bzero((char *)&frame, sizeof(frame));	frame.mii_phyaddr = sc->tl_phy_addr;	frame.mii_regaddr = reg;	frame.mii_data = data;	tl_mii_writereg(sc, &frame);	/* Reenable MII interrupts, just in case. */	tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MINTEN);	return;}/* * Initiate autonegotiation with a link partner. * * Note that the Texas Instruments ThunderLAN programmer's guide * fails to mention one very important point about autonegotiation. * Autonegotiation is done largely by the PHY, independent of the * ThunderLAN chip itself: the PHY sets the flags in the BMCR * register to indicate what modes were selected and if link status * is good. In fact, the PHY does pretty much all of the work itself, * except for one small detail. * * The PHY may negotiate a full-duplex of half-duplex link, and set * the PHY_BMCR_DUPLEX bit accordingly, but the ThunderLAN's 'NetCommand' * register _also_ has a half-duplex/full-duplex bit, and you MUST ALSO * SET THIS BIT MANUALLY TO CORRESPOND TO THE MODE SELECTED FOR THE PHY! * In other words, both the ThunderLAN chip and the PHY have to be * programmed for full-duplex mode in order for full-duplex to actually * work. So in order for autonegotiation to really work right, we have * to wait for the link to come up, check the BMCR register, then set * the ThunderLAN for full or half-duplex as needed. * * I struggled for two days to figure this out, so I'm making a point * of drawing attention to this fact. I think it's very strange that * the ThunderLAN doesn't automagically track the duplex state of the * PHY, but there you have it. * * Also when, using a National Semiconductor DP83840A PHY, we have to * allow a full three seconds for autonegotiation to complete. So what * we do is flip the autonegotiation restart bit, then set a timeout * to wake us up in three seconds to check the link state. * * Note that there are some versions of the Olicom 2326 that use a * Micro Linear ML6692 100BaseTX PHY. This particular PHY is designed * to provide 100BaseTX support only, but can be used with a controller * that supports an internal 10Mbps PHY to provide a complete * 10/100Mbps solution. However, the ML6692 does not have vendor and * device ID registers, and hence always shows up with a vendor/device * ID of 0. * * We detect this configuration by checking the phy vendor ID in the * softc structure. If it's a zero, and we're negotiating a high-speed * mode, then we turn off the internal PHY. If it's a zero and we've * negotiated a high-speed mode, we turn on the internal PHY. Note * that to make things even more fun, we have to make extra sure that * the loopback bit in the internal PHY's control register is turned * off. */static void tl_autoneg(sc, flag, verbose)	struct tl_softc		*sc;	int			flag;	int			verbose;{	u_int16_t		phy_sts = 0, media = 0, advert, ability;	struct ifnet		*ifp;	struct ifmedia		*ifm;	ifm = &sc->ifmedia;	ifp = &sc->arpcom.ac_if;	/*	 * First, see if autoneg is supported. If not, there's	 * no point in continuing.	 */	phy_sts = tl_phy_readreg(sc, PHY_BMSR);	if (!(phy_sts & PHY_BMSR_CANAUTONEG)) {		if (verbose)			printf("tl%d: autonegotiation not supported\n",							sc->tl_unit);		return;	}	switch (flag) {	case TL_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.	 	 */		tl_phy_writereg(sc, PHY_BMCR, PHY_BMCR_RESET);		DELAY(500);		phy_sts = tl_phy_readreg(sc, PHY_BMCR);		phy_sts |= PHY_BMCR_AUTONEGENBL|PHY_BMCR_AUTONEGRSTR;		tl_phy_writereg(sc, PHY_BMCR, phy_sts);		DELAY(5000000);		break;	case TL_FLAG_SCHEDDELAY:		/*		 * Wait for the transmitter to go idle before starting		 * an autoneg session, otherwise tl_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->tl_txeoc) {			sc->tl_want_auto = 1;			return;		}		tl_phy_writereg(sc, PHY_BMCR, PHY_BMCR_RESET);		DELAY(500);		phy_sts = tl_phy_readreg(sc, PHY_BMCR);		phy_sts |= PHY_BMCR_AUTONEGENBL|PHY_BMCR_AUTONEGRSTR;		tl_phy_writereg(sc, PHY_BMCR, phy_sts);		ifp->if_timer = 5;		sc->tl_autoneg = 1;		sc->tl_want_auto = 0;		return;	case TL_FLAG_DELAYTIMEO:		ifp->if_timer = 0;		sc->tl_autoneg = 0;		break;	default:		printf("tl%d: invalid autoneg flag: %d\n", sc->tl_unit, flag);

⌨️ 快捷键说明

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