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 + -
显示快捷键?