URL
https://opencores.org/ocsvn/openrisc/openrisc/trunk
Subversion Repositories openrisc
[/] [openrisc/] [trunk/] [rtos/] [ecos-3.0/] [packages/] [devs/] [eth/] [mips/] [upd985xx/] [current/] [src/] [if_upd985xx.c] - Rev 791
Go to most recent revision | Compare with Previous | Blame | View Log
//========================================================================== // // if_upd985xx.c // // Ethernet drivers // NEC UPD985XX device ethernet specific support // //========================================================================== // ####ECOSGPLCOPYRIGHTBEGIN#### // ------------------------------------------- // This file is part of eCos, the Embedded Configurable Operating System. // Copyright (C) 1998, 1999, 2000, 2001, 2002 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): hmt, gthomas // Contributors: // Date: 2001-06-28 // Purpose: // Description: hardware driver for uPD985xx ethernet devices // // //####DESCRIPTIONEND#### // //========================================================================== #include <pkgconf/system.h> #include <pkgconf/devs_eth_mips_upd985xx.h> #include <pkgconf/io_eth_drivers.h> #include <cyg/infra/cyg_type.h> #include <cyg/infra/cyg_ass.h> #include <cyg/hal/hal_arch.h> #include <cyg/hal/hal_intr.h> #include <cyg/hal/hal_cache.h> #include <cyg/infra/diag.h> #include <cyg/hal/drv_api.h> #include <cyg/io/eth/netdev.h> #include <cyg/io/eth/eth_drv.h> #ifdef CYGPKG_NET #include <pkgconf/net.h> #include <net/if.h> /* Needed for struct ifnet */ #else #include <cyg/hal/hal_if.h> #endif #include <cyg/devs/eth/upd985xx_eth.h> #include <cyg/io/eth/eth_drv_stats.h> // ------------------------------------------------------------------------ #ifdef CYGDBG_DEVS_ETH_MIPS_UPD985XX_CHATTER #define nDEBUG_TRAFFIC // This one prints stuff as packets come and go #define nDEBUG_IOCTL // ioctl() call printing #define DEBUG // Startup printing mainly #endif #define os_printf diag_printf #define db_printf diag_printf #define STATIC static // ------------------------------------------------------------------------ // I/O access macros as inlines for later changes to >1 device? // // (If we need to do this, then these macros would *assume* the // presence of a valid p_eth_upd985xx just like we always have) static inline void OUTL( volatile cyg_uint32 *io_address, cyg_uint32 value ) { *io_address = value; } static inline cyg_uint32 INL( volatile cyg_uint32 *io_address ) { return *io_address; } // These map cachable addresses to uncachable ones and vice versa. // This is all fixed on MIPS. 8-9xxxxxxx uncachable, A-Bxxxxxxx cachable. #define VIRT_TO_BUS( _x_ ) virt_to_bus((cyg_uint32)(_x_)) static inline cyg_uint8 *virt_to_bus(cyg_uint32 p_memory) { return (cyg_uint8 *)(0xa0000000u + (p_memory & ~0xe0000000u)); } #define BUS_TO_VIRT( _x_ ) bus_to_virt((cyg_uint32)(_x_)) static inline cyg_uint8 *bus_to_virt(cyg_uint32 p_memory) { return (cyg_uint8 *)(0x80000000u + (p_memory & ~0xe0000000u)); } // ------------------------------------------------------------------------ #ifdef CYGOPT_DEVS_ETH_MIPS_UPD985XX_HARDWARE_BUGS_S1 #define FLUSH_WRITES() CYG_MACRO_START \ (void) INL( ETH_RXSR ); \ CYG_MACRO_END #else #define FLUSH_WRITES() CYG_EMPTY_STATEMENT #endif // ------------------------------------------------------------------------ // // DEVICES AND PACKET QUEUES // // ------------------------------------------------------------------------ // 128 bytes extra for VLAN packets should be enough; AFAICT usually the // encapsulation is only 4 or 10 bytes extra. #define MAX_ETHERNET_PACKET_SIZE 1536 // Ethernet Rx packet size #define MAX_OVERSIZE_PACKET_SIZE 1664 // VLAN Rx packet size #define MAX_RX_PACKET_SIZE MAX_OVERSIZE_PACKET_SIZE #define NUM_RXBUFS (8) // This one is the hardware definition. struct bufdesc { volatile cyg_uint32 attr; cyg_uint8 *ptr; }; // Rx databuffer. STATIC cyg_uint8 rx_databuf[ NUM_RXBUFS ] [ MAX_RX_PACKET_SIZE ]; #ifdef CYGOPT_DEVS_ETH_MIPS_UPD985XX_HARDWARE_BUGS_E3 // Tx databuffer STATIC cyg_uint8 tx_databuf[ MAX_RX_PACKET_SIZE ]; #endif struct eth_upd985xx { cyg_uint8 active, index, tx_busy, mac_addr_ok; cyg_uint8 vector; // interrupt numbers are small cyg_uint8 phy_status; // from PHY_STATUS_ flags below cyg_uint8 hardwired_esa; #ifdef CYGOPT_DEVS_ETH_MIPS_UPD985XX_HARDWARE_BUGS_E1E2 cyg_uint8 promisc; #endif cyg_uint8 mac_address[6]; cyg_handle_t interrupt_handle; cyg_interrupt interrupt_object; struct cyg_netdevtab_entry *ndp; // these shall hold uncached addresses of the structures following... volatile struct bufdesc *txring; volatile struct bufdesc *rxring_active; volatile struct bufdesc *rxring_next; int rxring_active_index; int rxring_next_index; cyg_uint32 intrs; cyg_uint32 tx_keys[1]; // ----------------------------------------------------------------- // Statistics counters cyg_uint32 count_rx_resource; cyg_uint32 count_rx_restart; cyg_uint32 count_interrupts; cyg_uint32 count_bad_isr_restarts; cyg_uint32 count_bad_tx_completion; // ----------------------------------------------------------------- // DO NOT ACCESS THESE DIRECTLY - THE DEVICE HAS TO SEE THEM UNCACHED // Initially, enough for one whole transmission to be described in one go, // plus a null link on the end. struct bufdesc tx_bufdesc[ MAX_ETH_DRV_SG + 2 ]; // Pending rx buffers, of full size. struct bufdesc rx_bufdesc[ NUM_RXBUFS+1 ]; // ----------------------------------------------------------------- }; struct eth_upd985xx eth_upd985xx[CYGNUM_DEVS_ETH_MIPS_UPD985XX_DEV_COUNT] = { { index: 0, vector: CYGNUM_HAL_INTERRUPT_ETHER, #ifdef CYGOPT_DEVS_ETH_MIPS_UPD985XX_HARDWARE_BUGS_E1E2 promisc: 0, #endif #ifdef CYGSEM_DEVS_ETH_UPD985XX_ETH0_SET_ESA hardwired_esa: 1, mac_address: CYGDAT_DEVS_ETH_UPD985XX_ETH0_ESA, mac_addr_ok: 1, #else hardwired_esa: 0, mac_addr_ok: 0, #endif } }; // eth0 ETH_DRV_SC(upd985xx_sc0, ð_upd985xx[0], // Driver specific data CYGDAT_DEVS_ETH_UPD985XX_ETH0_NAME, // name for this interface eth_upd985xx_start, eth_upd985xx_stop, eth_upd985xx_ioctl, eth_upd985xx_can_send, eth_upd985xx_send, eth_upd985xx_recv, eth_upd985xx_deliver, eth_upd985xx_poll, eth_upd985xx_int_vector ); NETDEVTAB_ENTRY(upd985xx_netdev0, "upd985xx-" CYGDAT_DEVS_ETH_UPD985XX_ETH0_NAME, upd985xx_eth_upd985xx_init, &upd985xx_sc0); // This is in a macro so that if more devices arrive it can easily be changed #define CHECK_NDP_SC_LINK() CYG_MACRO_START \ CYG_ASSERT( ((void *)ndp == (void *)&upd985xx_netdev0), "Bad ndp" ); \ CYG_ASSERT( ((void *)sc == (void *)&upd985xx_sc0), "Bad sc" ); \ CYG_ASSERT( (void *)p_eth_upd985xx == sc->driver_private, \ "sc pointer bad" ); \ CYG_ASSERT( (void *)p_eth_upd985xx == (void *)ð_upd985xx[0], \ "bad p_eth_upd985x" ); \ CYG_MACRO_END #define NUM_ELEMENTS( _x_ ) (sizeof( (_x_) ) / sizeof( (_x_[0]) ) ) // ------------------------------------------------------------------------ // // FUNCTION PROTOTYPES // // ------------------------------------------------------------------------ STATIC void InitRxRing(struct eth_upd985xx* p_eth_upd985xx); STATIC void NextRxRing(struct eth_upd985xx* p_eth_upd985xx); STATIC void InitTxRing(struct eth_upd985xx* p_eth_upd985xx); STATIC void ResetTxRing(struct eth_upd985xx* p_eth_upd985xx); #ifdef CYGPKG_NET STATIC int eth_upd985xx_configure(struct eth_upd985xx* p_eth_upd985xx, int promisc, int oversized); #endif STATIC void PacketRxReady(struct eth_upd985xx* p_eth_upd985xx); STATIC void TxDone(struct eth_upd985xx* p_eth_upd985xx); #define PHY_STATUS_LINK (1) #define PHY_STATUS_FDX (2) #define PHY_STATUS_100MBPS (4) STATIC int eth_upd985xx_status( struct eth_upd985xx *p_eth_upd985xx ); STATIC int eth_set_mac_address( struct eth_upd985xx *p_eth_upd985xx, void *data ); // ------------------------------------------------------------------------ // // MII ACCESS TO PHY DEVICE // // ------------------------------------------------------------------------ STATIC cyg_bool mii_read( cyg_uint32 reg, cyg_uint32 *pvalue, struct eth_upd985xx *p_eth_upd985xx ) { int i = 1000; // wait a bit for it to be idle while ( 0 != ((ETH_MIND_NVALID | ETH_MIND_SCANA | ETH_MIND_BUSY) & INL(ETH_MIND)) ) if ( --i < 0 ) return false; // Tell it the register address and PHY address OUTL( ETH_MADR, ETH_MADR_PHY_DEVICE_PHYS_ADDRESS | reg ); OUTL( ETH_MCMD, ETH_MCMD_RSTAT ); // "do a read" // wait for the read to complete while ( 0 != ((ETH_MIND_NVALID | ETH_MIND_SCANA | ETH_MIND_BUSY) & INL(ETH_MIND)) ) if ( --i < 0 ) return false; // so get the data *pvalue = INL( ETH_MRDD ); return true; } #if 0 STATIC cyg_bool mii_write( cyg_uint32 reg, cyg_uint32 value, struct eth_upd985xx *p_eth_upd985xx ) { int i = 1000; // wait a bit for it to be idle while ( 0 != ((ETH_MIND_NVALID | ETH_MIND_SCANA | ETH_MIND_BUSY) & INL(ETH_MIND)) ) if ( --i < 0 ) return false; // Tell it the register address and PHY address OUTL( ETH_MADR, ETH_MADR_PHY_DEVICE_PHYS_ADDRESS | reg ); // And write the data: OUTL( ETH_MWTD, value ); // wait a bit for it to be idle while ( 0 != ((ETH_MIND_NVALID | ETH_MIND_SCANA | ETH_MIND_BUSY) & INL(ETH_MIND)) ) if ( --i < 0 ) return false; return true; } #endif // ------------------------------------------------------------------------ // // INTERRUPT HANDLERS // // ------------------------------------------------------------------------ STATIC cyg_uint32 eth_isr(cyg_vector_t vector, cyg_addrword_t data) { cyg_drv_interrupt_mask( vector ); return CYG_ISR_CALL_DSR; // schedule DSR } // ------------------------------------------------------------------------ // This is a callback from the higher level thread in consequence of the DSR STATIC void eth_upd985xx_deliver(struct eth_drv_sc *sc) { register int intrs; struct eth_upd985xx *p_eth_upd985xx; p_eth_upd985xx = (struct eth_upd985xx *)sc->driver_private; p_eth_upd985xx->count_interrupts++; intrs = INL( ETH_ISR ); // Read-clear // Acknowledge once at the start anyway to prevent an interrupt loop in // case of a transient - interrupts latch in the interrupt controller // as well as in the ethernet device. cyg_drv_interrupt_acknowledge(p_eth_upd985xx->vector); #ifdef DEBUG_TRAFFIC os_printf("\n[[[[[[[ Deliver intrs = %x\n", intrs ); #endif // Guard possible external entry points if ( ! p_eth_upd985xx->active ) return; // without unmasking the interrupt while ( intrs ) { if ( 0xffff0000 & intrs ) { // Then something very bad has happened p_eth_upd985xx->count_bad_isr_restarts++; CYG_ASSERT ( p_eth_upd985xx->active, "Device not active!" ); eth_upd985xx_stop( sc ); eth_upd985xx_start( sc, NULL, 0 ); intrs = INL( ETH_ISR ); // Read-clear } p_eth_upd985xx->intrs = intrs; if ( ( ETH_ISR_XMTDN | ETH_ISR_TABR ) & intrs ) { // Scan for completed Txen and inform the stack TxDone(p_eth_upd985xx); } if ( ( ETH_ISR_RCVDN | ETH_ISR_RBDRS | ETH_ISR_RBDRU ) & intrs ) { // Pass any rx data up the stack PacketRxReady(p_eth_upd985xx); } // Now we have made the interrupt causes go away, acknowledge and // *then* read the ISR again. That way the race can result in a // spurious interrupt rather than a lost interrupt. cyg_drv_interrupt_acknowledge(p_eth_upd985xx->vector); intrs = INL( ETH_ISR ); // Read-clear #ifdef DEBUG_TRAFFIC if ( intrs ) os_printf("------- Again intrs = %x\n", intrs ); #endif } #ifdef DEBUG_TRAFFIC os_printf("]]]]]]]] Done intrs = %x\n\n", intrs ); #endif cyg_drv_interrupt_unmask(p_eth_upd985xx->vector); } // ------------------------------------------------------------------------ // Device table entry to operate the chip in a polled mode. // Only diddle the interface we were asked to! STATIC void eth_upd985xx_poll(struct eth_drv_sc *sc) { struct eth_upd985xx *p_eth_upd985xx; p_eth_upd985xx = (struct eth_upd985xx *)sc->driver_private; // As it happens, this driver always requests the DSR to be called: (void)eth_isr( p_eth_upd985xx->vector, (cyg_addrword_t)sc ); eth_upd985xx_deliver( sc ); } // ------------------------------------------------------------------------ // Determine interrupt vector used by a device - for attaching GDB stubs // packet handler. STATIC int eth_upd985xx_int_vector(struct eth_drv_sc *sc) { struct eth_upd985xx *p_eth_upd985xx; p_eth_upd985xx = (struct eth_upd985xx *)sc->driver_private; return (p_eth_upd985xx->vector); } // ------------------------------------------------------------------------ STATIC int eth_set_mac_address( struct eth_upd985xx *p_eth_upd985xx, void *data ) { cyg_uint8 *p = (cyg_uint8 *)data; cyg_uint8 *mac_address; mac_address = &p_eth_upd985xx->mac_address[0]; mac_address[5] = p[5]; mac_address[4] = p[4]; mac_address[3] = p[3]; mac_address[2] = p[2]; mac_address[1] = p[1]; mac_address[0] = p[0]; p_eth_upd985xx->mac_addr_ok = 1; // Set the ESA in the device regs OUTL( ETH_LSA2, (p_eth_upd985xx->mac_address[1]) | (p_eth_upd985xx->mac_address[0] << 8 ) ); OUTL( ETH_LSA1, (p_eth_upd985xx->mac_address[5]) | (p_eth_upd985xx->mac_address[4] << 8 ) | (p_eth_upd985xx->mac_address[3] << 16 ) | (p_eth_upd985xx->mac_address[2] << 24) ); return 0; // OK } // ------------------------------------------------------------------------ STATIC void eth_upd985xx_reset( struct eth_upd985xx *p_eth_upd985xx ) { int i; // Reset whole device: Software Reset (clears automatically) OUTL( ETH_CCR, ETH_CCR_SRT ); for ( i = 0; i < 10000; i++ ) /* nothing */; // Reset internal units OUTL( ETH_MACC2, ETH_MACC2_MCRST | ETH_MACC2_RFRST | ETH_MACC2_TFRST ); for ( i = 0; i < 10000; i++ ) /* nothing */; FLUSH_WRITES(); OUTL( ETH_MACC2, 0 ); // (and release reset) // Enable CRC adding, padding FLUSH_WRITES(); OUTL( ETH_MACC1, ETH_MACC1_CRCEN | ETH_MACC1_PADEN | ETH_MACC1_TXFC | ETH_MACC1_RXFC | ETH_MACC1_PARF ); FLUSH_WRITES(); OUTL( ETH_MACC2, ETH_MACC2_APD ); // Auto VLAN pad FLUSH_WRITES(); OUTL( ETH_HT1, 0 ); FLUSH_WRITES(); OUTL( ETH_HT2, 0 ); // Enable rx of broadcasts, multicasts, but not promiscuous... FLUSH_WRITES(); #if defined( CYGOPT_DEVS_ETH_MIPS_UPD985XX_HARDWARE_BUGS_E1E2 ) && \ !defined( CYGOPT_DEVS_ETH_MIPS_UPD985XX_HARDWARE_BUGS_E1E2_E2ONLY ) // Unless we are faking it. OUTL( ETH_AFR, ETH_AFR_ABC | ETH_AFR_PRM | ETH_AFR_PRO ); #else OUTL( ETH_AFR, ETH_AFR_ABC | ETH_AFR_PRM ); #endif FLUSH_WRITES(); OUTL( ETH_IPGT, 0x00000013 ); FLUSH_WRITES(); OUTL( ETH_IPGR, 0x00000e13 ); FLUSH_WRITES(); OUTL( ETH_CLRT, 0x0000380f ); FLUSH_WRITES(); OUTL( ETH_LMAX, MAX_ETHERNET_PACKET_SIZE ); // Select a clock for the MII FLUSH_WRITES(); OUTL( ETH_MIIC, ETH_MIIC_66 ); // Example code sets to 66. // Set VLAN type reg FLUSH_WRITES(); OUTL( ETH_VLTP, ETH_VLTP_VLTP ); // Set the ESA in the device regs if ( p_eth_upd985xx->mac_addr_ok ) { FLUSH_WRITES(); OUTL( ETH_LSA2, (p_eth_upd985xx->mac_address[1]) | (p_eth_upd985xx->mac_address[0] << 8 ) ); FLUSH_WRITES(); OUTL( ETH_LSA1, (p_eth_upd985xx->mac_address[5]) | (p_eth_upd985xx->mac_address[4] << 8 ) | (p_eth_upd985xx->mac_address[3] << 16 ) | (p_eth_upd985xx->mac_address[2] << 24) ); } FLUSH_WRITES(); OUTL( ETH_RXFCR, ETH_RXFCR_UWM_DEFAULT | ETH_RXFCR_LWM_DEFAULT | ETH_RXFCR_DRTH16W ); FLUSH_WRITES(); // Fault E4 - use only 32 for FLTH, not the previously recommended 48 (words) // Tag: CYGOPT_DEVS_ETH_MIPS_UPD985XX_HARDWARE_BUGS_E4 // but no config opt is provided. OUTL( ETH_TXFCR, ETH_TXFCR_TPTV_DEFAULT | ETH_TXFCR_TX_DRTH_DEFAULT | (32 << ETH_TXFCR_TX_FLTH_SHIFT) ); // Transmit and receive config regs we hit when enabling those // functions separately, and the wet string end of the receiver // which is controlled by MACC1 |= ETH_MACC1_SRXEN; // Tx and Rx interrupts enabled internally; // Tx done/aborted, and rx OK. FLUSH_WRITES(); OUTL( ETH_MSR, ETH_ISR_XMTDN | ETH_ISR_TABR | ETH_ISR_RCVDN | ETH_ISR_RBDRS | ETH_ISR_RBDRU ); FLUSH_WRITES(); } // ------------------------------------------------------------------------ // // NETWORK INTERFACE INITIALIZATION // // ------------------------------------------------------------------------ STATIC bool upd985xx_eth_upd985xx_init(struct cyg_netdevtab_entry * ndp) { struct eth_drv_sc *sc; cyg_uint8 *mac_address; struct eth_upd985xx *p_eth_upd985xx; #ifdef DEBUG db_printf("upd985xx_eth_upd985xx_init\n"); #endif sc = (struct eth_drv_sc *)(ndp->device_instance); p_eth_upd985xx = (struct eth_upd985xx *)(sc->driver_private); CHECK_NDP_SC_LINK(); p_eth_upd985xx->tx_busy = 0; // record the net dev pointer p_eth_upd985xx->ndp = (void *)ndp; mac_address = &p_eth_upd985xx->mac_address[0]; #ifdef CYGSEM_DEVS_ETH_UPD985XX_ETH0_GET_EEPROM_ESA if ( ! p_eth_upd985xx->hardwired_esa ) { cyg_uint8 *p; union macar { struct { cyg_uint32 macar1, macar2, macar3; } integers; cyg_uint8 bytes[12]; } eeprom; eeprom.integers.macar1 = INL( MACAR1 ); // MAC Address Register 1 eeprom.integers.macar2 = INL( MACAR2 ); // MAC Address Register 2 eeprom.integers.macar3 = INL( MACAR3 ); // MAC Address Register 3 if ( (0 != eeprom.integers.macar1 || 0 != eeprom.integers.macar2 || 0 != eeprom.integers.macar3 ) && (0xffffffff != eeprom.integers.macar1 || 0xffffffff != eeprom.integers.macar2 || 0xffffffff != eeprom.integers.macar3 ) ) { // Then we have good data in the EEPROM #ifdef DEBUG os_printf( "EEPROM data %08x %08x %08x\n", eeprom.integers.macar1, eeprom.integers.macar2, eeprom.integers.macar3 ); #endif p = &eeprom.bytes[0]; // pick up either set of ESA info if ( 1 == p_eth_upd985xx->index ) p += 6; mac_address[5] = p[5]; mac_address[4] = p[4]; mac_address[3] = p[3]; mac_address[2] = p[2]; mac_address[1] = p[1]; mac_address[0] = p[0]; p_eth_upd985xx->mac_addr_ok = 1; } else { // Fake it so we can get RedBoot going on a board with no EEPROM mac_address[0] = 0; mac_address[1] = 0xBA; mac_address[2] = 0xD0; mac_address[3] = 0xEE; mac_address[4] = 0x00; mac_address[5] = p_eth_upd985xx->index; p_eth_upd985xx->mac_addr_ok = 1; } } #endif // CYGSEM_DEVS_ETH_UPD985XX_ETH0_GET_EEPROM_ESA // Init the underlying hardware and insert the ESA: eth_upd985xx_reset(p_eth_upd985xx); #ifdef DEBUG os_printf("MAC Address %s, ESA = %02X %02X %02X %02X %02X %02X\n", p_eth_upd985xx->mac_addr_ok ? "OK" : "**BAD**", mac_address[0], mac_address[1], mac_address[2], mac_address[3], mac_address[4], mac_address[5]); #endif // Set up the pointers to data structures InitTxRing(p_eth_upd985xx); // Construct the interrupt handler p_eth_upd985xx->active = 0; cyg_drv_interrupt_acknowledge(p_eth_upd985xx->vector); cyg_drv_interrupt_mask(p_eth_upd985xx->vector); cyg_drv_interrupt_create( p_eth_upd985xx->vector, 0, // Priority - unused (CYG_ADDRWORD)sc, // Data item passed to ISR & DSR eth_isr, // ISR eth_drv_dsr, // DSR (generic) &p_eth_upd985xx->interrupt_handle, // handle to intr obj &p_eth_upd985xx->interrupt_object ); // space for int obj cyg_drv_interrupt_attach(p_eth_upd985xx->interrupt_handle); // Initialize upper level driver if ( p_eth_upd985xx->mac_addr_ok ) (sc->funs->eth_drv->init)(sc, &(p_eth_upd985xx->mac_address[0]) ); else (sc->funs->eth_drv->init)(sc, 0 ); return (1); } // ------------------------------------------------------------------------ // // Function : eth_upd985xx_start // // ------------------------------------------------------------------------ STATIC void eth_upd985xx_start( struct eth_drv_sc *sc, unsigned char *enaddr, int flags ) { struct eth_upd985xx *p_eth_upd985xx; cyg_uint32 ss; #ifdef CYGPKG_NET struct ifnet *ifp = &sc->sc_arpcom.ac_if; #endif p_eth_upd985xx = (struct eth_upd985xx *)sc->driver_private; #ifdef DEBUG os_printf("eth_upd985xx_start %d flg %x\n", p_eth_upd985xx->index, *(int *)p_eth_upd985xx ); #endif if ( p_eth_upd985xx->active ) eth_upd985xx_stop( sc ); p_eth_upd985xx->active = 1; #ifdef CYGPKG_NET /* Enable promiscuous mode if requested, reception of oversized frames always. * The latter is needed for VLAN support and shouldn't hurt even if we're not * using VLANs. */ eth_upd985xx_configure(p_eth_upd985xx, !!(ifp->if_flags & IFF_PROMISC), 1); #endif // renegotiate link status p_eth_upd985xx->phy_status = eth_upd985xx_status( p_eth_upd985xx ); if ( p_eth_upd985xx->phy_status & PHY_STATUS_FDX ) { cyg_uint32 ss; // then enable full duplex in the MAC ss = INL( ETH_MACC1 ); ss |= ETH_MACC1_FDX; OUTL( ETH_MACC1, ss ); } #ifdef DEBUG { int status = p_eth_upd985xx->phy_status; os_printf("eth_upd985xx_start %d Link = %s, %s Mbps, %s Duplex\n", p_eth_upd985xx->index, status & PHY_STATUS_LINK ? "Up" : "Down", status & PHY_STATUS_100MBPS ? "100" : "10", status & PHY_STATUS_FDX ? "Full" : "Half" ); } #endif // Start the receive engine p_eth_upd985xx->count_rx_restart++; // Initialize all but one buffer: [B0,B1,B2,...Bx,NULL,LINK] InitRxRing( p_eth_upd985xx ); // Point the hardware at the list of buffers OUTL( ETH_RXDPR, (cyg_uint32)p_eth_upd985xx->rxring_active ); // Tell it about the buffers via the rx descriptor count OUTL( ETH_RXPDR, ETH_RXPDR_AL | (NUM_RXBUFS-1) ); // Ack any pending interrupts from the system p_eth_upd985xx->intrs = INL( ETH_ISR ); // Read-clear // Start the rx. OUTL( ETH_RXCR, ETH_RXCR_RXE | ETH_RXCR_DRBS_16 ); // Enable the wet string end of the receiver ss = INL( ETH_MACC1 ); ss |= ETH_MACC1_SRXEN; OUTL( ETH_MACC1, ss ); // And unmask the interrupt cyg_drv_interrupt_acknowledge(p_eth_upd985xx->vector); cyg_drv_interrupt_unmask(p_eth_upd985xx->vector); } // ------------------------------------------------------------------------ // // Function : eth_upd985xx_status; 10/100 and Full/Half Duplex (FDX/HDX) // // ------------------------------------------------------------------------ STATIC int eth_upd985xx_status( struct eth_upd985xx *p_eth_upd985xx ) { int status; int i, j; // Some of these bits latch and only reflect "the truth" on a 2nd reading. // So read and discard. mii_read( PHY_CONTROL_REG, &i, p_eth_upd985xx ); mii_read( PHY_STATUS_REG, &i, p_eth_upd985xx ); // Use the "and" of the local and remote capabilities words to infer // what is selected: status = 0; if ( mii_read( PHY_STATUS_REG, &i, p_eth_upd985xx ) ) { if ( PHY_STATUS_LINK_OK & i ) status |= PHY_STATUS_LINK; } if ( mii_read( PHY_AUTONEG_ADVERT, &j, p_eth_upd985xx ) && mii_read( PHY_AUTONEG_REMOTE, &i, p_eth_upd985xx ) ) { #if defined( DEBUG_TRAFFIC ) || defined( DEBUG_IOCTL ) os_printf( "MII: capabilities are %04x, %04x; common %04x\n", i, j, i & j ); #endif j &= i; // select only common capabilities if ( (PHY_AUTONEG_100BASET4 | PHY_AUTONEG_100BASETX_FDX | PHY_AUTONEG_100BASETX_HDX) & j ) status |= PHY_STATUS_100MBPS; if ( (PHY_AUTONEG_100BASETX_FDX | PHY_AUTONEG_10BASET_FDX) & j ) status |= PHY_STATUS_FDX; } return status; } // ------------------------------------------------------------------------ // // Function : eth_upd985xx_stop // // ------------------------------------------------------------------------ STATIC void eth_upd985xx_stop( struct eth_drv_sc *sc ) { struct eth_upd985xx *p_eth_upd985xx; p_eth_upd985xx = (struct eth_upd985xx *)sc->driver_private; // No more interrupts cyg_drv_interrupt_acknowledge(p_eth_upd985xx->vector); cyg_drv_interrupt_mask(p_eth_upd985xx->vector); #ifdef DEBUG os_printf("eth_upd985xx_stop %d flg %x\n", p_eth_upd985xx->index, *(int *)p_eth_upd985xx ); #endif p_eth_upd985xx->active = 0; // stop people tormenting it if ( p_eth_upd985xx->tx_busy ) { // Then it is finshed now, by force: cyg_uint32 key = p_eth_upd985xx->tx_keys[ 0 ]; // Turn off the transmitter (before the callback to the stack). OUTL( ETH_TXCR, 0 ); #ifdef DEBUG_TRAFFIC os_printf("Stop: tidying up TX, KEY %x\n", key ); #endif // Leave tx_busy true so no recursion can occur here. // Then tell the stack we are done: if ( key ) { (sc->funs->eth_drv->tx_done)( sc, key, 0 ); } } p_eth_upd985xx->tx_keys[ 0 ] = 0; p_eth_upd985xx->tx_busy = p_eth_upd985xx->active = 0; eth_upd985xx_reset(p_eth_upd985xx); ResetTxRing( p_eth_upd985xx ); } // ------------------------------------------------------------------------ // // Function : InitRxRing // // ------------------------------------------------------------------------ STATIC void InitRxRing(struct eth_upd985xx* p_eth_upd985xx) { int i; struct bufdesc *bp; // first just blat the various flags and addresses: the first N // bufdescs point to data buffers, the last one is NULL. bp = (struct bufdesc *)VIRT_TO_BUS( &p_eth_upd985xx->rx_bufdesc[0] ); // Record the initial active buffer: p_eth_upd985xx->rxring_active = bp; p_eth_upd985xx->rxring_active_index = 0; for ( i = 0; i < NUM_RXBUFS - 1; i++, bp++ ) { bp->ptr = VIRT_TO_BUS( &rx_databuf[i][0] ); bp->attr = ( ETH_BUF_D_L_DATA | ETH_BUF_OWN_CPU | (ETH_BUF_SIZE & sizeof( rx_databuf[0] )) ); } CYG_ASSERT( i == NUM_RXBUFS-1, "Penultimate rx buffer index mismatch" ); CYG_ASSERT( (cyg_uint8 *)bp == VIRT_TO_BUS( &p_eth_upd985xx->rx_bufdesc[NUM_RXBUFS-1] ), "Penultimate rx buffer address mismatch" ); // NULL out the penultimate one bp->ptr = NULL; bp->attr = 0; // And record it as next one to use p_eth_upd985xx->rxring_next = bp; p_eth_upd985xx->rxring_next_index = NUM_RXBUFS-1; // Step on to the extra entry at the end which makes a ring: bp++; CYG_ASSERT( (cyg_uint8 *)bp == VIRT_TO_BUS( &p_eth_upd985xx->rx_bufdesc[NUM_RXBUFS] ), "Ultimate rx buffer address mismatch" ); // Link the Ultimate back to the start bp->ptr = (cyg_uint8 *)p_eth_upd985xx->rxring_active; // Zeroth entry bp->attr = ETH_BUF_D_L_LINK; // All done. } // ------------------------------------------------------------------------ // // Function : NextRxRing // // ------------------------------------------------------------------------ STATIC void NextRxRing(struct eth_upd985xx* p_eth_upd985xx ) { volatile struct bufdesc *next, *dead; int iactive; int inext; iactive = p_eth_upd985xx->rxring_active_index; inext = p_eth_upd985xx->rxring_next_index; // Preconditions: CYG_ASSERT( 0 <= inext && inext < NUM_RXBUFS, "Bad inext" ); CYG_ASSERT( 0 <= iactive && iactive < NUM_RXBUFS, "Bad iactive" ); CYG_ASSERT( VIRT_TO_BUS( &p_eth_upd985xx->rx_bufdesc[ inext ] ) == (cyg_uint8 *)p_eth_upd985xx->rxring_next, "Next rx_bufdesc bad" ); CYG_ASSERT( VIRT_TO_BUS( &p_eth_upd985xx->rx_bufdesc[ iactive ] ) == (cyg_uint8 *)p_eth_upd985xx->rxring_active, "Active rx_bufdesc bad" ); CYG_ASSERT( ETH_BUF_D_L_LINK == p_eth_upd985xx->rxring_next->attr, "Next not a link" ); CYG_ASSERT( ETH_BUF_D_L_DATA & p_eth_upd985xx->rxring_active->attr, "Active not data" ); CYG_ASSERT( NULL == p_eth_upd985xx->rxring_next->ptr, "Next not NULL" ); CYG_ASSERT( VIRT_TO_BUS( &rx_databuf[iactive][0] ) == p_eth_upd985xx->rxring_active->ptr, "Active bad data pointer" ); CYG_ASSERT( (iactive - 1 == inext) || (0 == iactive && NUM_RXBUFS - 1 == inext), "Chasing pointers mismatch" ); // Select the new bufdesc to be active - ie. next to scan for reception: if ( ++iactive >= NUM_RXBUFS ) iactive = 0; dead = p_eth_upd985xx->rxring_active; // the one that just died // Step ahead the new active buffer: p_eth_upd985xx->rxring_active = (volatile struct bufdesc *) VIRT_TO_BUS( &p_eth_upd985xx->rx_bufdesc[ iactive ] ); p_eth_upd985xx->rxring_active_index = iactive; // Blow away the currently active entry; we have dealt with it already // and it is needed for an end stop to the ring: dead->ptr = NULL; dead->attr = 0; // Select the next bufdesc to enliven next = p_eth_upd985xx->rxring_next; next->ptr = VIRT_TO_BUS( &rx_databuf[inext][0] ); next->attr = ( ETH_BUF_D_L_DATA | ETH_BUF_OWN_CPU | (ETH_BUF_SIZE & sizeof( rx_databuf[0] )) ); // And update the external info to reflect this: if ( ++inext >= NUM_RXBUFS ) inext = 0; next = (volatile struct bufdesc *) VIRT_TO_BUS( &p_eth_upd985xx->rx_bufdesc[ inext ] ); p_eth_upd985xx->rxring_next = next; p_eth_upd985xx->rxring_next_index = inext; // Postconditions: CYG_ASSERT( 0 <= inext && inext < NUM_RXBUFS, "Bad inext" ); CYG_ASSERT( 0 <= iactive && iactive < NUM_RXBUFS, "Bad iactive" ); CYG_ASSERT( VIRT_TO_BUS( &p_eth_upd985xx->rx_bufdesc[ inext ] ) == (cyg_uint8 *)p_eth_upd985xx->rxring_next, "Next rx_bufdesc bad" ); CYG_ASSERT( VIRT_TO_BUS( &p_eth_upd985xx->rx_bufdesc[ iactive ] ) == (cyg_uint8 *)p_eth_upd985xx->rxring_active, "Active rx_bufdesc bad" ); CYG_ASSERT( ETH_BUF_D_L_LINK == p_eth_upd985xx->rxring_next->attr, "Next not a link" ); CYG_ASSERT( ETH_BUF_D_L_DATA & p_eth_upd985xx->rxring_active->attr, "Active not data" ); CYG_ASSERT( NULL == p_eth_upd985xx->rxring_next->ptr, "Next not NULL" ); CYG_ASSERT( VIRT_TO_BUS( &rx_databuf[iactive][0] ) == p_eth_upd985xx->rxring_active->ptr, "Active bad data pointer" ); CYG_ASSERT( (iactive - 1 == inext) || (0 == iactive && NUM_RXBUFS - 1 == inext), "Chasing pointers mismatch" ); } // ------------------------------------------------------------------------ // // Function : PacketRxReady (Called from delivery thread) // // ------------------------------------------------------------------------ STATIC void PacketRxReady(struct eth_upd985xx* p_eth_upd985xx) { struct cyg_netdevtab_entry *ndp; struct eth_drv_sc *sc; cyg_uint32 ss, length; cyg_bool reset_required = 0; ndp = (struct cyg_netdevtab_entry *)(p_eth_upd985xx->ndp); sc = (struct eth_drv_sc *)(ndp->device_instance); CHECK_NDP_SC_LINK(); #ifdef DEBUG_TRAFFIC ss = INL( ETH_RXSR ); os_printf("PacketRxReady: RXSR %x\n", ss ); #endif if ( ETH_ISR_RBDRU & p_eth_upd985xx->intrs ) reset_required = 1; // Out of buffers if ( ! ETH_ISR_RCVDN & p_eth_upd985xx->intrs ) reset_required = 1; // or if no reception completed, reset anyway // For all ready rx blocks... do { volatile struct bufdesc *bp; bp = p_eth_upd985xx->rxring_active; // Current rx candidate ss = bp->attr; #ifdef DEBUG_TRAFFIC os_printf("PacketRxReady attr %x at %x\n", ss, bp ); #endif if ( ETH_BUF_OWN_CPU == (ETH_BUF_OWN & ss) ) { // Then the packet is untouched... break; } #ifdef CYGOPT_DEVS_ETH_MIPS_UPD985XX_HARDWARE_BUGS_E1E2 // Perform address recognition by hand, hardware is in promisc mode // (we have settable "software" promisc mode too of course) if ( ETH_BUF_OK & ss ) { cyg_uint8 *esa = (cyg_uint8 *)bp->ptr; // (this is a non-cachable address) int ok = 0; if ( p_eth_upd985xx->promisc ) ok = 1; // accept the packet else if ( p_eth_upd985xx->mac_address[0] == esa[0] && p_eth_upd985xx->mac_address[1] == esa[1] && p_eth_upd985xx->mac_address[2] == esa[2] && p_eth_upd985xx->mac_address[3] == esa[3] && p_eth_upd985xx->mac_address[4] == esa[4] && p_eth_upd985xx->mac_address[5] == esa[5] ) ok = 1; // Then they are equal - accept else if ( 0xff == esa[0] && 0xff == esa[1] && 0xff == esa[2] && 0xff == esa[3] && 0xff == esa[4] && 0xff == esa[5] ) ok = 1; // Then they are equal - accept if ( !ok ) ss = 0; // Easiest way... } #endif if ( ETH_BUF_OK & ss ) { length = ETH_BUF_SIZE & ss; #ifdef DEBUG_TRAFFIC os_printf("PacketRxReady found a packet size %d attr %x\n", length, ss ); #endif // Asserts for the length in-range can fire, with good status // in the block, so be defensive here instead. Belt and braces. if ( 63 < length && length <= MAX_RX_PACKET_SIZE ) { CYG_ASSERT( ETH_BUF_D_L_DATA == (ETH_BUF_D_L & ss), "Not data buffer" ); CYG_ASSERT( length > 63, "Tiny packet" ); CYG_ASSERT( length <= MAX_RX_PACKET_SIZE, "Too big packet" ); CYG_ASSERT( ETH_BUF_LAST & ss, "Not last buffer" ); (sc->funs->eth_drv->recv)( sc, length ); } // Else drop it on the floor. } // Step along to the next buffer descriptor... NextRxRing( p_eth_upd985xx ); if ( ! reset_required ) { // And tell the device it can have a biscuit: OUTL( ETH_RXPDR, ETH_RXPDR_AL | 1 ); // Now, before moving on to the next packet, find out if receptions // had caught up with us before adding that new buffer: ss = INL( ETH_RXPDR ); ss &= ETH_RXPDR_RNOD; ss >>= ETH_RXPDR_RNOD_SHIFT; if ( 1 >= ss ) { // Then it was zero before. So the rx engine is stopped. #ifdef DEBUG_TRAFFIC os_printf( "***ZERO rx buffers were left\n" ); #endif reset_required = 1; } // Otherwise we carry on as usual. } } while ( 1 ); if ( reset_required ) { p_eth_upd985xx->count_rx_resource++; // Disable the wet string end of the receiver ss = INL( ETH_MACC1 ); ss &=~ETH_MACC1_SRXEN; OUTL( ETH_MACC1, ss ); // Disable the DMA engine OUTL( ETH_RXCR, 0 ); // Reset the RxRing from scratch InitRxRing( p_eth_upd985xx ); // Point the hardware at the list of buffers OUTL( ETH_RXDPR, (cyg_uint32)p_eth_upd985xx->rxring_active ); // Tell it about the buffers via the rx descriptor count: ss = INL( ETH_RXPDR ); ss &= ETH_RXPDR_RNOD; ss >>= ETH_RXPDR_RNOD_SHIFT; // This awful register *increments* by what you write, even if the // machinery is halted. Vile filthy evil rubbish. OUTL( ETH_RXPDR, ETH_RXPDR_AL | ((NUM_RXBUFS-1) - ss) ); ss = INL( ETH_RXPDR ); CYG_ASSERT( (ETH_RXPDR_AL | (NUM_RXBUFS-1)) == ss, "RXPDR not right" ); // Start the rx. OUTL( ETH_RXCR, ETH_RXCR_RXE | ETH_RXCR_DRBS_16 ); // Enable the wet string end of the receiver ss = INL( ETH_MACC1 ); ss |= ETH_MACC1_SRXEN; OUTL( ETH_MACC1, ss ); // All done. #ifdef DEBUG_TRAFFIC os_printf( "***Rx Machine restarted\n" ); #endif } } // and the callback function STATIC void eth_upd985xx_recv( struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len ) { struct eth_upd985xx *p_eth_upd985xx; int total_len; struct eth_drv_sg *last_sg; cyg_uint8 *from_p; volatile struct bufdesc *bp; p_eth_upd985xx = (struct eth_upd985xx *)sc->driver_private; // Guard possible external entry points if ( ! p_eth_upd985xx->active ) return; bp = p_eth_upd985xx->rxring_active; // Current rx candidate #ifdef DEBUG_TRAFFIC os_printf("Rx status %x\n", bp->attr ); #endif if ( 0 == (ETH_BUF_OK & bp->attr) ) return; total_len = ETH_BUF_SIZE & bp->attr; #ifdef DEBUG_TRAFFIC os_printf("Rx %d %x (status %x): %d sg's, %d bytes\n", p_eth_upd985xx->index, (int)p_eth_upd985xx, bp->attr, sg_len, total_len); #endif // Copy the data to the network stack from_p = bp->ptr; // (this is a non-cachable address) // check we have memory to copy into; we would be called even if // caller was out of memory in order to maintain our state. if ( 0 == sg_len || 0 == sg_list ) return; // caller was out of mbufs CYG_ASSERT( 0 < sg_len, "sg_len underflow" ); CYG_ASSERT( MAX_ETH_DRV_SG >= sg_len, "sg_len overflow" ); for ( last_sg = &sg_list[sg_len]; sg_list < last_sg; sg_list++ ) { cyg_uint8 *to_p; int l; to_p = (cyg_uint8 *)(sg_list->buf); l = sg_list->len; CYG_ASSERT( 0 <= l, "sg length -ve" ); if ( 0 >= l || 0 == to_p ) return; // caller was out of mbufs if ( l > total_len ) l = total_len; memcpy( to_p, from_p, l ); from_p += l; total_len -= l; } CYG_ASSERT( 0 == total_len, "total_len mismatch in rx" ); CYG_ASSERT( last_sg == sg_list, "sg count mismatch in rx" ); CYG_ASSERT( bp->ptr < from_p, "from_p wild in rx" ); CYG_ASSERT( bp->ptr + MAX_RX_PACKET_SIZE >= from_p, "from_p overflow in rx" ); } // ------------------------------------------------------------------------ // // Function : InitTxRing // // ------------------------------------------------------------------------ STATIC void InitTxRing(struct eth_upd985xx* p_eth_upd985xx) { int i; volatile struct bufdesc *bp; p_eth_upd985xx->txring = (struct bufdesc *)VIRT_TO_BUS( &p_eth_upd985xx->tx_bufdesc[0] ); bp = p_eth_upd985xx->txring; for ( i = 0; i < NUM_ELEMENTS( p_eth_upd985xx->tx_bufdesc ); i++, bp++ ) { bp->ptr = NULL; bp->attr = 0; } // Last one is a NULL link bp--; bp->ptr = NULL; bp->attr = ETH_BUF_D_L_LINK; ResetTxRing(p_eth_upd985xx); } // ------------------------------------------------------------------------ // // Function : ResetTxRing // // ------------------------------------------------------------------------ STATIC void ResetTxRing(struct eth_upd985xx* p_eth_upd985xx) { int i; volatile struct bufdesc *bp; bp = p_eth_upd985xx->txring; for ( i = 0; i < NUM_ELEMENTS( p_eth_upd985xx->tx_bufdesc ) - 1; i++, bp++ ) { bp->attr = ETH_BUF_LAST | ETH_BUF_D_L_DATA | ETH_BUF_OWN_CPU | (ETH_BUF_SIZE & 0); } } // ------------------------------------------------------------------------ // // Function : TxDone (Called from delivery thread) // // This returns Tx's from the Tx Machine to the stack (ie. reports // completion) - allowing for missed interrupts, and so on. // ------------------------------------------------------------------------ STATIC void TxDone(struct eth_upd985xx* p_eth_upd985xx) { struct cyg_netdevtab_entry *ndp; struct eth_drv_sc *sc; ndp = (struct cyg_netdevtab_entry *)(p_eth_upd985xx->ndp); sc = (struct eth_drv_sc *)(ndp->device_instance); CHECK_NDP_SC_LINK(); if ( p_eth_upd985xx->tx_busy ) { cyg_uint32 ss; ss = INL( ETH_TXSR ); // Get tx status if ( ss & (ETH_TXSR_CSE | ETH_TXSR_TUDR | ETH_TXSR_TGNT | ETH_TXSR_LCOL | ETH_TXSR_ECOL | ETH_TXSR_TEDFR | ETH_TXSR_TDFR | ETH_TXSR_TBRO | ETH_TXSR_TMUL | ETH_TXSR_TDONE ) ) { // Then it finished; somehow... cyg_uint32 key = p_eth_upd985xx->tx_keys[ 0 ]; // Turn off the transmitter (before the callback to the stack). OUTL( ETH_TXCR, 0 ); #ifdef CYGOPT_DEVS_ETH_MIPS_UPD985XX_HARDWARE_BUGS_E8 // Must take action after certain types of tx failure: if ( ss & (ETH_TXSR_TUDR | ETH_TXSR_LCOL | ETH_TXSR_ECOL) ) { p_eth_upd985xx->count_bad_tx_completion++; CYG_ASSERT ( p_eth_upd985xx->active, "Device not active!" ); eth_upd985xx_stop( sc ); eth_upd985xx_start( sc, NULL, 0 ); key = 0; // Important! Stop above already fed it back. } #endif #ifdef DEBUG_TRAFFIC os_printf("TxDone %d %x: KEY %x\n", p_eth_upd985xx->index, (int)p_eth_upd985xx, key ); #endif // Finished, ready for the next one p_eth_upd985xx->tx_keys[ 0 ] = 0; p_eth_upd985xx->tx_busy = 0; // Then tell the stack we are done: if (key) { (sc->funs->eth_drv->tx_done)( sc, key, 0 == (p_eth_upd985xx->intrs & ETH_ISR_TABR) ); } } } } // ------------------------------------------------------------------------ // // Function : eth_upd985xx_can_send // // ------------------------------------------------------------------------ STATIC int eth_upd985xx_can_send(struct eth_drv_sc *sc) { struct eth_upd985xx *p_eth_upd985xx; p_eth_upd985xx = (struct eth_upd985xx *)sc->driver_private; // Guard possible external entry points if ( ! p_eth_upd985xx->active ) return 0; return ! p_eth_upd985xx->tx_busy; } // ------------------------------------------------------------------------ // // Function : eth_upd985xx_send // // ------------------------------------------------------------------------ STATIC void eth_upd985xx_send(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len, int total_len, unsigned long key) { struct eth_upd985xx *p_eth_upd985xx; struct eth_drv_sg *last_sg; volatile struct bufdesc *bp; #ifdef CYGOPT_DEVS_ETH_MIPS_UPD985XX_HARDWARE_BUGS_E3 struct eth_drv_sg local_sg[2]; #endif p_eth_upd985xx = (struct eth_upd985xx *)sc->driver_private; #ifdef DEBUG_TRAFFIC os_printf("Tx %d %x: %d sg's, %d bytes, KEY %x\n", p_eth_upd985xx->index, (int)p_eth_upd985xx, sg_len, total_len, key ); #endif if ( ! p_eth_upd985xx->active ) return; // device inactive, no return CYG_ASSERT( ! p_eth_upd985xx->tx_busy, "Can't send when busy!" ); p_eth_upd985xx->tx_busy++; p_eth_upd985xx->tx_keys[0] = key; bp = &p_eth_upd985xx->txring[0]; // Current free tx CYG_ASSERT( 0 < sg_len, "sg_len underflow" ); CYG_ASSERT( MAX_ETH_DRV_SG >= sg_len, "sg_len overflow" ); #ifdef CYGOPT_DEVS_ETH_MIPS_UPD985XX_HARDWARE_BUGS_E3 // We must copy any Tx that is more than two SGs into just one buffer. if ( sg_len > 2 ) { cyg_uint8 *from_p, *to_p; to_p = &tx_databuf[0]; // normal cached address if ( sizeof( tx_databuf ) < total_len ) total_len = sizeof( tx_databuf ); for ( last_sg = &sg_list[sg_len]; sg_list < last_sg; sg_list++ ) { int l; from_p = (cyg_uint8 *)(sg_list->buf); // normal cached address l = sg_list->len; if ( l > total_len ) l = total_len; memcpy( to_p, from_p, l ); // All in cached memory to_p += l; total_len -= l; if ( 0 > total_len ) break; // Should exit via sg_last normally } // Set up SGs describing the single tx buffer total_len = to_p - &tx_databuf[0]; local_sg[0].buf = (CYG_ADDRESS)&tx_databuf[0]; local_sg[0].len = (CYG_ADDRWORD)total_len; local_sg[1].buf = (CYG_ADDRESS)0; local_sg[1].len = (CYG_ADDRWORD)0; // And make the subsequent code use it. sg_len = 1; sg_list = &local_sg[0]; } #endif 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 4120) HAL_DCACHE_STORE( ((CYG_ADDRESS)from_p) &~(HAL_DCACHE_LINE_SIZE-1), l + HAL_DCACHE_LINE_SIZE ); bp->ptr = VIRT_TO_BUS( from_p ); // uncached real RAM address bp->attr &=~(ETH_BUF_LAST | ETH_BUF_SIZE); bp->attr |= ETH_BUF_SIZE & l; bp->attr |= ETH_BUF_D_L_DATA; total_len -= l; bp++; if ( 0 > total_len ) break; // Should exit via sg_last normally } CYG_ASSERT( bp > &p_eth_upd985xx->txring[0], "bp underflow" ); CYG_ASSERT( bp < &p_eth_upd985xx->txring[ NUM_ELEMENTS(p_eth_upd985xx->tx_bufdesc) ], "bp underflow" ); bp--; bp->attr |= ETH_BUF_LAST; // Make the rest be null links for ( bp++; bp < &p_eth_upd985xx->txring[NUM_ELEMENTS(p_eth_upd985xx->tx_bufdesc)]; bp++ ) { bp->attr = ETH_BUF_D_L_LINK; bp->ptr = NULL; } CYG_ASSERT( 0 == total_len, "length mismatch in tx" ); CYG_ASSERT( last_sg == sg_list, "sg count mismatch in tx" ); // And start off the tx system // Point the hardware at the list of buffers OUTL( ETH_TXDPR, (cyg_uint32)p_eth_upd985xx->txring ); // and start the tx. // Fault E4 - use only 8 for DTBS, not the previously recommended 16. // Tag: CYGOPT_DEVS_ETH_MIPS_UPD985XX_HARDWARE_BUGS_E4 // but no config opt is provided. // Fault E7: ETH_TXCR_AFCE must not be used. // Tag: CYGOPT_DEVS_ETH_MIPS_UPD985XX_HARDWARE_BUGS_E7 // but no config opt is provided. OUTL( ETH_TXCR, ETH_TXCR_TXE | ETH_TXCR_DTBS_8 /* | ETH_TXCR_AFCE */ ); } #ifdef CYGPKG_NET // ------------------------------------------------------------------------ // // Function : eth_upd985xx_configure // // Return : 0 = It worked. // non0 = It failed. // ------------------------------------------------------------------------ STATIC int eth_upd985xx_configure(struct eth_upd985xx* p_eth_upd985xx, int promisc, int oversized) { int ss; // We implement permission of oversize packets by changing LMAX (rather // than enabling HUGEN in ETH_MACC1) because we rely on only one // reception per rx descriptor. General oversize packets could eat // many rx descriptors and we would become ...confused. // Sanity check the numbers we're about to use. CYG_ASSERT( sizeof( rx_databuf[0] ) >= MAX_OVERSIZE_PACKET_SIZE, "Oversize packet would overflow rx buffer" ); CYG_ASSERT( sizeof( rx_databuf[0] ) >= MAX_ETHERNET_PACKET_SIZE, "Ethernet packet would overflow rx buffer" ); if ( oversized ) OUTL( ETH_LMAX, MAX_OVERSIZE_PACKET_SIZE ); else OUTL( ETH_LMAX, MAX_ETHERNET_PACKET_SIZE ); #ifdef CYGOPT_DEVS_ETH_MIPS_UPD985XX_HARDWARE_BUGS_E1E2 ss = promisc ? 1 : 0; // avoid unused var warning p_eth_upd985xx->promisc = ss; #ifdef CYGOPT_DEVS_ETH_MIPS_UPD985XX_HARDWARE_BUGS_E1E2_E2ONLY // Then we must also set the mode in the chip ss = INL( ETH_AFR ); if ( promisc ) ss |= ETH_AFR_PRO; else ss &=~ETH_AFR_PRO; OUTL( ETH_AFR, ss ); #endif // CYGOPT_DEVS_ETH_MIPS_UPD985XX_HARDWARE_BUGS_E1E2_E2ONLY #else ss = INL( ETH_AFR ); if ( promisc ) ss |= ETH_AFR_PRO; else ss &=~ETH_AFR_PRO; OUTL( ETH_AFR, ss ); #endif return 0; // OK } #endif // ------------------------------------------------------------------------ // // Function : eth_upd985xx_ioctl // // ------------------------------------------------------------------------ STATIC int eth_upd985xx_ioctl(struct eth_drv_sc *sc, unsigned long key, void *data, int data_length) { struct eth_upd985xx *p_eth_upd985xx; p_eth_upd985xx = (struct eth_upd985xx *)sc->driver_private; #ifdef DEBUG_IOCTL db_printf( "eth_upd985xx_ioctl: device eth%d at %x; key is 0x%x, data at %x[%d]\n", p_eth_upd985xx->index, p_eth_upd985xx, key, data, data_length ); #endif // DO NOT guard possible external entry points - want to be able eg. to // set a mac address of a down interface before bringing it up! switch ( key ) { #ifdef ETH_DRV_SET_MAC_ADDRESS case ETH_DRV_SET_MAC_ADDRESS: if ( 6 != data_length ) return -2; return eth_set_mac_address( p_eth_upd985xx, data ); #endif #ifdef ETH_DRV_GET_IF_STATS_UD case ETH_DRV_GET_IF_STATS_UD: // UD == UPDATE #endif // drop through #ifdef ETH_DRV_GET_IF_STATS case ETH_DRV_GET_IF_STATS: #endif #if defined(ETH_DRV_GET_IF_STATS) || defined (ETH_DRV_GET_IF_STATS_UD) { struct ether_drv_stats *p = (struct ether_drv_stats *)data; int i; // Chipset entry is no longer supported; RFC1573. for ( i = 0; i < SNMP_CHIPSET_LEN; i++ ) p->snmp_chipset[i] = 0; // This perhaps should be a config opt, so you can make up your own // description, or supply it from the instantiation. strcpy( p->description, "NEC uPD985xx on-chip ethernet (CANDY)" ); // CYG_ASSERT( 48 > strlen(p->description), "Description too long" ); i = eth_upd985xx_status( p_eth_upd985xx ); if ( !( i & PHY_STATUS_LINK) ) { p->operational = 2; // LINK DOWN p->duplex = 1; // UNKNOWN p->speed = 0; } else { p->operational = 3; // LINK UP p->duplex = (i & PHY_STATUS_FDX) ? 3 : 2; // 2 = SIMPLEX, 3 = DUPLEX p->speed = ((i & PHY_STATUS_100MBPS) ? 100 : 10) * 1000000; } // Admit to it... p->supports_dot3 = true; // Those commented out are not available on this chip. p->tx_good = INL( ETH_TPCT ) ; p->tx_max_collisions = INL( ETH_TXCL ) ; p->tx_late_collisions = INL( ETH_TLCL ) ; //p->tx_underrun = INL( ) ; p->tx_carrier_loss = INL( ETH_TCSE ) ; p->tx_deferred = INL( ETH_TDFR ) + INL( ETH_TXDF ) ; //p->tx_sqetesterrors = INL( ) ; p->tx_single_collisions = INL( ETH_TSCL ) ; p->tx_mult_collisions = INL( ETH_TMCL ) ; p->tx_total_collisions = INL( ETH_TSCL ) + INL( ETH_TMCL ) + INL( ETH_TLCL ) + INL( ETH_TXCL ) ; p->rx_good = INL( ETH_RPKT ) ; p->rx_crc_errors = INL( ETH_RFCS ) ; p->rx_align_errors = INL( ETH_RALN ) ; p->rx_resource_errors = p_eth_upd985xx->count_rx_resource; //p->rx_overrun_errors = INL( ) ; //p->rx_collisions = INL( ) ; p->rx_short_frames = INL( ETH_RUND ) ; p->rx_too_long_frames = INL( ETH_ROVR ) ; p->rx_symbol_errors = INL( ETH_RXUO ) ; p->interrupts = p_eth_upd985xx->count_interrupts; p->rx_count = INL( ETH_RBYT ) ; p->rx_deliver = INL( ETH_RPKT ) ; p->rx_resource = p_eth_upd985xx->count_rx_resource; p->rx_restart = p_eth_upd985xx->count_rx_resource + p_eth_upd985xx->count_rx_restart; p->tx_count = INL( ETH_TBYT ) ; p->tx_complete = INL( ETH_TPCT ) ; p->tx_dropped = INL( ETH_TNCL ) ; p->tx_queue_len = 1; return 0; // OK } #endif default: break; } return -1; } // ------------------------------------------------------------------------ // EOF if_upd985xx.c
Go to most recent revision | Compare with Previous | Blame | View Log