if_ppc405.c

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

C
688
字号
    // Frame gap
    EMAC0_IPGVR = 8;

    // Enable MAL
    CYGARC_MTDCR(MAL0_TXCASR, MAL_CASR_C0);
    CYGARC_MTDCR(MAL0_RXCASR, MAL_CASR_C0);

    // Reset all interrupts
    EMAC0_ISR = 0xFFFFFFFF;
#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
    qi->ints = 0;
#endif

    // Enable interface
    EMAC0_MR0 |= (EMAC0_MR0_RXE|EMAC0_MR0_TXE);

    // Restore interrupts
    HAL_RESTORE_INTERRUPTS(int_state);
    return true;
}

//
// Initialize the interface - performed at system startup
// This function must set up the interface, including arranging to
// handle interrupts, etc, so that it may be "started" cheaply later.
//

static bool 
ppc405_eth_init(struct cyg_netdevtab_entry *tab)
{
    struct eth_drv_sc *sc = (struct eth_drv_sc *)tab->device_instance;
    struct ppc405_eth_info *qi = (struct ppc405_eth_info *)sc->driver_private;
    bool esa_ok;
    unsigned char enaddr[6];

    ppc405_eth_stop(sc);  // Make sure it's not running yet

#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
    // Set up to handle interrupts
    EMAC_INTERRUPT_HANDLER(CYGNUM_HAL_INTERRUPT_MAL_SERR, mal_serr);
    EMAC_INTERRUPT_HANDLER(CYGNUM_HAL_INTERRUPT_MAL_TX_EOB, mal_txeob);
    EMAC_INTERRUPT_HANDLER(CYGNUM_HAL_INTERRUPT_MAL_RX_EOB, mal_rxeob);
    EMAC_INTERRUPT_HANDLER(CYGNUM_HAL_INTERRUPT_MAL_TX_DE, mal_txde);
    EMAC_INTERRUPT_HANDLER(CYGNUM_HAL_INTERRUPT_MAL_RX_DE, mal_rxde);
    EMAC_INTERRUPT_HANDLER(CYGNUM_HAL_INTERRUPT_EMAC0, emac);
#endif

    // Get physical device address
#ifdef CYGPKG_REDBOOT
#ifdef CYGSEM_REDBOOT_FLASH_CONFIG
    esa_ok = flash_get_config(qi->esa_key, enaddr, CONFIG_ESA);
#else
    esa_ok = false;
#endif
#else
    esa_ok = CYGACC_CALL_IF_FLASH_CFG_OP(CYGNUM_CALL_IF_FLASH_CFG_GET,         
                                         qi->esa_key, enaddr, CONFIG_ESA);
#endif
    if (!esa_ok) {
        // Can't figure out ESA
        os_printf("PPC405_ETH - Warning! ESA unknown\n");
        memcpy(&enaddr, qi->enaddr, sizeof(enaddr));
    }
    memcpy(qi->cfg_enaddr, enaddr, sizeof(enaddr));

    // Configure the device
    if (!ppc405_eth_reset(sc, enaddr, 0)) {
        return false;
    }

    // Initialize upper level driver
    (sc->funs->eth_drv->init)(sc, (unsigned char *)&enaddr);
    
    return true;
}
 
//
// This function is called to shut down the interface.
//
static void
ppc405_eth_stop(struct eth_drv_sc *sc)
{
    EMAC0_MR0 &= ~(EMAC0_MR0_RXE|EMAC0_MR0_TXE);
}

//
// This function is called to "start up" the interface.  It may be called
// multiple times, even when the hardware is already running.  It will be
// called whenever something "hardware oriented" changes and should leave
// the hardware ready to send/receive packets.
//
static void
ppc405_eth_start(struct eth_drv_sc *sc, unsigned char *enaddr, int flags)
{
    EMAC0_MR0 |= (EMAC0_MR0_RXE|EMAC0_MR0_TXE);
}

//
// This function is called for low level "control" operations
//
static int
ppc405_eth_control(struct eth_drv_sc *sc, unsigned long key,
                  void *data, int length)
{
    os_printf("%s.%d\n", __FUNCTION__, __LINE__);
    return 1;
#if 0
#ifdef ETH_DRV_SET_MC_ALL
    struct ppc405_eth_info *qi = (struct ppc405_eth_info *)sc->driver_private;
    volatile struct ppc405 *ppc405 = qi->ppc405;
#endif

    switch (key) {
    case ETH_DRV_SET_MAC_ADDRESS:
        return 0;
        break;
#ifdef ETH_DRV_SET_MC_ALL
    case ETH_DRV_SET_MC_ALL:
    case ETH_DRV_SET_MC_LIST:
        ppc405->RxControl &= ~RxControl_PROM;
        ppc405->hash[0] = 0xFFFFFFFF;
        ppc405->hash[1] = 0xFFFFFFFF;
        return 0;
        break;
#endif
    default:
        return 1;
        break;
    }
#endif
}

