URL
https://opencores.org/ocsvn/openrisc/openrisc/trunk
Subversion Repositories openrisc
[/] [openrisc/] [trunk/] [rtos/] [ecos-3.0/] [packages/] [devs/] [eth/] [powerpc/] [ppc405/] [current/] [src/] [if_ppc405.c] - Rev 786
Compare with Previous | Blame | View Log
//========================================================================== // // dev/if_ppc405.c // // Ethernet device driver for PowerPC PPC405 boards // //========================================================================== // ####ECOSGPLCOPYRIGHTBEGIN#### // ------------------------------------------- // This file is part of eCos, the Embedded Configurable Operating System. // Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2005 Free Software Foundation, Inc. // // eCos is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free // Software Foundation; either version 2 or (at your option) any later // version. // // eCos is distributed in the hope that it will be useful, but WITHOUT // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License // for more details. // // You should have received a copy of the GNU General Public License // along with eCos; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // // As a special exception, if other files instantiate templates or use // macros or inline functions from this file, or you compile this file // and link it with other works to produce a work based on this file, // this file does not by itself cause the resulting work to be covered by // the GNU General Public License. However the source code for this file // must still be made available in accordance with section (3) of the GNU // General Public License v2. // // This exception does not invalidate any other reasons why a work based // on this file might be covered by the GNU General Public License. // ------------------------------------------- // ####ECOSGPLCOPYRIGHTEND#### //========================================================================== //#####DESCRIPTIONBEGIN#### // // Author(s): gthomas // Contributors: gthomas // Date: 2003-08-15 // Purpose: // Description: hardware driver for PPC405 // // //####DESCRIPTIONEND#### // //========================================================================== // Ethernet device driver for PPC405 #include <pkgconf/system.h> #include <pkgconf/devs_eth_powerpc_ppc405.h> #include <pkgconf/io_eth_drivers.h> #ifdef CYGPKG_NET #include <pkgconf/net.h> #endif #include <cyg/infra/cyg_type.h> #include <cyg/infra/diag.h> #include <cyg/hal/hal_arch.h> #include <cyg/hal/hal_cache.h> #include <cyg/hal/hal_intr.h> #include <cyg/hal/drv_api.h> #include <cyg/hal/hal_if.h> #include <cyg/hal/ppc_regs.h> #include <cyg/io/eth/netdev.h> #include <cyg/io/eth/eth_drv.h> #include <cyg/io/eth_phy.h> // // PHY access functions // static void ppc405_eth_phy_init(void); static void ppc405_eth_phy_put_reg(int reg, int phy, unsigned short data); static bool ppc405_eth_phy_get_reg(int reg, int phy, unsigned short *val); #include "ppc405_enet.h" #include CYGDAT_DEVS_PPC405_ETH_INL #define os_printf diag_printf // For fetching the ESA from RedBoot #include <cyg/hal/hal_if.h> #ifndef CONFIG_ESA #define CONFIG_ESA 6 #endif static void ppc405_eth_int(struct eth_drv_sc *data); #ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED static cyg_interrupt ppc405_emac_interrupt; static cyg_handle_t ppc405_emac_interrupt_handle; static cyg_interrupt ppc405_mal_txeob_interrupt; static cyg_handle_t ppc405_mal_txeob_interrupt_handle; static cyg_interrupt ppc405_mal_rxeob_interrupt; static cyg_handle_t ppc405_mal_rxeob_interrupt_handle; static cyg_interrupt ppc405_mal_txde_interrupt; static cyg_handle_t ppc405_mal_txde_interrupt_handle; static cyg_interrupt ppc405_mal_rxde_interrupt; static cyg_handle_t ppc405_mal_rxde_interrupt_handle; static cyg_interrupt ppc405_mal_serr_interrupt; static cyg_handle_t ppc405_mal_serr_interrupt_handle; #define EMAC_INTERRUPT_HANDLER(_int_,_hdlr_) \ cyg_drv_interrupt_create(_int_, \ 0, \ (cyg_addrword_t)sc, /* Data item passed to interrupt handler */ \ (cyg_ISR_t *)ppc405_eth_isr, \ (cyg_DSR_t *)eth_drv_dsr, \ &ppc405_##_hdlr_##_interrupt_handle, \ &ppc405_##_hdlr_##_interrupt); \ cyg_drv_interrupt_attach(ppc405_##_hdlr_##_interrupt_handle); \ cyg_drv_interrupt_acknowledge(_int_); \ cyg_drv_interrupt_unmask(_int_); // This ISR is called when the ethernet interrupt occurs static int ppc405_eth_isr(cyg_vector_t vector, cyg_addrword_t data, HAL_SavedRegisters *regs) { struct eth_drv_sc *sc = (struct eth_drv_sc *)data; struct ppc405_eth_info *qi = (struct ppc405_eth_info *)sc->driver_private; cyg_drv_interrupt_mask(CYGNUM_HAL_INTERRUPT_MAL_SERR); cyg_drv_interrupt_mask(CYGNUM_HAL_INTERRUPT_MAL_TX_EOB); cyg_drv_interrupt_mask(CYGNUM_HAL_INTERRUPT_MAL_RX_EOB); cyg_drv_interrupt_mask(CYGNUM_HAL_INTERRUPT_MAL_TX_DE); cyg_drv_interrupt_mask(CYGNUM_HAL_INTERRUPT_MAL_RX_DE); cyg_drv_interrupt_mask(CYGNUM_HAL_INTERRUPT_EMAC0); qi->ints = vector; return (CYG_ISR_HANDLED|CYG_ISR_CALL_DSR); // Run the DSR } #endif // Deliver function (ex-DSR) handles the ethernet [logical] processing static void ppc405_eth_deliver(struct eth_drv_sc *sc) { #ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED struct ppc405_eth_info *qi = (struct ppc405_eth_info *)sc->driver_private; cyg_uint32 old_ints; #endif ppc405_eth_int(sc); #ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED // Allow interrupts to happen again HAL_DISABLE_INTERRUPTS(old_ints); cyg_drv_interrupt_acknowledge(qi->ints); cyg_drv_interrupt_unmask(CYGNUM_HAL_INTERRUPT_MAL_SERR); cyg_drv_interrupt_unmask(CYGNUM_HAL_INTERRUPT_MAL_TX_EOB); cyg_drv_interrupt_unmask(CYGNUM_HAL_INTERRUPT_MAL_RX_EOB); cyg_drv_interrupt_unmask(CYGNUM_HAL_INTERRUPT_MAL_TX_DE); cyg_drv_interrupt_unmask(CYGNUM_HAL_INTERRUPT_MAL_RX_DE); cyg_drv_interrupt_unmask(CYGNUM_HAL_INTERRUPT_EMAC0); HAL_RESTORE_INTERRUPTS(old_ints); #endif } // // PHY unit access // static void ppc405_eth_phy_init(void) { // Set up MII hardware - nothing to do on this platform } static void ppc405_eth_phy_put_reg(int reg, int phy, unsigned short data) { unsigned long reg_val; reg_val = EMAC0_STACR_STAC_WRITE | EMAC0_STACR_OPBC_66; reg_val |= (phy << EMAC0_STACR_PCDA_SHIFT) | reg; reg_val |= (data << EMAC0_STACR_PHYD_SHIFT); #ifdef PHY_DEBUG os_printf("PHY PUT - reg: %d, phy: %d, val: %04x [%08x]\n", reg, phy, data, reg_val); #endif while ((EMAC0_STACR & EMAC0_STACR_OC) == 0) ; // Wait for MII free EMAC0_STACR = reg_val; while ((EMAC0_STACR & EMAC0_STACR_OC) == 0) ; // Wait for MII complete } static bool ppc405_eth_phy_get_reg(int reg, int phy, unsigned short *val) { unsigned long reg_val; reg_val = EMAC0_STACR_STAC_READ | EMAC0_STACR_OPBC_66; reg_val |= (phy << EMAC0_STACR_PCDA_SHIFT) | reg; #ifdef PHY_DEBUG os_printf("PHY GET - reg: %d, phy: %d [%08x] = ", reg, phy, reg_val); #endif while ((EMAC0_STACR & EMAC0_STACR_OC) == 0) ; // Wait for MII free EMAC0_STACR = reg_val; while ((EMAC0_STACR & EMAC0_STACR_OC) == 0) ; // Wait for MII complete if ((EMAC0_STACR & EMAC0_STACR_PHYE) == 0) { // Operation completed with no error *val = (EMAC0_STACR & EMAC0_STACR_PHYD) >> EMAC0_STACR_PHYD_SHIFT; #ifdef PHY_DEBUG os_printf("%04x\n", *val); #endif return true; } else { // No response #ifdef PHY_DEBUG os_printf("***ERROR***\n"); #endif return false; } } // // [re]Initialize the ethernet controller // Done separately since shutting down the device requires a // full reconfiguration when re-enabling. // when static bool ppc405_eth_reset(struct eth_drv_sc *sc, unsigned char *enaddr, int flags) { struct ppc405_eth_info *qi = (struct ppc405_eth_info *)sc->driver_private; volatile mal_bd_t *rxbd, *RxBD, *txbd, *TxBD; unsigned char *RxBUF, *TxBUF; int i, int_state; unsigned long mal_status, mode; unsigned short phy_state = 0; // Ignore unless device is idle/stopped if ((EMAC0_MR0 & (EMAC0_MR0_RXI|EMAC0_MR0_TXI)) != (EMAC0_MR0_RXI|EMAC0_MR0_TXI)) { return true; } // Make sure interrupts are off while we mess with the device HAL_DISABLE_INTERRUPTS(int_state); // Reset EMAC controller EMAC0_MR0 |= EMAC0_MR0_SRST; i = 0; while ((EMAC0_MR0 & EMAC0_MR0_SRST) != 0) { if (++i >= 500000) { os_printf("PPC405 Ethernet does not reset\n"); HAL_RESTORE_INTERRUPTS(int_state); return false; } } TxBD = qi->txbd_table; txbd = (mal_bd_t *)CYGARC_UNCACHED_ADDRESS(TxBD); RxBD = qi->rxbd_table; rxbd = (mal_bd_t *)CYGARC_UNCACHED_ADDRESS(RxBD); qi->tbase = qi->txbd = qi->tnext = txbd; qi->rbase = qi->rxbd = qi->rnext = rxbd; qi->txactive = 0; RxBUF = qi->rxbuf; TxBUF = qi->txbuf; // setup buffer descriptors for (i = 0; i < CYGNUM_DEVS_ETH_POWERPC_PPC405_RxNUM; i++) { rxbd->length = 0; rxbd->buffer = (unsigned long)RxBUF; rxbd->status = MAL_BD_R | MAL_BD_I; RxBUF += CYGNUM_DEVS_ETH_POWERPC_PPC405_BUFSIZE; rxbd++; } rxbd--; rxbd->status |= MAL_BD_W; // Last buffer for (i = 0; i < CYGNUM_DEVS_ETH_POWERPC_PPC405_TxNUM; i++) { txbd->length = 0; txbd->buffer = (unsigned long)TxBUF; txbd->status = 0; TxBUF += CYGNUM_DEVS_ETH_POWERPC_PPC405_BUFSIZE; txbd++; } txbd--; txbd->status |= MAL_BD_W; // Last buffer // Tell memory access layer where the buffer descriptors are CYGARC_MTDCR(MAL0_TXCARR, MAL_CASR_C0|MAL_CASR_C1); // Disable/reset channel #0 & #1 CYGARC_MTDCR(MAL0_RXCARR, MAL_CASR_C0); // Disable/reset channel #0 CYGARC_MTDCR(MAL0_CFG, MAL_CFG_SR); i = 0; CYGARC_MFDCR(MAL0_CFG, mal_status); while ((mal_status & MAL_CFG_SR) != 0) { if (++i >= 500000) { os_printf("PPC405 MAL does not reset\n"); HAL_RESTORE_INTERRUPTS(int_state); return false; } } CYGARC_MTDCR(MAL0_CFG, MAL_CFG_PLBB | MAL_CFG_OPBBL | MAL_CFG_LEA | MAL_CFG_PLBT_DEFAULT); CYGARC_MTDCR(MAL0_TXCTP0R, TxBD); CYGARC_MTDCR(MAL0_RXCTP0R, RxBD); CYGARC_MTDCR(MAL0_RXBS0, (CYGNUM_DEVS_ETH_POWERPC_PPC405_BUFSIZE/16)); // Receive buffer size // Set device physical address (ESA) EMAC0_IAHR = (enaddr[0]<<8) | (enaddr[1]<<0); EMAC0_IALR = (enaddr[2]<<24) | (enaddr[3]<<16) | (enaddr[4]<<8) | (enaddr[5]<<0); // Operating mode if (!_eth_phy_init(qi->phy)) { return false; } phy_state = _eth_phy_state(qi->phy); os_printf("PPC405 ETH: "); mode = EMAC0_MR1_RFS_4096 | EMAC0_MR1_TFS_2048 | EMAC0_MR1_TR0_MULTI | EMAC0_MR1_APP; if ((phy_state & ETH_PHY_STAT_LINK) != 0) { if ((phy_state & ETH_PHY_STAT_100MB) != 0) { // Link can handle 100Mb mode |= EMAC0_MR1_MF_100MB; os_printf("100Mb"); if ((phy_state & ETH_PHY_STAT_FDX) != 0) { mode |= EMAC0_MR1_FDE | EMAC0_MR1_EIFC | EMAC0_MR1_IST; os_printf("/Full Duplex"); } } else { // Assume 10Mb, half duplex mode |= EMAC0_MR1_MF_10MB; os_printf("10Mb"); } } else { os_printf("/***NO LINK***"); return false; } os_printf("\n"); EMAC0_MR1 = mode; // Configure receiver EMAC0_RMR = EMAC0_RMR_IAE | EMAC0_RMR_BAE | EMAC0_RMR_RRP | EMAC0_RMR_RFP; // Transmit threshold to 256 bytes EMAC0_TRTR = ((256/EMAC0_TRTR_TRT_SCALE)-1) << EMAC0_TRTR_TRT_SHIFT; // Receive FIFO watermarks EMAC0_RWMR = (0x1F<<EMAC0_RWMR_RLWM_SHIFT) | (0x10<<EMAC0_RWMR_RHWM_SHIFT); // 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; }