if_i82544.c

来自「开放源码实时操作系统源码.」· C语言 代码 · 共 2,053 行 · 第 1/5 页

C
2,053
字号

#else // ! CYGHWR_DEVS_ETH_INTEL_I82544_GET_ESA

#ifndef CYGHWR_DEVS_ETH_INTEL_I82544_HAS_NO_EEPROM
        int addr_length, i;
        cyg_uint16 checksum;

        // read eeprom and get 82544's mac address
        addr_length = get_eeprom_size(p_i82544);
        // (this is the length of the *EEPROM*s address, not MAC address)

        // If length is 1, it _probably_ means there's no EEPROM
        // present.  Couldn't find an explicit mention of this in the
        // docs, but length=1 appears to be the behaviour in that case.
        if (1 == addr_length) {
#ifdef DEBUG_EE
            os_printf("Error: No EEPROM present for device %d\n", 
                      p_i82544->index);
#endif
        } else {
            for (checksum = 0, i = 0, count = 0; count < 64; count++) {
                cyg_uint16 value;
                // read word from eeprom
                value = read_eeprom_word(p_i82544, addr_length, count);
#ifdef DEBUG_EE
                os_printf( "%02x: %04x\n", count, value );
#endif
                checksum += value;
                if (count < 3) {
                    mac_address[i++] = value & 0xFF;
                    mac_address[i++] = (value >> 8) & 0xFF;
                }
            }

#ifndef CYGHWR_DEVS_ETH_INTEL_I82544_HAS_ONE_EEPROM_WITHOUT_CRC
            // If the EEPROM checksum is wrong, the MAC address read
            // from the EEPROM is probably wrong as well. In that
            // case, we don't set mac_addr_ok, but continue the
            // initialization. If then somebody calls i82544_start
            // without calling eth_set_mac_address() first, we refuse
            // to bring up the interface, because running with an
            // invalid MAC address is not a very brilliant idea.
        
            if ((checksum & 0xFFFF) != 0xBABA)  {
                // selftest verified checksum, verify again
#ifdef DEBUG_EE
                os_printf("Warning: Invalid EEPROM checksum %04X for device %d\n",
                          checksum, p_i82544->index);
#endif
            } else // trailing block
#endif
            {
                p_i82544->mac_addr_ok = 1;
#ifdef DEBUG_EE
                os_printf("Valid EEPROM checksum\n");
#endif
		// Second port of dual-port 82546 uses EEPROM ESA | 1
		if (p_i82544->device == 0x1010 ||
                    p_i82544->device == 0x100e) {
		    cyg_uint8 devfn = CYG_PCI_DEV_GET_DEVFN(p_i82544->devid);
		    if (CYG_PCI_DEV_GET_FN(devfn) == 1)
			mac_address[5] |= 1;
		}
                eth_set_mac_address(p_i82544, mac_address, 0);
            }
        }

        // record the MAC address in the device structure
        p_i82544->mac_address[0] = mac_address[0];
        p_i82544->mac_address[1] = mac_address[1];
        p_i82544->mac_address[2] = mac_address[2];
        p_i82544->mac_address[3] = mac_address[3];
        p_i82544->mac_address[4] = mac_address[4];
        p_i82544->mac_address[5] = mac_address[5];

#endif // ! CYGHWR_DEVS_ETH_INTEL_I82544_HAS_NO_EEPROM
#endif // ! CYGHWR_DEVS_ETH_INTEL_I82544_GET_ESA
    }

#ifdef DEBUG
    os_printf("i82544_init: MAC Address = %02X %02X %02X %02X %02X %02X\n",
              p_i82544->mac_address[0], p_i82544->mac_address[1],
              p_i82544->mac_address[2], p_i82544->mac_address[3],
              p_i82544->mac_address[4], p_i82544->mac_address[5]);
