summaryrefslogtreecommitdiff
path: root/ecos/packages/devs/eth/sh/etherc/current/src/if_etherc.c
diff options
context:
space:
mode:
Diffstat (limited to 'ecos/packages/devs/eth/sh/etherc/current/src/if_etherc.c')
-rw-r--r--ecos/packages/devs/eth/sh/etherc/current/src/if_etherc.c1126
1 files changed, 1126 insertions, 0 deletions
diff --git a/ecos/packages/devs/eth/sh/etherc/current/src/if_etherc.c b/ecos/packages/devs/eth/sh/etherc/current/src/if_etherc.c
new file mode 100644
index 0000000..1f6563d
--- /dev/null
+++ b/ecos/packages/devs/eth/sh/etherc/current/src/if_etherc.c
@@ -0,0 +1,1126 @@
+//==========================================================================
+//
+// 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 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): 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 CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
+ 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