//
// This function is called to see if another packet can be sent.
// It should return the number of packets which can be handled.
// Zero should be returned if the interface is busy and can not send any more.
//
static int
ppc405_eth_can_send(struct eth_drv_sc *sc)
{
    struct ppc405_eth_info *qi = (struct ppc405_eth_info *)sc->driver_private;

    return (qi->txactive < CYGNUM_DEVS_ETH_POWERPC_PPC405_TxNUM);
}

//
// This routine is called to send data to the hardware.

static void 
ppc405_eth_send(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len, 
               int total_len, unsigned long key)
{
    struct ppc405_eth_info *qi = (struct ppc405_eth_info *)sc->driver_private;
    volatile mal_bd_t *txbd;
    volatile char *bp;
    int i, txindex;

    // Find a free buffer
    txbd = qi->txbd;
    // Set up buffer
    bp = (volatile char *)CYGARC_UNCACHED_ADDRESS(txbd->buffer);
    for (i = 0;  i < sg_len;  i++) {
        memcpy((void *)bp, (void *)sg_list[i].buf, sg_list[i].len);
        bp += sg_list[i].len;
    } 
    txbd->length = total_len;
    txindex = ((unsigned long)txbd - (unsigned long)qi->tbase) / sizeof(*txbd);
    qi->txkey[txindex] = key;
    // Send it on it's way
    txbd->status = (txbd->status & MAL_BD_W) | MAL_BD_R | MAL_BD_L | MAL_BD_I | 
        MAL_BD_TX_GFCS | MAL_BD_TX_GPAD;
    qi->txactive++;
    EMAC0_TMR0 = EMAC0_TMR0_GNP0;  // Start channel 0
    // Remember the next buffer to try
    if (txbd->status & MAL_BD_W) {
        qi->txbd = qi->tbase;
    } else {
        qi->txbd = txbd+1;
    }
}

//
// This function is called when a packet has been received.  It's job is
// to prepare to unload the packet from the hardware.  Once the length of
// the packet is known, the upper layer of the driver can be told.  When
// the upper layer is ready to unload the packet, the internal function
// 'ppc405_eth_recv' will be called to actually fetch it from the hardware.
//
static void
ppc405_eth_RxEvent(struct eth_drv_sc *sc)
{
    struct ppc405_eth_info *qi = (struct ppc405_eth_info *)sc->driver_private;
    volatile mal_bd_t *rxbd, *rxfirst;

    rxbd = rxfirst = qi->rnext;
    while ((rxbd->status & MAL_BD_R) == 0) {
        qi->rxbd = rxbd;  // Save for callback
        (sc->funs->eth_drv->recv)(sc, rxbd->length-4);  // Adjust for FCS
        if (rxbd->status & MAL_BD_W) {
            rxbd = qi->rbase;
        } else {
            rxbd++;
        }
    }
    // Remember where we left off
    qi->rnext = (mal_bd_t *)rxbd;
}

//
// This function is called as a result of the "eth_drv_recv()" call above.
// It's job is to actually fetch data for a packet from the hardware once
// memory buffers have been allocated for the packet.  Note that the buffers
// may come in pieces, using a scatter-gather list.  This allows for more
// efficient processing in the upper layers of the stack.
//
static void
ppc405_eth_recv(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len)
{
    struct ppc405_eth_info *qi = (struct ppc405_eth_info *)sc->driver_private;
    unsigned char *bp;
    int i;

    bp = (unsigned char *)CYGARC_UNCACHED_ADDRESS(qi->rxbd->buffer);
    for (i = 0;  i < sg_len;  i++) {
        if (sg_list[i].buf != 0) {
            memcpy((void *)sg_list[i].buf, bp, sg_list[i].len);
            bp += sg_list[i].len;
        }
    }
    qi->rxbd->status = (qi->rxbd->status & (MAL_BD_W|MAL_BD_I)) | MAL_BD_R;
}