#endif
    
    // and record the net dev pointer
    p_i82544->ndp = (void *)ndp;
    
    p_i82544->within_send = 0; // init recursion level

    // Initialize upper level driver
    if ( p_i82544->mac_addr_ok )
        (sc->funs->eth_drv->init)(sc, &(p_i82544->mac_address[0]) );
    else
    {
        (sc->funs->eth_drv->init)(sc, NULL );
    }


#ifdef DEBUG

    os_printf("CTRL %08x\n",INL( ioaddr + I82544_CTRL ));    
    os_printf("STATUS %08x\n",INL( ioaddr + I82544_STATUS ));    
    
#endif    
    
    return (1);
}

// ------------------------------------------------------------------------
//
//  Function : i82544_setup
//
// ------------------------------------------------------------------------

static void 
i82544_setup( struct i82544 *p_i82544 )
{
    cyg_uint32 ioaddr;    
    cyg_uint32 ctrl;
#ifndef CYGHWR_DEVS_ETH_INTEL_I82544_USE_ASD
    cyg_uint32 ctrl_ext;
#endif

    ioaddr = p_i82544->io_address; // get 82544's I/O address

#ifdef CYGHWR_DEVS_ETH_INTEL_I82544_USE_ASD
    // Use Auto-negotiation

    ctrl = INL( ioaddr + I82544_CTRL );
    
    // Set link up bit
    ctrl |= I82544_CTRL_SLU | I82544_CTRL_ASDE;
    ctrl &= ~(I82544_CTRL_ILOS | I82544_CTRL_FRCSPD | I82544_CTRL_FRCDPLX);
    OUTL( ctrl, ioaddr + I82544_CTRL );
    udelay(20);

    // we can assume link is up with autonegotiation
    p_i82544->link = 1;

    // wait up to 5 seconds for link to come up
    {
	int delay_cnt = 500;
	while ((mii_read_register( p_i82544, PHY_ADDRESS, 1 ) & 0x4) == 0) {
	    udelay(10000);
	    if (--delay_cnt <= 0)
		break;
	}
    }
    
#else
    // The following sequence of resets and bit twiddling seem to be
    // necessary to get the 82543 working. Not sure what is necessary
    // for the 82544.

    ctrl = INL( ioaddr + I82544_CTRL );
    
    // Set link up bit
    ctrl |= I82544_CTRL_SLU;    
    ctrl &= ~I82544_CTRL_ILOS;
    OUTL( ctrl, ioaddr + I82544_CTRL );
    udelay(20);

    // Force PHY physical reset
    // We can only access the PHY after we have done this.

    ctrl_ext = INL( ioaddr + I82544_CTRL_EXT );
    ctrl_ext |= I82544_CTRL_EXT_PHY_RESET_DIR4;
    OUTL( ctrl_ext, ioaddr + I82544_CTRL_EXT );
    udelay( 20000 );

    ctrl_ext = INL( ioaddr + I82544_CTRL_EXT );    
    ctrl_ext &= ~I82544_CTRL_EXT_PHY_RESET4;
    OUTL( ctrl_ext, ioaddr + I82544_CTRL_EXT );
    udelay( 20000 );

    ctrl_ext = INL( ioaddr + I82544_CTRL_EXT );    
    ctrl_ext |= I82544_CTRL_EXT_PHY_RESET4;
    OUTL( ctrl_ext, ioaddr + I82544_CTRL_EXT );
    udelay( 20000 );

#ifdef DEBUG    
    show_phy( p_i82544, PHY_ADDRESS );
#endif
    
#if 0
    // Reset PHY
    // Does not appear to be necessary.
    {
        cyg_uint16 phy_ctrl;        
        phy_ctrl = mii_read_register( p_i82544, PHY_ADDRESS, 0 );
        phy_ctrl = 0x9000;
//        os_printf("PHY ctrl %04x\n",phy_ctrl);
        mii_write_register( p_i82544, PHY_ADDRESS, 0, phy_ctrl );
        do {
            phy_ctrl = mii_read_register( p_i82544, PHY_ADDRESS, 0 );
//            os_printf("PHY ctrl %04x\n",phy_ctrl);
        } while( phy_ctrl & 0x8000 );
    }
    show_phy( p_i82544, PHY_ADDRESS );
#endif

#if 0
    // Tinker with PHY configuration.
    // Only on 82543? May not be necessary at all, since disabling
    // this does not see to make any difference.
    {
        cyg_uint16 data;

        // Set CRS on Tx bit in PHY specific CR
        data = mii_read_register( p_i82544, PHY_ADDRESS, 16 );
        os_printf("PSCR %04x\n",data);
        data |= 0x0800;
        mii_write_register( p_i82544, PHY_ADDRESS, 16, data );

        // Set TX clock to 25MHz in PHY extended CR
        data = mii_read_register( p_i82544, PHY_ADDRESS, 20 );
        os_printf("PSECR %04x\n",data);
        data |= 0x0070;
        mii_write_register( p_i82544, PHY_ADDRESS, 20, data );
    }
    show_phy( p_i82544, PHY_ADDRESS );
#endif
    
#if 1
    // Force speed renegotiation.

    {
        cyg_uint16 phy_ctrl;
        cyg_uint16 phy_stat;
	int delay_cnt = 100 * 5;  // wait five seconds, then give up
        
	p_i82544->link = 0;
        phy_ctrl = mii_read_register( p_i82544, PHY_ADDRESS, 0 );
        phy_ctrl |= 0x1200;
//        os_printf("PHY ctrl %04x\n",phy_ctrl);
        mii_write_register( p_i82544, PHY_ADDRESS, 0, phy_ctrl );
        // Wait for it to complete
        do {
            udelay(10000);
            phy_stat = mii_read_register( p_i82544, PHY_ADDRESS, 1 );
            phy_stat = mii_read_register( p_i82544, PHY_ADDRESS, 1 );
        } while( (phy_stat & 0x0020) == 0 && (delay_cnt-- > 0) );

        if (phy_stat & 0x0020)
            p_i82544->link = 1;
    }

#ifdef DEBUG    
    show_phy( p_i82544, PHY_ADDRESS );
#endif    
#endif
    
#if 0
    // Reset link
    OUTL( ctrl | I82544_CTRL_LRST, ioaddr + I82544_CTRL );
    udelay(20);
    OUTL( ctrl, ioaddr + I82544_CTRL );
    udelay(20);
    show_phy( p_i82544, PHY_ADDRESS );
#endif


    
    // Transfer speed and duplicity settings from PHY to MAC

    // In theory the MAC is supposed to auto-configure from what the
    // PHY has autonegotiated. In practice this does not seem to work
    // (on the 82543 at least, it always thinks it is 1000MHz full
    // duplex) and we have to transfer the settings from the PHY by
    // hand. Additionally, the settings in the PHY ctrl register seem
    // bogus, so we read the values out of the PHY specific status
    // register instead.
    
    if (p_i82544->link) {
        cyg_uint16 phy_pssr;
        
        phy_pssr = mii_read_register( p_i82544, PHY_ADDRESS, 17 );

        ctrl = INL( ioaddr + I82544_CTRL );
//        os_printf("CTRL %08x\n",ctrl);
        ctrl &= ~(I82544_CTRL_SPEED | I82544_CTRL_FD);
        if( phy_pssr & (1<<13) )
            ctrl |= I82544_CTRL_FD;

        // Transfer speed
        ctrl |= ((phy_pssr>>14)&3)<<8;

        ctrl |= I82544_CTRL_FRCDPLX | I82544_CTRL_FRCSPD;

        OUTL( ctrl, ioaddr + I82544_CTRL );
//        os_printf("CTRL %08x\n",ctrl);
    }

    
#if 0
#ifdef DEBUG
    {
        int status = i82544_status( sc );
        static int speed[4] = { 10, 100, 1000, 1000 };
        os_printf("i82544_start %d flg %x Link = %s, %d Mbps, %s Duplex\n",
                  p_i82544->index,
                  *(int *)p_i82544,
                  status & GEN_STATUS_LINK ? "Up" : "Down",
                  speed[(status & GEN_STATUS_BPS)>>GEN_STATUS_BPS_SHIFT],
                  status & GEN_STATUS_FDX ? "Full" : "Half");
    }
#endif
#endif

    // Hang around here for a bit to let the device settle down. We
    // don't seem to get away without this.
    
    udelay( 1000000 );
    
#if 0

    // Having done all that, the interface still does not work
    // properly, UNLESS I now wait >= 45 seconds here. After that it
    // seems happy. I cannot find any difference in the state of the
    // PHY or the 82543 to explain this.
    
    show_phy( p_i82544, PHY_ADDRESS );
    os_printf("CTRL %08x\n",INL( ioaddr + I82544_CTRL ));    
    os_printf("STATUS %08x\n",INL( ioaddr + I82544_STATUS ));    
    os_printf("ICR %08x\n",INL( ioaddr + I82544_ICR ));    
    os_printf("RCTL %08x\n",INL( ioaddr + I82544_RCTL ));    
    os_printf("TCTL %08x\n",INL( ioaddr + I82544_TCTL ));    

    os_printf("Waiting 45 seconds\n");
    {
        int i;
        cyg_uint32 status = INL( ioaddr + I82544_STATUS );
//        for( i = 0; i < 60; i++ )       // works
//        for( i = 0; i < 45; i++ )       // works
//        for( i = 0; i < 40; i++ )       // fails
//        for( i = 0; i < 35; i++ )       // fails
//        for( i = 0; i < 30; i++ )       // fails
        {
            cyg_uint32 s;
            PC_WRITE_SCREEN_32( 60, i );
            udelay(1000000);
            s = INL( ioaddr + I82544_STATUS );
            if( s != status )
            {
                os_printf("%d STATUS change %08x\n",i,s);
                status = s;
            }
        }
    }

    show_phy( p_i82544, PHY_ADDRESS );
    os_printf("CTRL %08x\n",INL( ioaddr + I82544_CTRL ));    
    os_printf("STATUS %08x\n",INL( ioaddr + I82544_STATUS ));    
    os_printf("ICR %08x\n",INL( ioaddr + I82544_ICR ));    
    os_printf("RCTL %08x\n",INL( ioaddr + I82544_RCTL ));    
    os_printf("TCTL %08x\n",INL( ioaddr + I82544_TCTL ));    
    
#endif
#endif // CYGHWR_DEVS_ETH_INTEL_I82544_USE_ASD

    // Set up interrupts

    // Clear any pending interrupts
    ctrl = INL( ioaddr + I82544_ICR );

    // clear all mask bits
    OUTL( 0xFFFFFFFF, ioaddr + I82544_IMC );

    // Set interrupt bits for:
    // 1 = Transmit queue empty
    // 7 = Receiver timeout interrupt
    OUTL( (1<<1)|(1<<7), ioaddr + I82544_IMS );
        
}

// ------------------------------------------------------------------------
//
//  Function : i82544_start
//
// ------------------------------------------------------------------------

static void 
i82544_start( struct eth_drv_sc *sc, unsigned char *enaddr, int flags )
{
    struct i82544 *p_i82544;
    cyg_uint32 ioaddr;
    cyg_uint32 txctl, rxctl;
    
#ifdef CYGPKG_NET
    struct ifnet *ifp = &sc->sc_arpcom.ac_if;
#endif
#ifdef DEBUG
    db_printf("i82544_start\n");
#endif

    p_i82544 = (struct i82544 *)sc->driver_private;
    ioaddr = p_i82544->io_address; // get 82544's I/O address
    
    IF_BAD_82544( p_i82544 ) {
#ifdef DEBUG
        os_printf( "i82544_start: Bad device pointer %x\n", p_i82544 );
#endif
        return;
    }

    if ( ! p_i82544->mac_addr_ok ) {
#ifdef DEBUG
        os_printf("i82544_start %d: invalid MAC address, "
                  "can't bring up interface\n",

⌨️ 快捷键说明

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