if_i21143.c

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

C
1,882
字号
}

// ------------------------------------------------------------------------
//
//  Function : i21143_send
//
// ------------------------------------------------------------------------

static void 
i21143_send(struct eth_drv_sc *sc,
            struct eth_drv_sg *sg_list, int sg_len, int total_len,
            unsigned long key)
{
    struct i21143 *p_i21143;
    cyg_uint32 ioaddr;
    struct eth_drv_sg *last_sg;
    volatile BUFDES *bp;
    int bufcount;
    cyg_uint32 x;

    p_i21143 = (struct i21143 *)sc->driver_private;

    IF_BAD_21143( p_i21143 ) {
#ifdef DEBUG_TRAFFIC
        diag_printf( "i21143_send: Bad device pointer %x\n", p_i21143 );
#endif
        return;
    }

    if ( NO_TX_IN_PROGRESS != p_i21143->tx_endbuf) {
        // Then we cannot do this
#ifdef DEBUG_TRAFFIC
        diag_printf( "i21143_send: tx busy %x, %d bufs key %x\n",
                     p_i21143, p_i21143->tx_endbuf, p_i21143->tx_keys[0] );
#endif
        CYG_FAIL( "Send call when already busy" );
        INCR_STAT( tx_dropped );
        return;
    }

    ioaddr = p_i21143->io_address;

    bp = p_i21143->tx_ring;
    bufcount = 0;

#ifdef DEBUG_TRAFFIC
    diag_printf( "i21143_send() key %x\n", key );
#endif

    INCR_STAT( tx_count );

    for ( last_sg = &sg_list[sg_len]; sg_list < last_sg; sg_list++ ) {
        cyg_uint8 *from_p;
        int l;

        from_p = (cyg_uint8 *)(sg_list->buf); // normal cached address
        l = sg_list->len;

        if ( l > total_len )
            l = total_len;

        // Ensure the mbuf contents really is in RAM where DMA can see it.
        // (Must round to cache lines apparantly for some MIPS)
        HAL_DCACHE_STORE( ((CYG_ADDRESS)from_p) &~(HAL_DCACHE_LINE_SIZE-1),
                          l + HAL_DCACHE_LINE_SIZE );
        
        // There are two pointer, length pairs in each descriptor.  We only
        // bother using the first one - the code saved is small but so is
        // the overhead of memory.
        bp->buf1 = CYGHWR_PCI_VIRT_TO_BUS( from_p ); // uncached real RAM address
        bp->buf2 = 0;
        // Record length and add endring flag iff end of ring - never leave
        // it blank in memory!
        x = (l << DES1_B1SIZE_SHIFT) & DES1_B1SIZE_MASK; // plus zero buf2 size.
        if ( (TX_DESCRIPTORS-1) == bufcount )
            x |= DES1_ENDRING;
        bp->des1 = x;

        // Leave writing the zeroth bufdesc DES0 until all are complete:
        // the tx engine will be stopped looking there.
        if ( 0 < bufcount ) // then we can write DES0 also.
            bp->des0 = DES0_STATUS_OWN_OPEN;

        total_len -= l;
        bp++;
        bufcount++;

        if ( 0 > total_len ) 
            break; // Should exit via sg_last normally
    }

    CYG_ASSERT( 0 == total_len, "length mismatch in tx" );
    CYG_ASSERT( last_sg == sg_list, "sg count mismatch in tx" );

    CYG_ASSERT( bp > &p_i21143->tx_ring[0], "bp underflow" );

    bp--; // back to the last-used bp.
    bufcount--;
    p_i21143->tx_endbuf = bufcount;     // record that we are busy
    p_i21143->tx_keys[0] = key;         // and the key to return

    CYG_ASSERT( bp < &p_i21143->tx_ring[ TX_DESCRIPTORS ], "bp underflow" );

    // Mark this the last valid buffer.
    bp->des1 |= TDES1_CONTROL_LAST | TDES1_CONTROL_INTERRUPT;

    CYG_ASSERT( &(p_i21143->tx_ring[bufcount]) == bp, "Bp/bufcount misstep" );

    bp++;
    bufcount++;

    // Make the rest be null links
    for ( /* bp */ ; bp < &p_i21143->tx_ring[ TX_DESCRIPTORS ]; bp++ ) {
        bp->buf1 = 0;
        bp->buf2 = 0;
        x = 0; // zero size for both buffers
        if ( (TX_DESCRIPTORS-1) == bufcount )
            x |= DES1_ENDRING;
        bp->des1 = x;
        bp->des0 = DES0_STATUS_OWN_OPEN;
        bufcount++;
    }

    // Now it's safe to write the zeroth descriptor:
    p_i21143->tx_ring->des1 |= TDES1_CONTROL_FIRST;
    p_i21143->tx_ring->des0 = DES0_STATUS_OWN_OPEN;

#ifdef DEBUG_TRAFFIC_TXDETAILS
    dump_tx_details( p_i21143, "i21143_send" );
#endif

    // And start off the tx system
    x = INL( CSR_STATUS );

#ifdef DEBUG_TRAFFIC
    diag_printf( "i21143_send: ready, status %08x %s\n",
                 x,
                 (CSR_STATUS_TXSTATUS & x) != CSR_STATUS_TXSTATUS_STOPPED ?
                 "Running" : "Stopped" );
#endif

    if ( CSR_STATUS_TXSTATUS_STOPPED == (CSR_STATUS_TXSTATUS & x) ) {
        cyg_uint32 l;
        // Then we must initialize and start the tx machine
        OUTL( CYGHWR_PCI_VIRT_TO_BUS( (cyg_uint32)p_i21143->tx_ring ), CSR_TXBASE );
        l = INL( CSR_OPMODE );
        l |= CSR_OPMODE_TX_START;
        OUTL( l, CSR_OPMODE );
    }
    else if ( CSR_STATUS_TXSTATUS_SUSPENDED == (CSR_STATUS_TXSTATUS & x) ) {
        // Then we just have to ping it
        OUTL( 0, CSR_TXPOLL );
    }
    else {
#ifdef DEBUG_TRAFFIC
        diag_printf( "i21143_send: tx not ready %x CSR_STATUS %08x\n", p_i21143, x );
#endif
        CYG_FAIL( "Tx is not ready to tx" );

        // Try to recover by brutal means...
        i21143_stop( sc );              // will return the key
        i21143_start( sc, NULL, 0 );
    }
}

