URL
https://opencores.org/ocsvn/openrisc_me/openrisc_me/trunk
Subversion Repositories openrisc_me
[/] [openrisc/] [trunk/] [rtos/] [ecos-2.0/] [packages/] [devs/] [eth/] [sh/] [etherc/] [v2_0/] [src/] [if_etherc.c] - Rev 174
Compare with Previous | Blame | View Log
//========================================================================== // // dev/if_etherc.c // // Ethernet device driver for SH EtherC CPU module controller // //========================================================================== //####ECOSGPLCOPYRIGHTBEGIN#### // ------------------------------------------- // This file is part of eCos, the Embedded Configurable Operating System. // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, 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., // 59 Temple Place, Suite 330, Boston, MA 02111-1307 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. // // This exception does not invalidate any other reasons why a work based on // this file might be covered by the GNU General Public License. // // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. // at http://sources.redhat.com/ecos/ecos-license/ // ------------------------------------------- //####ECOSGPLCOPYRIGHTEND#### //========================================================================== //#####DESCRIPTIONBEGIN#### // // Author(s): jskov // Contributors: jskov // Date: 2002-01-30 // Purpose: // Description: Hardware driver for SH EtherC CPU module controller // // Notes: The KEEP_STATISTICS code is not implemented yet. Look // for FIXME macro. // //####DESCRIPTIONEND#### // //========================================================================== #include <pkgconf/system.h> #include <pkgconf/devs_eth_sh_etherc.h> #include <pkgconf/io_eth_drivers.h> #include <cyg/infra/cyg_type.h> #include <cyg/hal/hal_arch.h> #include <cyg/hal/hal_intr.h> #include <cyg/infra/cyg_ass.h> #include <cyg/infra/diag.h> #include <cyg/hal/drv_api.h> #include <cyg/hal/hal_if.h> // delays #include <string.h> #include <cyg/io/eth/netdev.h> #include <cyg/io/eth/eth_drv.h> #ifdef CYGPKG_NET #include <pkgconf/net.h> #include <cyg/kernel/kapi.h> #include <net/if.h> /* Needed for struct ifnet */ #include <pkgconf/io_eth_drivers.h> #endif #include CYGHWR_MEMORY_LAYOUT_H #define FIXME 0 // Set size so it is 16-byte aligned #define _BUF_SIZE (1544+8) #define IEEE_8023_MIN_FRAME 64 // Smallest possible ethernet frame #ifdef CYGPKG_INFRA_DEBUG // Then we log, OOI, the number of times we get a bad packet number // from the tx done fifo. int etherc_txfifo_good = 0; int etherc_txfifo_bad = 0; #endif #include "sh_etherc.h" #define __WANT_DEVS #include CYGDAT_DEVS_ETH_SH_ETHERC_INL #undef __WANT_DEVS static void etherc_poll(struct eth_drv_sc *sc); static cyg_uint16 etherc_read_MII(struct etherc_priv_data *cpd, int id, int reg); static void etherc_write_MII(struct etherc_priv_data *cpd, int id, int reg, cyg_uint16 value); #define _MII_READ(_priv_, _id_, _reg_) etherc_read_MII(_priv_, _id_, _reg_) #define _MII_WRITE(_priv_, _id_, _reg_, _val_) etherc_write_MII(_priv_, _id_, _reg_, _val_) #define _MII_HAS_EXTENDED #include "phyter.inl" #ifndef CYGPKG_IO_ETH_DRIVERS_STAND_ALONE // This ISR is called when the ethernet interrupt occurs static cyg_uint32 etherc_isr(cyg_vector_t vector, cyg_addrword_t data) { struct etherc_priv_data *cpd = (struct etherc_priv_data *)data; DEBUG_FUNCTION(); INCR_STAT( interrupts ); cyg_drv_interrupt_mask(cpd->interrupt); cyg_drv_interrupt_acknowledge(cpd->interrupt); return (CYG_ISR_HANDLED|CYG_ISR_CALL_DSR); // Run the DSR } static void etherc_dsr(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data) { // This conditioning out is necessary because of explicit calls to this // DSR - which would not ever be called in the case of a polled mode // usage ie. in RedBoot. #ifdef CYGPKG_IO_ETH_DRIVERS_NET struct etherc_priv_data* cpd = (struct etherc_priv_data *)data; struct cyg_netdevtab_entry *ndp = (struct cyg_netdevtab_entry *)(cpd->ndp); struct eth_drv_sc *sc = (struct eth_drv_sc *)(ndp->device_instance); // but here, it must be a *sc: eth_drv_dsr( vector, count, (cyg_addrword_t)sc ); #else # ifndef CYGPKG_REDBOOT # error Empty Etherc ethernet DSR is compiled. Is this what you want? # endif #endif } #endif // CYGPKG_IO_ETH_DRIVERS_STAND_ALONE // The deliver function (ex-DSR) handles the ethernet [logical] processing static void etherc_deliver(struct eth_drv_sc *sc) { struct etherc_priv_data *cpd = (struct etherc_priv_data *)sc->driver_private; DEBUG_FUNCTION(); // Service the interrupt: etherc_poll(sc); // Allow interrupts to happen again cyg_drv_interrupt_unmask(cpd->interrupt); } static int etherc_int_vector(struct eth_drv_sc *sc) { struct etherc_priv_data *cpd = (struct etherc_priv_data *)sc->driver_private; return (cpd->interrupt); } static bool sh_etherc_init(struct cyg_netdevtab_entry *tab) { struct eth_drv_sc *sc = (struct eth_drv_sc *)tab->device_instance; struct etherc_priv_data *cpd = (struct etherc_priv_data *)sc->driver_private; cyg_uint8 *p, *d; cyg_uint32 reg; int i; bool esa_configured = true; DEBUG_FUNCTION(); cpd->txbusy = 0; // Ensure that addresses of ring descriptors and buffers are properly aligned // and in uncached memory. cpd->rx_buffers = (cyg_uint8*)CYGARC_UNCACHED_ADDRESS(((cyg_uint32)cpd->rx_buffers+16-1) & ~(16-1)); cpd->rx_ring = (cyg_uint8*)CYGARC_UNCACHED_ADDRESS(((cyg_uint32)cpd->rx_ring+16-1) & ~(16-1)); cpd->tx_buffers = (cyg_uint8*)CYGARC_UNCACHED_ADDRESS(((cyg_uint32)cpd->tx_buffers+16-1) & ~(16-1)); cpd->tx_ring = (cyg_uint8*)CYGARC_UNCACHED_ADDRESS(((cyg_uint32)cpd->tx_ring+16-1) & ~(16-1)); #if DEBUG & 8 db_printf("Etherc at base 0x%08x\n", cpd->base); #endif // Find ESA - check possible sources in sequence and stop when // one provides the ESA: // RedBoot option (via provide_esa) // Compile-time configuration // EEPROM // <fail configuration of device> if (NULL != cpd->provide_esa) { esa_configured = cpd->provide_esa(cpd); # if DEBUG & 8 if (esa_configured) diag_printf("Got ESA from RedBoot option\n"); # endif } if (!esa_configured && cpd->hardwired_esa) { // ESA is already set in cpd->esa[] esa_configured = true; } if (!esa_configured) { # if DEBUG & 8 diag_printf("EtherC - no EEPROM, static ESA or RedBoot config option.\n"); # endif return false; } #if DEBUG & 9 db_printf("ETHERC ESA: %02x:%02x:%02x:%02x:%02x:%02x\n", cpd->esa[0], cpd->esa[1], cpd->esa[2], cpd->esa[3], cpd->esa[4], cpd->esa[5] ); #endif // Prepare RX and TX rings p = cpd->rx_ring; d = cpd->rx_buffers; for (i = 0; i < cpd->rx_ring_cnt; i++) { _SU32(p, ETHERC_RD_STAT) = ETHERC_RD_STAT_RACT | ETHERC_RD_STAT_RFP_OTO; _SU16(p, ETHERC_RD_RBL) = _BUF_SIZE; _SU16(p, ETHERC_RD_RDL) = 0; _SU32(p, ETHERC_RD_RBA) = (cyg_uint32)d; _SU32(p, ETHERC_RD_PAD) = 0; p += ETHERC_RD_SIZE; d += _BUF_SIZE; } // Set ring-end marker p -= ETHERC_RD_SIZE; _SU32(p, ETHERC_RD_STAT) |= ETHERC_RD_STAT_RDLE; cpd->rx_ring_next = 0; p = cpd->tx_ring; d = cpd->tx_buffers; for (i = 0; i < cpd->tx_ring_cnt; i++) { _SU32(p, ETHERC_TD_STAT) = ETHERC_TD_STAT_TFP_OTO; _SU16(p, ETHERC_TD_TDL) = 0; _SU16(p, ETHERC_TD_PAD0) = 0; _SU32(p, ETHERC_TD_TBA) = (cyg_uint32)d; _SU32(p, ETHERC_TD_PAD1) = 0; p += ETHERC_TD_SIZE; d += _BUF_SIZE; } // Set ring-end marker p -= ETHERC_RD_SIZE; _SU32(p, ETHERC_TD_STAT) |= ETHERC_TD_STAT_TDLE; cpd->tx_ring_free = cpd->tx_ring_alloc = cpd->tx_ring_owned = 0; // Reset ethernet module, then wait for (at least) 16 clocks. put_reg(cpd, _REG_EDMR, CYGARC_REG_EDMR_SWR | CYGARC_REG_EDMR_DL16); for (i = 0; i < 16; i++); put_reg(cpd, _REG_EDMR, CYGARC_REG_EDMR_DL16); // Program ring data into controller put_reg(cpd, _REG_RDLAR, (cyg_uint32)cpd->rx_ring); put_reg(cpd, _REG_TDLAR, (cyg_uint32)cpd->tx_ring); // Set ESA put_reg(cpd, _REG_MAHR, (cpd->esa[0] << 24) | (cpd->esa[1] << 16) | (cpd->esa[2] << 8) | cpd->esa[3]); put_reg(cpd, _REG_MALR, (cpd->esa[4] << 8) | cpd->esa[5]); // Set receive mode: receive continuously put_reg(cpd, _REG_RCR, CYGARC_REG_RCR_RNC); // Stop controller, set duplex mode put_reg(cpd, _REG_ECMR, CYGARC_REG_ECMR_DM); cpd->active = 0; // Clear pending interrupt flags reg = get_reg(cpd, _REG_EESR); put_reg(cpd, _REG_EESR, reg); #ifndef CYGPKG_IO_ETH_DRIVERS_STAND_ALONE // Attach ISR/DSRs. cpd->interrupt = CYGNUM_HAL_INTERRUPT_EDMAC_EINT; cyg_drv_interrupt_create(cpd->interrupt, 1, // Priority (cyg_addrword_t)cpd, // Data item passed to ISR & DSR etherc_isr, // ISR etherc_dsr, // DSR &cpd->interrupt_handle, // handle to intr obj &cpd->interrupt_object ); // space for int obj cyg_drv_interrupt_attach(cpd->interrupt_handle); cyg_drv_interrupt_unmask(cpd->interrupt); put_reg(cpd, _REG_EESIPR, CYGARC_REG_EESIPR_TCIP|CYGARC_REG_EESIPR_FRIP); #if DEBUG & 8 db_printf("Attached interrupt on vector %d\n", cpd->interrupt); #endif #endif // Record the net dev pointer cpd->ndp = (void *)tab; // Initialize the PHY #ifdef CYGSEM_DEVS_ETH_SH_ETHERC_FORCE_10MBPS #if DEBUG & 8 db_printf("Forcing 10Mb link\n"); #endif _MII_SPEED_FORCE_10MB(cpd, 1); _MII_RENEGOTIATE(cpd, 1); #else // Wait for automatic negotiation to complete _MII_RENEGOTIATION_WAIT(cpd, 1); #endif // Initialize upper level driver (sc->funs->eth_drv->init)(sc, cpd->esa); #if DEBUG & 9 db_printf("Done\n"); #endif return true; } static void etherc_suspend(struct etherc_priv_data *cpd) { cyg_uint32 reg; #if 0 bool still_active; #endif reg = get_reg(cpd, _REG_ECMR); reg &= ~(CYGARC_REG_ECMR_TE | CYGARC_REG_ECMR_RE); put_reg(cpd, _REG_ECMR, reg); #if 0 // // Try to find out if controller stopped. Supposedly, it should // communicate this by clearing the active signal of the active // RX/TX descriptors. // Check RX do { still_active = false; reg = get_reg(cpd, _REG_RDFAR); #if 1 { int i; cyg_uint8* p; p = cpd->rx_ring; for (i = 0; i < cpd->rx_ring_cnt; i++) { if ((cyg_uint32)p == reg) { if (_SU32(reg, ETHERC_RD_STAT) & ETHERC_RD_STAT_RACT) { still_active = true; break; } } } } #else if (_SU32(reg, ETHERC_RD_STAT) & ETHERC_RD_STAT_RACT) still_active = true; #endif } while (still_active); // Check TX do { still_active = false; reg = get_reg(cpd, _REG_TDFAR); #if 1 { int i; cyg_uint8* p; p = cpd->tx_ring; for (i = 0; i < cpd->tx_ring_cnt; i++) { if ((cyg_uint32)p == reg) { if (_SU32(reg, ETHERC_TD_STAT) & ETHERC_TD_STAT_TACT) { still_active = true; break; } } } } #else if (_SU32(reg, ETHERC_TD_STAT) & ETHERC_TD_STAT_TACT) still_active = true; #endif } while (still_active); db_printf("all done\n"); #endif cpd->active = 0; } static void etherc_stop(struct eth_drv_sc *sc) { struct etherc_priv_data *cpd = (struct etherc_priv_data *)sc->driver_private; DEBUG_FUNCTION(); etherc_suspend(cpd); } // // 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 etherc_start(struct eth_drv_sc *sc, unsigned char *enaddr, int flags) { cyg_uint32 reg, prm_mode = 0; struct etherc_priv_data *cpd = (struct etherc_priv_data *)sc->driver_private; #ifdef CYGPKG_NET struct ifnet *ifp = &sc->sc_arpcom.ac_if; #endif DEBUG_FUNCTION(); // If device is already active, suspend it if (cpd->active) { etherc_suspend(cpd); } #ifdef CYGPKG_NET if (( 0 #ifdef ETH_DRV_FLAGS_PROMISC_MODE != (flags & ETH_DRV_FLAGS_PROMISC_MODE) #endif ) || (ifp->if_flags & IFF_PROMISC) ) { // Then we select promiscuous mode. prm_mode = CYGARC_REG_ECMR_PRM; } #endif // Unsuspend the device reg = get_reg(cpd, _REG_ECMR); reg |= CYGARC_REG_ECMR_TE | CYGARC_REG_ECMR_RE; reg &= ~CYGARC_REG_ECMR_PRM; reg |= prm_mode; put_reg(cpd, _REG_ECMR, reg); put_reg(cpd, _REG_EDRRR, CYGARC_REG_EDRRR_RR); cpd->active = 1; } // // This routine is called to perform special "control" opertions // static int etherc_control(struct eth_drv_sc *sc, unsigned long key, void *data, int data_length) { cyg_uint8 *esa = (cyg_uint8 *)data; int i, res; cyg_uint16 reg; struct etherc_priv_data *cpd = (struct etherc_priv_data *)sc->driver_private; cyg_bool was_active = cpd->active; DEBUG_FUNCTION(); // If device is already active, suspend it if (cpd->active) { etherc_suspend(cpd); } res = 0; // expect success switch (key) { case ETH_DRV_SET_MAC_ADDRESS: #if 9 & DEBUG db_printf("ETHERC - set ESA: %02x:%02x:%02x:%02x:%02x:%02x\n", esa[0], esa[1], esa[2], esa[3], esa[4], esa[5] ); #endif // DEBUG for ( i = 0; i < sizeof(cpd->esa); i++ ) cpd->esa[i] = esa[i]; put_reg(cpd, _REG_MAHR,(cpd->esa[0] << 24) | (cpd->esa[1] << 16) | (cpd->esa[2] << 8) | cpd->esa[3]); put_reg(cpd, _REG_MALR, (cpd->esa[4] << 8) | cpd->esa[5]); break; #ifdef ETH_DRV_GET_MAC_ADDRESS case ETH_DRV_GET_MAC_ADDRESS: // Extract the MAC address that is in the chip, and tell the // system about it. cyg_uint32 reg; reg = get_reg(cpd, _REG_MAHR); cpd->esa[0] = (reg >> 24) & 0xff; cpd->esa[1] = (reg >> 16) & 0xff; cpd->esa[2] = (reg >> 08) & 0xff; cpd->esa[3] = (reg >> 00) & 0xff; reg = get_reg(cpd, _REG_MALR); cpd->esa[4] = (reg >> 08) & 0xff; cpd->esa[5] = (reg >> 00) & 0xff; break; #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; // 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, "SH Etherc" ); // CYG_ASSERT( 48 > strlen(p->description), "Description too long" ); if (_MII_LINK_STATE(cpd, 1)) { // Link is up p->operational = 3; // LINK UP if (_MII_DUPLEX_STATE(cpd, 1)) p->duplex = 3; // 3 = DUPLEX else p->duplex = 2; // 2 = SIMPLEX if (_MII_SPEED_STATE(cpd, 1)) p->speed = 100 * 1000000; else p->speed = 10 * 1000000; } else { // Link is down p->operational = 2; // LINK DOWN p->duplex = 1; // UNKNOWN p->speed = 0; } #if FIXME #ifdef KEEP_STATISTICS { struct sh_etherc_stats *ps = &(cpd->stats); // Admit to it... p->supports_dot3 = true; p->tx_good = ps->tx_good ; p->tx_max_collisions = ps->tx_max_collisions ; p->tx_late_collisions = ps->tx_late_collisions ; p->tx_underrun = ps->tx_underrun ; p->tx_carrier_loss = ps->tx_carrier_loss ; p->tx_deferred = ps->tx_deferred ; p->tx_sqetesterrors = ps->tx_sqetesterrors ; p->tx_single_collisions = ps->tx_single_collisions; p->tx_mult_collisions = ps->tx_mult_collisions ; p->tx_total_collisions = ps->tx_total_collisions ; p->rx_good = ps->rx_good ; p->rx_crc_errors = ps->rx_crc_errors ; p->rx_align_errors = ps->rx_align_errors ; p->rx_resource_errors = ps->rx_resource_errors ; p->rx_overrun_errors = ps->rx_overrun_errors ; p->rx_collisions = ps->rx_collisions ; p->rx_short_frames = ps->rx_short_frames ; p->rx_too_long_frames = ps->rx_too_long_frames ; p->rx_symbol_errors = ps->rx_symbol_errors ; p->interrupts = ps->interrupts ; p->rx_count = ps->rx_count ; p->rx_deliver = ps->rx_deliver ; p->rx_resource = ps->rx_resource ; p->rx_restart = ps->rx_restart ; p->tx_count = ps->tx_count ; p->tx_complete = ps->tx_complete ; p->tx_dropped = ps->tx_dropped ; } #endif // KEEP_STATISTICS #endif // FIXME p->tx_queue_len = 1; break; } #endif default: res = 1; break; } // Restore controller state if (was_active) { // Unsuspend the device reg = get_reg(cpd, _REG_ECMR); reg |= CYGARC_REG_ECMR_RE | CYGARC_REG_ECMR_TE; put_reg(cpd, _REG_ECMR, reg); } return res; } // // This routine is called to see if it is possible to send another packet. // It will return non-zero if a transmit is possible, zero otherwise. // static int etherc_can_send(struct eth_drv_sc *sc) { struct etherc_priv_data *cpd = (struct etherc_priv_data *)sc->driver_private; DEBUG_FUNCTION(); if (!_MII_LINK_STATE(cpd, 1)) return 0; // Link not connected return (0 == cpd->txbusy); } // // This routine is called to send data to the hardware. static void etherc_send(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len, int total_len, unsigned long key) { struct etherc_priv_data *cpd = (struct etherc_priv_data *)sc->driver_private; int i, len, plen, ring_entry; cyg_uint8* sdata = NULL; cyg_uint8 *d, *buf, *txd; DEBUG_FUNCTION(); INCR_STAT( tx_count ); cpd->txbusy = 1; cpd->txkey = key; // Find packet length plen = 0; for (i = 0; i < sg_len; i++) plen += sg_list[i].len; CYG_ASSERT( plen == total_len, "sg data length mismatch" ); // Get next TX descriptor ring_entry = cpd->tx_ring_free; do { if (cpd->tx_ring_owned == cpd->tx_ring_cnt) { // Is this a dead end? Probably is. #if DEBUG & 1 db_printf("%s: Allocation failed! Retrying...\n", __FUNCTION__ ); #endif continue; } cpd->tx_ring_free++; cpd->tx_ring_owned++; if (cpd->tx_ring_free == cpd->tx_ring_cnt) cpd->tx_ring_free = 0; } while (0); txd = cpd->tx_ring + ring_entry*ETHERC_TD_SIZE; buf = cpd->tx_buffers + ring_entry*_BUF_SIZE; CYG_ASSERT(0 == (_SU32(txd, ETHERC_TD_STAT) & ETHERC_TD_STAT_TACT), "TX descriptor not free"); #if DEBUG & 4 db_printf("#####Tx descriptor 0x%08x buffer 0x%08x\n", txd, buf); #endif // Put data into buffer d = buf; for (i = 0; i < sg_len; i++) { sdata = (cyg_uint8 *)sg_list[i].buf; len = sg_list[i].len; CYG_ASSERT( sdata, "No sg data pointer here" ); while(len--) *d++ = *sdata++; } CYG_ASSERT( sdata, "No sg data pointer outside" ); if (plen < IEEE_8023_MIN_FRAME) { #if DEBUG & 1 db_printf("Packet too short (%d) - padding to %d bytes.\n", plen, IEEE_8023_MIN_FRAME); #endif for (i = plen; i < IEEE_8023_MIN_FRAME; i++) *d++ = 0; plen = IEEE_8023_MIN_FRAME; } _SU16(txd, ETHERC_TD_TDL) = plen; _SU32(txd, ETHERC_TD_STAT) |= ETHERC_TD_STAT_TACT; #if DEBUG & 1 db_printf("Last TX: LEN %04x STAT %08x PTR %08x\n", _SU16(txd, ETHERC_TD_TDL), _SU32(txd, ETHERC_TD_STAT), _SU32(txd, ETHERC_TD_TBA)); #endif // Set transmit demand put_reg(cpd, _REG_EDTRR, CYGARC_REG_EDTRR_TR); #if DEBUG & 1 { cyg_uint32 reg; reg = get_reg(cpd, _REG_EESR); db_printf("%s:END: EESR at TX: 0x%08x\n", __FUNCTION__, reg); } #endif } static void etherc_TxEvent(struct eth_drv_sc *sc, int stat) { struct etherc_priv_data *cpd = (struct etherc_priv_data *)sc->driver_private; int success = 1; cyg_uint8 *txd; cyg_uint32 pkt_stat; DEBUG_FUNCTION(); if (0 == cpd->tx_ring_owned) { #if DEBUG & 1 db_printf("%s: got TX completion when no outstanding packets\n", __FUNCTION__); #endif // Ack the TX int put_reg(cpd, _REG_EESR, CYGARC_REG_EESR_TC); return; } INCR_STAT( tx_complete ); txd = cpd->tx_ring + cpd->tx_ring_alloc*ETHERC_TD_SIZE; pkt_stat = _SU32(txd, ETHERC_TD_STAT); if (pkt_stat & ETHERC_TD_STAT_TACT) { #if DEBUG & 1 db_printf("%s: got TX completion when buffer is still owned\n", __FUNCTION__); #endif // first dirty ring entry not freed - wtf? } if (pkt_stat & ETHERC_TD_STAT_TDFE) { // We had an error. Tell the stack. success = 0; #if DEBUG & 1 db_printf("%s: TX failure, retrying...\n", __FUNCTION__); #endif } cpd->tx_ring_alloc++; if (cpd->tx_ring_alloc == cpd->tx_ring_cnt) cpd->tx_ring_alloc = 0; cpd->tx_ring_owned--; #if FIXME #ifdef KEEP_STATISTICS { cyg_uint16 reg; reg = get_reg( sc, ETHERC_CSR_CSCR ); // Covering each bit in turn... if ( reg & ETHERC_STATUS_TX_UNRN ) INCR_STAT( tx_underrun ); //if ( reg & ETHERC_STATUS_LINK_OK ) INCR_STAT( ); //if ( reg & ETHERC_STATUS_CTR_ROL ) INCR_STAT( ); //if ( reg & ETHERC_STATUS_EXC_DEF ) INCR_STAT( ); if ( reg & ETHERC_STATUS_LOST_CARR ) INCR_STAT( tx_carrier_loss ); if ( reg & ETHERC_STATUS_LATCOL ) INCR_STAT( tx_late_collisions ); //if ( reg & ETHERC_STATUS_WAKEUP ) INCR_STAT( ); if ( reg & ETHERC_STATUS_TX_DEFR ) INCR_STAT( tx_deferred ); //if ( reg & ETHERC_STATUS_LTX_BRD ) INCR_STAT( ); if ( reg & ETHERC_STATUS_SQET ) INCR_STAT( tx_sqetesterrors ); if ( reg & ETHERC_STATUS_16COL ) INCR_STAT( tx_max_collisions ); //if ( reg & ETHERC_STATUS_LTX_MULT) INCR_STAT( ); if ( reg & ETHERC_STATUS_MUL_COL ) INCR_STAT( tx_mult_collisions ); if ( reg & ETHERC_STATUS_SNGL_COL ) INCR_STAT( tx_single_collisions ); if ( reg & ETHERC_STATUS_TX_SUC ) INCR_STAT( tx_good ); cpd->stats.tx_total_collisions = cpd->stats.tx_late_collisions + cpd->stats.tx_max_collisions + cpd->stats.tx_mult_collisions + cpd->stats.tx_single_collisions; // We do not need to look in the Counter Register (ETHERC_COUNTER) // because it just mimics the info we already have above. } #endif // KEEP_STATISTICS #endif // FIXME // Ack the TX int put_reg(cpd, _REG_EESR, CYGARC_REG_EESR_TC); #if DEBUG & 4 db_printf("#####Tx packet freed 0x%08x\n", txd ); #endif if ( cpd->txbusy ) { cpd->txbusy = 0; (sc->funs->eth_drv->tx_done)(sc, cpd->txkey, success); } } // // This function is called when a packet has been received. Its 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 // 'etherc_recv' will be called to actually fetch it from the hardware. // static void etherc_RxEvent(struct eth_drv_sc *sc) { struct etherc_priv_data *cpd = (struct etherc_priv_data *)sc->driver_private; cyg_uint8 *rxd; cyg_uint32 rstat; cyg_uint32 ints; cyg_uint16 len; DEBUG_FUNCTION(); ints = get_reg(cpd, _REG_EESR); #if DEBUG & 1 db_printf("RxEvent - ESSR: 0x%08x\n", ints); #endif while (1) { // Get state of next (supposedly) full ring entry cpd->rxpacket = cpd->rx_ring_next; rxd = cpd->rx_ring + cpd->rxpacket*ETHERC_RD_SIZE; rstat = _SU32(rxd, ETHERC_RD_STAT); // Keep going until we hit an entry that is owned by the // controller. if (rstat & ETHERC_RD_STAT_RACT) { #if DEBUG & 1 int i; for (i = 0; i < cpd->rx_ring_cnt; i++) { rxd = cpd->rx_ring + i*ETHERC_RD_SIZE; rstat = _SU32(rxd, ETHERC_RD_STAT); if (!(rstat & ETHERC_RD_STAT_RACT)) { int i; cyg_uint32 rstat; cyg_uint16 mlen, blen; cyg_uint8* rxd; db_printf("%s: Inconsistent RX state for %d\n", __FUNCTION__, cpd->rxpacket); for (i = 0; i < cpd->rx_ring_cnt; i++) { rxd = cpd->rx_ring + i*ETHERC_RD_SIZE; rstat = _SU32(rxd, ETHERC_RD_STAT); blen = _SU16(rxd, ETHERC_RD_RBL); mlen = _SU16(rxd, ETHERC_RD_RDL); db_printf(" %02d: 0x%08x:0x%04x:0x%04x\n", i, rstat, blen, mlen); } } } #endif break; } #if DEBUG & 4 db_printf("#####Rx packet at index %d\n", cpd->rxpacket); #endif // Increment counts INCR_STAT( rx_count ); cpd->rx_ring_next++; if (cpd->rx_ring_next == cpd->rx_ring_cnt) cpd->rx_ring_next = 0; len = _SU16(rxd, ETHERC_RD_RDL); #ifdef KEEP_STATISTICS //if ( rstat & ETHERC_RD_PTR_FRAM ) INCR_STAT( rx_frame_errors ); //if ( rstat & ETHERC_RD_PTR_OFLO ) INCR_STAT( ); if ( rstat & ETHERC_RD_STAT_RFOF ) INCR_STAT( rx_crc_errors ); //if ( rstat & ETHERC_RD_PTR_BUFF ) INCR_STAT( ); #endif // KEEP_STATISTICS if (0 == (rstat & ETHERC_RD_STAT_RFE)) { // It's OK INCR_STAT( rx_good ); #if DEBUG & 1 db_printf("RxEvent good rx - stat: 0x%08x, len: 0x%04x\n", rstat, len); #endif // Check for bogusly short packets; can happen in promisc // mode: Asserted against and checked by upper layer // driver. #ifdef CYGPKG_NET if ( len > sizeof( struct ether_header ) ) // then it is acceptable; offer the data to the network stack #endif (sc->funs->eth_drv->recv)(sc, len); } else { // Not OK for one reason or another... #if DEBUG & 1 db_printf("RxEvent - No RX bit: stat: 0x%08x, len: 0x%04x\n", rstat, len); #endif } // Free packet (clear all status flags, and set RACT) _SU32(rxd, ETHERC_RD_STAT) &= ETHERC_RD_STAT_CLEAR; _SU32(rxd, ETHERC_RD_STAT) |= ETHERC_RD_STAT_RACT; } // Ack RX interrupt set put_reg(cpd, _REG_EESR, CYGARC_REG_EESR_FR); // ensure Receive Request is enabled put_reg(cpd, _REG_EDRRR, CYGARC_REG_EDRRR_RR); #if DEBUG & 1 ints = get_reg(cpd, _REG_EESR); db_printf("RxEvent exit - ESSR: 0x%08x\n", ints); #endif } // // This function is called as a result of the "eth_drv_recv()" call above. // Its 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 etherc_recv(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len) { struct etherc_priv_data *cpd = (struct etherc_priv_data *)sc->driver_private; int i, mlen=0, plen; cyg_uint8 *data, *rxd, *buf; DEBUG_FUNCTION(); rxd = cpd->rx_ring + cpd->rxpacket*ETHERC_TD_SIZE; buf = cpd->rx_buffers + cpd->rxpacket*_BUF_SIZE; INCR_STAT( rx_deliver ); plen = _SU16(rxd, ETHERC_RD_RDL); for (i = 0; i < sg_len; i++) { data = (cyg_uint8*)sg_list[i].buf; mlen = sg_list[i].len; #if DEBUG & 1 db_printf("%s : mlen %04x, plen %04x\n", __FUNCTION__, mlen, plen); #endif if (data) { while (mlen > 0) { *data++ = *buf++; mlen--; plen--; } } } } static void etherc_poll(struct eth_drv_sc *sc) { cyg_uint32 event; struct etherc_priv_data *cpd = (struct etherc_priv_data *)sc->driver_private; // DEBUG_FUNCTION(); while (1) { // Get the (unmasked) requests event = get_reg(cpd, _REG_EESR); if (0 == event) break; if (event & CYGARC_REG_EESR_FR) { etherc_RxEvent(sc); } else if (event & CYGARC_REG_EESR_TC) { etherc_TxEvent(sc, event); } else if (event & CYGARC_REG_EESR_RDE) { #if DEBUG & 1 int i; cyg_uint32 rstat; cyg_uint16 mlen, blen; cyg_uint8* rxd; struct etherc_priv_data *cpd = (struct etherc_priv_data *)sc->driver_private; db_printf("%s: Ran out of RX buffers (%04x)\n", __FUNCTION__, event); for (i = 0; i < cpd->rx_ring_cnt; i++) { rxd = cpd->rx_ring + i*ETHERC_RD_SIZE; rstat = _SU32(rxd, ETHERC_RD_STAT); blen = _SU16(rxd, ETHERC_RD_RBL); mlen = _SU16(rxd, ETHERC_RD_RDL); db_printf(" %02d: 0x%08x:0x%04x:0x%04x\n", i, rstat, blen, mlen); } #endif // Just clear the flag - RF is handled earlier in the loop // so a new RX block should be ready when this code // executes. put_reg(cpd, _REG_EESR, CYGARC_REG_EESR_RDE); } else { #if DEBUG & 1 db_printf("%s: Unknown interrupt: 0x%04x\n", __FUNCTION__, event); #endif put_reg(cpd, _REG_EESR, event); } } // Make sure RX is enabled if (cpd->active) { cyg_uint32 reg; reg = get_reg(cpd, _REG_ECMR); reg |= CYGARC_REG_ECMR_RE; put_reg(cpd, _REG_ECMR, reg); } } //---------------------------------------------------------------------------- // MII accessors #define _MII_WRITE_BIT(_cpd_, _b_) \ CYG_MACRO_START \ cyg_uint32 _d_ = (_b_) ? CYGARC_REG_PIR_MDO : 0; \ HAL_WRITE_UINT32(_cpd_->base+_REG_PIR, CYGARC_REG_PIR_MMD_WRITE | _d_); \ HAL_WRITE_UINT32(_cpd_->base+_REG_PIR, CYGARC_REG_PIR_MMD_WRITE | _d_ | CYGARC_REG_PIR_MDC); \ HAL_WRITE_UINT32(_cpd_->base+_REG_PIR, CYGARC_REG_PIR_MMD_WRITE | _d_); \ CYG_MACRO_END #define _MII_READ_BIT(_cpd_, _b_) \ CYG_MACRO_START \ cyg_uint32 _d_; \ HAL_WRITE_UINT32(_cpd_->base+_REG_PIR, CYGARC_REG_PIR_MMD_READ | CYGARC_REG_PIR_MDC); \ HAL_READ_UINT32(_cpd_->base+_REG_PIR, _d_); \ HAL_WRITE_UINT32(_cpd_->base+_REG_PIR, CYGARC_REG_PIR_MMD_READ); \ (_b_) = (_d_ & CYGARC_REG_PIR_MDI) ? 1 : 0; \ CYG_MACRO_END #define _MII_RELEASE(_cpd_) \ CYG_MACRO_START \ HAL_WRITE_UINT32(_cpd_->base+_REG_PIR, CYGARC_REG_PIR_MMD_READ); \ HAL_WRITE_UINT32(_cpd_->base+_REG_PIR, CYGARC_REG_PIR_MMD_READ | CYGARC_REG_PIR_MDC); \ HAL_WRITE_UINT32(_cpd_->base+_REG_PIR, CYGARC_REG_PIR_MMD_READ); \ CYG_MACRO_END #define _MII_RELEASE_INDEP(_cpd_) \ CYG_MACRO_START \ HAL_WRITE_UINT32(_cpd_->base+_REG_PIR, CYGARC_REG_PIR_MMD_READ); \ CYG_MACRO_END static void etherc_write_MII(struct etherc_priv_data *cpd, int id, int reg, cyg_uint16 value) { int i; // Pre for(i = 0; i < 32; i++) _MII_WRITE_BIT(cpd, 1); // Start of frame _MII_WRITE_BIT(cpd, 0); _MII_WRITE_BIT(cpd, 1); // Operation (write) _MII_WRITE_BIT(cpd, 0); _MII_WRITE_BIT(cpd, 1); // Phy address for (i = 4; i >= 0; i--) _MII_WRITE_BIT(cpd, (id & (1<<i)) ? 1: 0); // Register address for (i = 4; i >= 0; i--) _MII_WRITE_BIT(cpd, (reg & (1<<i)) ? 1: 0); // TA _MII_WRITE_BIT(cpd, 1); _MII_WRITE_BIT(cpd, 0); // Data for (i = 15; i >= 0; i--) _MII_WRITE_BIT(cpd, (value & (1<<i)) ? 1: 0); // Release bus _MII_RELEASE_INDEP(cpd); } static cyg_uint16 etherc_read_MII(struct etherc_priv_data *cpd, int id, int reg) { bool b; int i; cyg_uint16 val = 0; // Pre for(i = 0; i < 32; i++) _MII_WRITE_BIT(cpd, 1); // Start of frame _MII_WRITE_BIT(cpd, 0); _MII_WRITE_BIT(cpd, 1); // Operation (read) _MII_WRITE_BIT(cpd, 1); _MII_WRITE_BIT(cpd, 0); // Phy address for (i = 4; i >= 0; i--) _MII_WRITE_BIT(cpd, (id & (1<<i)) ? 1: 0); // Register address for (i = 4; i >= 0; i--) _MII_WRITE_BIT(cpd, (reg & (1<<i)) ? 1: 0); // TA _MII_RELEASE(cpd); // Data for (i = 15; i >= 0; i--) { _MII_READ_BIT(cpd, b); val = val << 1; if (b) val |= 1; } return val; } // EOF if_etherc.c