static void
ppc405_eth_TxEvent(struct eth_drv_sc *sc)
{
    struct ppc405_eth_info *qi = (struct ppc405_eth_info *)sc->driver_private;
    volatile mal_bd_t *txbd;
    int key, txindex;

    txbd = qi->tnext;
    while ((txbd->status & (MAL_BD_R|MAL_BD_I)) == MAL_BD_I) {
        txindex = ((unsigned long)txbd - (unsigned long)qi->tbase) / sizeof(*txbd);
        if ((key = qi->txkey[txindex]) != 0) {
            qi->txkey[txindex] = 0;
            (sc->funs->eth_drv->tx_done)(sc, key, 0);
        }
        qi->txactive -= 1;
        txbd->status &= MAL_BD_W;  //  Only preserve wrap bit
        if ((txbd->status & MAL_BD_W) != 0) {
            txbd = qi->tbase;
        } else {
            txbd++;
        }
        if (txbd == qi->tnext) {
            break;  // Went through whole list
        }
    }
    // Remember where we left off
    qi->tnext = (mal_bd_t *)txbd;
}

//
// Interrupt processing
//
int dump_mal0_esr = 0;

static void          
ppc405_eth_int(struct eth_drv_sc *sc)
{
    struct ppc405_eth_info *qi = (struct ppc405_eth_info *)sc->driver_private;
    unsigned long event, tx_event, rx_event, tx_deir, rx_deir;
    bool need_reset = false;

    CYGARC_MFDCR(MAL0_TXEOBISR, tx_event);
    if (tx_event != 0) {
        ppc405_eth_TxEvent(sc);
        CYGARC_MTDCR(MAL0_TXEOBISR, tx_event);
    }
    CYGARC_MFDCR(MAL0_RXEOBISR, rx_event);
    if (rx_event != 0) {
        ppc405_eth_RxEvent(sc);
        CYGARC_MTDCR(MAL0_RXEOBISR, rx_event);
    }
    if ((event = EMAC0_ISR) != 0) {
        if ((event & ~(EMAC0_ISR_SE0|EMAC0_ISR_SE1)) != 0) {
            // Error other than signal quality
            os_printf("EMAC0_ISR: %x\n", event);
            if ((event & (EMAC0_ISR_TE0|EMAC0_ISR_TE1)) != 0) {
                // Some problem with transmit - should be easily recoverable
                CYGARC_MTDCR(MAL0_TXCASR, MAL_CASR_C0);
                qi->tnext = qi->tbase;
            }
            if ((event & (EMAC0_ISR_OVR|EMAC0_ISR_BP|EMAC0_ISR_RP|EMAC0_ISR_ALE|EMAC0_ISR_BFCS)) != 0) {
                // Rx errors - reset device
                need_reset = true;
            }
        }
        EMAC0_ISR = event;  // Reset the bits we handled
    }
    CYGARC_MFDCR(MAL0_ESR, event);
    if ((event & MAL_ESR_INT_MASK) != 0) {
        CYGARC_MFDCR(MAL0_TXDEIR, tx_deir);
        CYGARC_MFDCR(MAL0_RXDEIR, rx_deir);
        if (dump_mal0_esr) {
            os_printf("MAL0_ESR: %x, Tx: %x, Rx: %x\n", event, tx_deir, rx_deir);
            os_printf("Tx buffer headers\n");
            diag_dump_buf((void *)qi->tbase, qi->txnum*sizeof(mal_bd_t));
            os_printf("Rx buffer headers\n");
            diag_dump_buf((void *)qi->rbase, qi->rxnum*sizeof(mal_bd_t));
        }
        if (tx_deir != 0) {
            // Fix Tx descriptor problems
            CYGARC_MTDCR(MAL0_TXDEIR, tx_deir);  // Clear interrupt indicator
            CYGARC_MTDCR(MAL0_TXCASR, MAL_CASR_C0);
            qi->tnext = qi->tbase;
        }
        if (rx_deir != 0) {
            // Fix Rx descriptor problems
            CYGARC_MTDCR(MAL0_RXDEIR, rx_deir);  // Clear interrupt indicator
            CYGARC_MTDCR(MAL0_RXCASR, MAL_CASR_C0);
            qi->rnext = qi->rbase;
        }
        CYGARC_MTDCR(MAL0_ESR, event);  // Clear events just handled
    }
    if (need_reset) {
        // Something has gone awry - try resetting the device
        os_printf("\n... PPC405 ethernet - hard reset after failure\n");
        ppc405_eth_stop(sc);
        if (!ppc405_eth_reset(sc, qi->cfg_enaddr, 0)) {        
            os_printf("!! Failed? !!\n");
        }
    }
}

//
// Interrupt vector
//
static int          
ppc405_eth_int_vector(struct eth_drv_sc *sc)
{
    struct ppc405_eth_info *qi = (struct ppc405_eth_info *)sc->driver_private;
    return qi->int_vector;
}

⌨️ 快捷键说明

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