// ------------------------------------------------------------------------
//
//  Function : i21143_reset
//
// ------------------------------------------------------------------------
static void
i21143_reset(struct i21143* p_i21143)
{
    cyg_uint32 ioaddr = p_i21143->io_address;
    cyg_uint32 l;
    int i, status;
    // First stop the tx and rx engines - doc suggests that's necessary
    // before writing the reset reg, but it seems a little paranoid.

    l = INL( CSR_OPMODE );
    l &=~ (CSR_OPMODE_RX_START | CSR_OPMODE_TX_START);
    OUTL( l, CSR_OPMODE );

    for ( i = 0; i < 10000; i++) {
        l = INL( CSR_STATUS );
        if ( ((CSR_STATUS_TXSTATUS & l) == CSR_STATUS_TXSTATUS_STOPPED) &&
             ((CSR_STATUS_RXSTATUS & l) == CSR_STATUS_RXSTATUS_STOPPED) )
            break;
    }
#ifdef DEBUG_STARTSTOPRESET
    diag_printf( "i21143_reset: rx and tx idle after %d iters, status %x\n",
                 i, l );
#endif

    // Acknowledge all the interrupts from these activities
    // within the device:
    OUTL( 0xffffffff, CSR_STATUS ); // clear all bits

    // Now reset the device.  We are going to re-init all params anyway, so
    // ignore other settings:
    OUTL( CSR_BUSMODE_RESET, CSR_BUSMODE );

    udelay( 100 ); // let reset take effect.  "50 PCI clock cycles"

    for ( i = 0; i < 10000; i++) {
        l = INL( CSR_BUSMODE );
        if ( (CSR_BUSMODE_RESET & l) == 0 )
            break;
    }
#ifdef DEBUG_STARTSTOPRESET
    diag_printf( "i21143_reset: reset zero after %d iters, busmode %x\n",
                 i, l );
#endif

    // Gross initialization
    OUTL( 0 // No other options
          // CSR_BUSMODE_PM        
          // CSR_BUSMODE_WIE       
          // CSR_BUSMODE_RLE       
          // CSR_BUSMODE_RME       
          // CSR_BUSMODE_DBO_BE    
          // CSR_BUSMODE_DBO_LE    
          // CSR_BUSMODE_TAP_SHIFT 
          | (1 << CSR_BUSMODE_CAL_SHIFT)
          | (4 << CSR_BUSMODE_PBL_SHIFT)
          // CSR_BUSMODE_ENDIAN_BE 
          // CSR_BUSMODE_ENDIAN_LE 
          // CSR_BUSMODE_DSL_SHIFT 
          // CSR_BUSMODE_BAR       
          // CSR_BUSMODE_RESET     

          , CSR_BUSMODE );

    // No interrupt sources enabled yet
    OUTL( 0, CSR_INTR_ENABLE );

    OUTL( 0
          // CSR_ROM_MII_MGT_MDI
          | CSR_ROM_MII_MGT_MOM_READ
          // CSR_ROM_MII_MGT_MDO  
          // CSR_ROM_MII_MGT_MDC  
          // CSR_ROM_MII_MGT_RD   
          // CSR_ROM_MII_MGT_WR   
          // CSR_ROM_MII_MGT_BR   
          // CSR_ROM_MII_MGT_SR   
          // CSR_ROM_MII_MGT_REG  
          // CSR_ROM_MII_MGT_SR_DO
          // CSR_ROM_MII_MGT_SR_DI
          // CSR_ROM_MII_MGT_SR_CK
          // CSR_ROM_MII_MGT_SR_CS
          | 0xff,

          CSR_ROM_MII_MGT );

    // -----------------------------------
    // Now find out about the status of the PHY via MII
#if 0
    // Try resetting the PHY and power-cycling it:
    mii_write_register( ioaddr, PHY_CONTROL_REG, PHY_CONTROL_RESET | PHY_CONTROL_AUTONEG_EN);
    while (1) {
        int v;
        v = mii_read_register( ioaddr, PHY_CONTROL_REG );
        if ( 0 == (v & PHY_CONTROL_RESET) )
            break;
    }

    mii_write_register( ioaddr, PHY_CONTROL_REG,
                        PHY_CONTROL_POWERDOWN | PHY_CONTROL_MII_DIS | PHY_CONTROL_AUTONEG_EN );
    udelay( 1000 );
    mii_write_register( ioaddr, PHY_CONTROL_REG, PHY_CONTROL_AUTONEG_EN | PHY_CONTROL_AUTONEG_RST );

    udelay( 1000 );

    while (1) {
        int v;
        v = mii_read_register( ioaddr, PHY_STATUS_REG );
        if ( PHY_STATUS_AUTONEG_ACK & v )
            break;
    }    
#endif

#ifdef DEBUG_DUMP_REGS
    debug_dump_regs( ioaddr, MII );
#endif

    status = i21143_status( p_i21143 );
    p_i21143->line_status = status;     // record it for SNMP info
#ifdef DEBUG_STARTSTOPRESET
    diag_printf("i21143_reset %d flg %x Link = %s, %s Mbps, %s Duplex\n",
                p_i21143->index,
                *(int *)p_i21143,
                (GEN_STATUS_LINK    & status) ?   "Up" : "Down",
                (GEN_STATUS_100MBPS & status) ?  "100" : "10",
                (GEN_STATUS_FDX     & status) ? "Full" : "Half");
#endif

    OUTL( -1, CSR_ROM_PGM_ADDR );
    OUTL(  0, CSR_INTR_MITIGATION );

    // CSR13, 14 = 0; select SIA mode temporarily.
//    OUTL( CSR_OPMODE_MUST_BE_ONE, CSR_OPMODE);
    OUTL( CSR_SIA_CONN_DEFAULT, CSR_SIA_CONN );
    OUTL( CSR_SIA_TXRX_DEFAULT, CSR_SIA_TXRX );
    OUTL( CSR_SIA_GPPORT_DEFAULT, CSR_SIA_GPPORT );

    // Last one, CSR6
    l = 0
        | CSR_OPMODE_SC          
        // CSR_OPMODE_RA
        // CSR_OPMODE_IGNOREMSB   
        | CSR_OPMODE_MUST_BE_ONE 
        // CSR_OPMODE_SCR      do not set for MII mode 
        // CSR_OPMODE_PCS      do not set for MII mode   
        // CSR_OPMODE_TTM      set for 10Mb, not set for 100Mb
        | CSR_OPMODE_SF          // Set the store&forward bit so we can keep-
        | CSR_OPMODE_HBD         //  -up at 100Mbit
        | CSR_OPMODE_PS_MIISYM 
        | CSR_OPMODE_CA          
        // CSR_OPMODE_TX_THRES_SHIFT
        // CSR_OPMODE_TX_START    
        // CSR_OPMODE_FC          
        // (0 << CSR_OPMODE_LOOPBACK_SHIFT)
        // CSR_OPMODE_FD
        // CSR_OPMODE_MULTICAST         // Pass all multicast
        // CSR_OPMODE_PROMISC           // Promisc mode
        // CSR_OPMODE_SB          
        // CSR_OPMODE_IF                // Inverse filtering (!)         
        // CSR_OPMODE_PB          
        // CSR_OPMODE_HO                // 0: Perfect filter
        // CSR_OPMODE_RX_START          //    1: hashing for /all/ addresses
        // CSR_OPMODE_HP                // 0: Perfect filter of N address
        ;                               //    1: imperfect hash filter + 1 fixed addr 
    
    if ( GEN_STATUS_FDX & status )
        l |= CSR_OPMODE_FD;
    if ( ! (GEN_STATUS_100MBPS & status) )
        l |= CSR_OPMODE_TTM;

    OUTL( l, CSR_OPMODE );

}

// ------------------------------------------------------------------------
//
//                       INTERRUPT HANDLERS
//
// ------------------------------------------------------------------------

static cyg_uint32
eth_isr(cyg_vector_t vector, cyg_addrword_t data)
{
    cyg_drv_interrupt_mask( vector );
    
#ifdef DEBUG_TRAFFIC
    diag_printf( "i21143_isr, vector %d\n", vector );
#endif

    return CYG_ISR_CALL_DSR;        // schedule DSR
}


// An indirection is used because (if we have multiple devices) we don't
// know all the "sc" values at the time the interrupts are created.
static 
void eth_dsr(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
{
    struct i21143* p_i21143 = (struct i21143 *)data;
    struct cyg_netdevtab_entry *ndp =
        (struct cyg_netdevtab_entry *)(p_i21143->ndp);
    struct eth_drv_sc *sc = (struct eth_drv_sc *)(ndp->device_instance);

    INCR_STAT( interrupts );

    // but here, it 

⌨️ 快捷键说明

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