changeset 3192:1b48b0437ac3

New package -- Ethernet driver for Actel Smartfusion [Bugzilla 1001291]
author vae
date Mon, 12 Mar 2012 20:44:39 +0000
parents 08eb5070a44f
children bbae8ea8a773
files packages/devs/eth/cortexm/a2fxxx/current/ChangeLog packages/devs/eth/cortexm/a2fxxx/current/cdl/a2fxxx_eth.cdl packages/devs/eth/cortexm/a2fxxx/current/src/if_a2fxxx.c
diffstat 3 files changed, 1271 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/packages/devs/eth/cortexm/a2fxxx/current/ChangeLog
@@ -0,0 +1,31 @@
+
+2011-05-07  Christophe Coutand  <ecos@hotmail.co.uk>
+
+	* src/if_a2fxxx.c: 
+	* cdl/a2fxxx_eth.cdl
+	New package -- Ethernet driver for Actel Smartfusion
+    [Bugzilla 1001291]
+    
+//===========================================================================
+// ####GPLCOPYRIGHTBEGIN####                                                
+// -------------------------------------------                              
+// This file is part of eCos, the Embedded Configurable Operating System.   
+// Copyright (C) 2011 Free Software Foundation, Inc.
+//
+// This program 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.                                                           
+//
+// This program 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 this program; if not, write to the                            
+// Free Software Foundation, Inc., 51 Franklin Street,                      
+// Fifth Floor, Boston, MA  02110-1301, USA.                                
+// -------------------------------------------                              
+// ####GPLCOPYRIGHTEND####                                                  
+//===========================================================================
new file mode 100644
--- /dev/null
+++ b/packages/devs/eth/cortexm/a2fxxx/current/cdl/a2fxxx_eth.cdl
@@ -0,0 +1,152 @@
+#==========================================================================
+#
+#      a2fxxx_eth.cdl
+#
+#      Ethernet drivers for Actel AF2xxx Controller
+#
+#==========================================================================
+## ####ECOSGPLCOPYRIGHTBEGIN####                                            
+## -------------------------------------------                              
+## This file is part of eCos, the Embedded Configurable Operating System.   
+## Copyright (C) 2011 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):    ccoutand
+# Contributors:
+# Date:         2011-05-04
+# Purpose:
+# Description:
+#
+#####DESCRIPTIONEND####
+#
+#========================================================================*/
+
+cdl_package CYGPKG_DEVS_ETH_CORTEXM_A2FXXX {
+    display       "Actel Smartfusion Ethernet driver"
+    parent        CYGPKG_IO_ETH_DRIVERS
+    active_if     CYGPKG_IO_ETH_DRIVERS
+    active_if     CYGPKG_HAL_CORTEXM_A2FXXX
+
+    implements    CYGHWR_NET_DRIVERS
+    implements    CYGHWR_NET_DRIVER_ETH0
+
+    include_dir   net
+    description   "
+       Ethernet drivers for Actel AF2xxx Controller."
+
+    compile       -library=libextras.a if_a2fxxx.c
+
+    cdl_option CYGSEM_DEVS_ETH_CORTEXM_A2FXXX_PROMISCUOUS {
+        display         "Promiscuous Mode"
+        flavor          bool
+        default_value   0
+        description     "
+           Selecting this option will set the Ethernet MAC in promiscuous mode, all Ethernet
+           packets will be delivered to the application layer."
+    }
+
+    cdl_option CYGSEM_DEVS_ETH_CORTEXM_A2FXXX_CHATTER {
+        display         "Display status messages during Ethernet operations"
+        flavor          bool
+        default_value   0
+        description     "
+           Selecting this option will cause the Ethernet driver to print status
+           messages as various Ethernet operations are undertaken."
+    }
+
+    cdl_option CYGNUM_DEVS_ETH_CORTEXM_A2FXXX_ISR_PRIORITY {
+        display       "Ethernet interrupt ISR priority"
+        flavor        data
+        default_value CYGNUM_DEVS_ETH_CORTEXM_A2FXXX_ISR_SP
+        description   "
+              Set the Ethernet controller ISR priority."
+    }
+
+    cdl_option CYGSEM_DEVS_ETH_CORTEXM_A2FXXX_STATS {
+        display         "Maintain statistics at the MAC layer"
+        flavor          bool
+        default_value   0
+        description     "
+           Selecting this option will cause the Ethernet driver to accumulate statistics
+           provided from the MAC layer."
+    }
+
+    cdl_option CYGNUM_DEVS_ETH_CORTEXM_A2FXXX_BUFSIZE_TX {
+        display       "Buffer size"
+        flavor        data
+        default_value 1536
+        description   "
+            This option specifies the size of the internal transmit buffers used
+            for the Actel A2Fxxx/Ethernet device."
+    }
+
+    cdl_option CYGNUM_DEVS_ETH_CORTEXM_A2FXXX_BUFSIZE_RX {
+        display       "Buffer size"
+        flavor        data
+        default_value 1536
+        description   "
+            This option specifies the size of the internal receive buffers used
+            for the Actel A2Fxxx/Ethernet device."
+    }
+
+    cdl_option CYGNUM_DEVS_ETH_CORTEXM_A2FXXX_TxNUM {
+        display       "Number of output buffers"
+        flavor        data
+        legal_values  2 to 8
+        default_value 4
+        description   "
+            This option specifies the number of output buffer packets
+            to be used for the Actel A2Fxxx/Ethernet device."
+    }
+
+    cdl_option CYGNUM_DEVS_ETH_CORTEXM_A2FXXX_RxNUM {
+        display       "Number of input buffers"
+        flavor        data
+        legal_values  2 to 8
+        default_value 4
+        description   "
+            This option specifies the number of input buffer packets
+            to be used for the Actel A2Fxxx/Ethernet device."
+    }
+
+    cdl_option  CYGPKG_DEVS_ETH_CORTEXM_A2FXXX_CFLAGS_ADD {
+        display "Additional compiler flags"
+        flavor  data
+        no_define
+        default_value { "-D_KERNEL -D__ECOS" }
+        description   "
+            This option modifies the set of compiler flags for
+            building the Actel A2Fxxx Ethernet driver package.
+            These flags are used in addition to the set of global flags."
+    }
+}
+
+# EOF a2fxxx_eth.cdl
new file mode 100644
--- /dev/null
+++ b/packages/devs/eth/cortexm/a2fxxx/current/src/if_a2fxxx.c
@@ -0,0 +1,1088 @@
+//==========================================================================
+//
+//      if_a2fxxx.c
+//
+//      Ethernet driver for Actel A2Fxxx device family
+//
+//==========================================================================
+// ####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 2011 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):    ccoutand
+// Contributors:
+// Date:         2011-05-05
+// Purpose:      Ethernet driver for Actel A2Fxxx device family
+// Description:
+//
+//####DESCRIPTIONEND####
+//
+//========================================================================*/
+
+#include <pkgconf/system.h>
+#include <pkgconf/devs_eth_cortexm_a2fxxx.h>
+
+#ifdef CYGDAT_DEVS_CORTEXM_A2FXXX_ETH_CDL
+# include CYGDAT_DEVS_CORTEXM_A2FXXX_ETH_CDL
+#endif
+
+#include <pkgconf/io_eth_drivers.h>
+
+#ifdef CYGPKG_NET
+# include <pkgconf/net.h>
+#endif
+
+#include <cyg/infra/cyg_type.h>
+
+#include <cyg/hal/hal_arch.h>
+#include <cyg/hal/hal_cache.h>
+#include <cyg/hal/hal_intr.h>
+#include <cyg/hal/drv_api.h>
+#include <cyg/hal/hal_if.h>
+#include <cyg/hal/var_io.h>
+
+#include <cyg/io/eth/netdev.h>
+#include <cyg/io/eth/eth_drv.h>
+
+#ifdef CYGPKG_DEVS_ETH_PHY
+# include <cyg/io/eth_phy.h>
+#endif
+
+#include <errno.h>
+#include <string.h>
+
+static unsigned char enaddr[6];
+
+// Buffer descriptor
+struct a2fxxx_bd {
+    cyg_uint32     des0;
+    cyg_uint32     des1;
+    cyg_uint32     des2;
+    cyg_uint32     des3;
+    cyg_uint32     private;
+};
+
+// Some statistics
+#ifdef CYGSEM_DEVS_ETH_CORTEXM_A2FXXX_STATS
+typedef struct {
+    cyg_uint32     rx_mcast;
+    cyg_uint32     rx_des_err;
+    cyg_uint32     rx_collision;
+    cyg_uint32     rx_crc_err;
+    cyg_uint32     rx_too_long;
+    cyg_uint32     tx_fifo_underflow;
+    cyg_uint32     tx_frame_deferred;
+    cyg_uint32     tx_late_collision;
+    cyg_uint32     tx_no_carrier;
+    cyg_uint32     tx_loss_carrier;
+} a2fxxx_eth_stats;
+#endif
+
+// Internal data structure
+struct a2fxxx_eth_info {
+    volatile struct    a2fxxx_bd *txbd,
+                      *rxbd;                   // Next Tx,Rx descriptor to use
+    volatile struct    a2fxxx_bd *tbase,
+                      *rbase;                  // First Tx,Rx descriptor
+    volatile struct    a2fxxx_bd *tnext,
+                      *rnext;                  // Next descriptor to check for interrupt
+    cyg_uint8          txactive;               // Count of active Tx buffers
+    unsigned long      txkey[CYGNUM_DEVS_ETH_CORTEXM_A2FXXX_TxNUM];
+    cyg_uint8          mac_address[6];         // mac (hardware) address
+    cyg_uint8          *init_rxbufs;           // Initial base pointer of RX buffers
+    cyg_uint8          *init_txbufs;           // Initial base pointer of TX buffers
+    volatile struct    a2fxxx_bd *init_rxring; // Initial base pointer of RX ring
+    volatile struct    a2fxxx_bd *init_txring; // Initial base pointer of TX ring
+    cyg_uint32         rx_mac[48];             // Setup frame (192) bytes
+    cyg_uint32         valid_mac;
+#ifdef CYGSEM_DEVS_ETH_CORTEXM_A2FXXX_STATS
+    a2fxxx_eth_stats   stats;                  // MAC statistics
+#endif
+    cyg_uint32         base;                   // MAC base address
+    cyg_uint32         base_bb;                // MAC base address (bit-band)
+    cyg_uint8          if_num;
+#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
+    cyg_uint8          vector;
+    cyg_interrupt      interrupt;
+    cyg_handle_t       interrupt_handle;
+#endif
+#ifdef CYGPKG_DEVS_ETH_PHY
+    eth_phy_access_t   *phy;
+#endif
+};
+
+static void     a2fxxx_phy_init(void);
+static bool     a2fxxx_mdio_read(cyg_int32, cyg_int32, cyg_uint16 *);
+static void     a2fxxx_mdio_write(cyg_int32, cyg_int32, cyg_uint16);
+
+static void     a2fxxx_eth_int(struct eth_drv_sc *data);
+
+#ifdef CYGDAT_DEVS_CORTEXM_A2FXXX_ETH_INL
+# include CYGDAT_DEVS_CORTEXM_A2FXXX_ETH_INL
+#endif
+
+#include <cyg/infra/diag.h>
+
+#ifdef CYGSEM_DEVS_ETH_CORTEXM_A2FXXX_CHATTER
+# define a2fxxx_eth_printf(args...)   diag_printf(args)
+#else
+# define a2fxxx_eth_printf(args...)    /* NOOP */
+#endif
+
+// For fetching the ESA from RedBoot
+#include <cyg/hal/hal_if.h>
+#ifndef CONFIG_ESA
+# define CONFIG_ESA 6
+#endif
+
+// ----------------------------------------------------------------
+// Definition
+//
+//
+
+#define RESET_FULL_DUPLEX                     0x00000001
+#define RESET_100MB                           0x00000002
+
+#define MDIO_PREAMBLE_SEQ                     0xffffffff
+#define MDIO_START_SEQ                        0x01
+#define MDIO_WRITE_SEQ                        0x01
+#define MDIO_READ_SEQ                         0x02
+#define MDIO_STOP_WRITE_SEQ                   0x02
+#define MDIO_STOP_READ_SEQ                    0x00
+
+#define CYGHWR_HAL_A2FXXX_MAC_CSR5_TI_BB(_base_)  \
+    ( _base_ + ( CYGHWR_HAL_A2FXXX_MAC_CSR5 << 5 ) + (  0*sizeof(cyg_uint32) ) )
+#define CYGHWR_HAL_A2FXXX_MAC_CSR5_RI_BB(_base_)  \
+    ( _base_ + ( CYGHWR_HAL_A2FXXX_MAC_CSR5 << 5 ) + (  6*sizeof(cyg_uint32) ) )
+#define CYGHWR_HAL_A2FXXX_MAC_CSR5_NIS_BB(_base_) \
+    ( _base_ + ( CYGHWR_HAL_A2FXXX_MAC_CSR5 << 5 ) + ( 16*sizeof(cyg_uint32) ) )
+
+#define CYGHWR_HAL_A2FXXX_MAC_PRIVATE_SENT    BIT_(0)
+
+#ifdef CYGSEM_DEVS_ETH_CORTEXM_A2FXXX_STATS
+# define A2FXXX_INC_STATS(_x_)                 (qi->stats._x_++)
+#endif
+
+// ----------------------------------------------------------------
+// MDIO API
+//
+//
+
+#define MDIO_XFER_BITS( reg, reg_val, sequence, length )      \
+{                                                             \
+    cyg_uint32 i;                                             \
+    for (i = (1 << (length-1)); i != 0; ) {                   \
+        reg_val &= ~CYGHWR_HAL_A2FXXX_MAC_CSR9_MDC;           \
+        HAL_WRITE_UINT32( reg, reg_val );                     \
+        HAL_DELAY_US(1);                                      \
+        if( i & sequence )                                    \
+            reg_val |= (  CYGHWR_HAL_A2FXXX_MAC_CSR9_MDO );   \
+        else                                                  \
+            reg_val &= ( ~CYGHWR_HAL_A2FXXX_MAC_CSR9_MDO );   \
+        HAL_WRITE_UINT32( reg, reg_val );                     \
+        reg_val |= CYGHWR_HAL_A2FXXX_MAC_CSR9_MDC;            \
+        HAL_WRITE_UINT32( reg, reg_val );                     \
+        HAL_DELAY_US(1);                                      \
+        i = i >> 1;                                           \
+    }                                                         \
+}
+
+//
+// Setup IOs
+//
+static void
+a2fxxx_phy_init(void)
+{
+    a2fxxx_eth_printf("ETH A2Fxxx - PHY Init\n");
+    CYGHWR_HAL_A2FXXX_GPIO_SET(CYGHWR_HAL_A2FXXX_MAC0_MDIO);
+    CYGHWR_HAL_A2FXXX_GPIO_SET(CYGHWR_HAL_A2FXXX_MAC0_MDC);
+}
+
+//
+// Start MDIO transfer -> 32 bits of preamble ('1'), start bits,
+// transfer type bits, phy address and register address bits
+//
+static void
+a2fxxx_mdio_xfer_start(cyg_bool rd, cyg_uint8 phy_addr, cyg_uint8 addr)
+{
+    cyg_uint32      csr9_reg =
+        (CYGHWR_HAL_A2FXXX_MAC + CYGHWR_HAL_A2FXXX_MAC_CSR9);
+    cyg_uint32      csr9_data,
+                    bit_sequence;
+
+    // Enable MDIO write
+    HAL_READ_UINT32(csr9_reg, csr9_data);
+    csr9_data |=
+        (CYGHWR_HAL_A2FXXX_MAC_CSR9_MDEN | CYGHWR_HAL_A2FXXX_MAC_CSR9_MDO |
+         CYGHWR_HAL_A2FXXX_MAC_CSR9_MDC);
+    HAL_WRITE_UINT32(csr9_reg, csr9_data);
+    HAL_DELAY_US(1);
+
+    // Send preamble bits
+    bit_sequence = MDIO_PREAMBLE_SEQ;
+    MDIO_XFER_BITS(csr9_reg, csr9_data, bit_sequence, 32);
+
+    // Send 2 start bits
+    bit_sequence = MDIO_START_SEQ;
+    MDIO_XFER_BITS(csr9_reg, csr9_data, bit_sequence, 2);
+
+    // Send transfer type bits
+    bit_sequence = (rd == true) ? MDIO_READ_SEQ : MDIO_WRITE_SEQ;
+    MDIO_XFER_BITS(csr9_reg, csr9_data, bit_sequence, 2);
+
+    // Send PHY address
+    bit_sequence = (cyg_uint32)phy_addr;
+    MDIO_XFER_BITS(csr9_reg, csr9_data, bit_sequence, 5);
+
+    // Send register address
+    bit_sequence = (cyg_uint32)addr;
+    MDIO_XFER_BITS(csr9_reg, csr9_data, bit_sequence, 5);
+
+    // Send TA sequence
+    if (rd == true) {
+        bit_sequence = MDIO_STOP_READ_SEQ;
+        csr9_data &= ~(CYGHWR_HAL_A2FXXX_MAC_CSR9_MDEN);
+        HAL_WRITE_UINT32(csr9_reg, csr9_data);
+    } else {
+        bit_sequence = MDIO_STOP_WRITE_SEQ;
+    }
+    MDIO_XFER_BITS(csr9_reg, csr9_data, bit_sequence, 2);
+
+    // now we can read or write the 16 bits data ...
+}
+
+//
+// Read 16 bits data from MDIO interface
+//
+static cyg_bool
+a2fxxx_mdio_xfer_read(cyg_uint16 *data)
+{
+    cyg_uint32      csr9_reg =
+        (CYGHWR_HAL_A2FXXX_MAC + CYGHWR_HAL_A2FXXX_MAC_CSR9);
+    cyg_uint32      csr9_data;
+    *data = 0;
+    int             i;
+
+    // Read MDIO register
+    HAL_READ_UINT32(csr9_reg, csr9_data);
+
+    // Clock in the RX data
+    for (i = (1 << 15); i != 0;) {
+        csr9_data &= ~CYGHWR_HAL_A2FXXX_MAC_CSR9_MDC;
+        HAL_WRITE_UINT32(csr9_reg, csr9_data);
+        HAL_DELAY_US(1);
+        HAL_READ_UINT32(csr9_reg, csr9_data);
+        if (csr9_data & CYGHWR_HAL_A2FXXX_MAC_CSR9_MDI)
+            *data |= i;
+        csr9_data |= CYGHWR_HAL_A2FXXX_MAC_CSR9_MDC;
+        HAL_WRITE_UINT32(csr9_reg, csr9_data);
+        HAL_DELAY_US(1);
+        i = i >> 1;
+    }
+
+    return true;
+}
+
+//
+// Write 16 bits data to MDIO interface
+//
+static bool
+a2fxxx_mdio_xfer_write(cyg_uint16 data)
+{
+    cyg_uint32      csr9_reg =
+        (CYGHWR_HAL_A2FXXX_MAC + CYGHWR_HAL_A2FXXX_MAC_CSR9);
+    cyg_uint32      csr9_data,
+                    bit_sequence;
+
+    // Read MDIO register
+    HAL_READ_UINT32(csr9_reg, csr9_data);
+
+    // Write data
+    bit_sequence = (cyg_uint32)data;
+    MDIO_XFER_BITS(csr9_reg, csr9_data, bit_sequence, 16);
+
+    // Set back clock to low
+    csr9_data &= ~CYGHWR_HAL_A2FXXX_MAC_CSR9_MDC;
+    HAL_WRITE_UINT32(csr9_reg, csr9_data);
+
+    return true;
+}
+
+//
+// MDIO read register
+//
+static bool
+a2fxxx_mdio_read(cyg_int32 addr, cyg_int32 phy_addr, cyg_uint16 *data)
+{
+    bool            res;
+    a2fxxx_mdio_xfer_start(true, phy_addr, addr);
+    res = a2fxxx_mdio_xfer_read(data);
+    a2fxxx_eth_printf("ETH A2Fxxx - PHY %x(%x) - RX data %x\n", phy_addr, addr,
+                      *data);
+    return res;
+}
+
+//
+// MDIO write register
+//
+static void
+a2fxxx_mdio_write(cyg_int32 addr, cyg_int32 phy_addr, cyg_uint16 data)
+{
+    a2fxxx_mdio_xfer_start(false, phy_addr, addr);
+    a2fxxx_eth_printf("ETH A2Fxxx - PHY %x(%x) - TX data %x\n", phy_addr, addr,
+                      data);
+    a2fxxx_mdio_xfer_write(data);
+}
+
+
+// ----------------------------------------------------------------
+// Ethernet driver
+//
+//
+#define A2FXXX_RESET_MAC( qi )                                \
+{                                                             \
+    cyg_uint32      swr_addr =                                \
+          qi->base_bb + (CYGHWR_HAL_A2FXXX_MAC_CSR0 << 5);    \
+    cyg_uint32      swr_data = 0x1;                           \
+    HAL_WRITE_UINT32(swr_addr, swr_data);                     \
+    do {                                                      \
+        HAL_READ_UINT32(swr_addr, swr_data);                  \
+    } while (swr_data);                                       \
+}
+
+//
+// Deliver function (ex-DSR) handles the Ethernet [logical] processing
+//
+static void
+a2fxxx_eth_deliver(struct eth_drv_sc *sc)
+{
+#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
+    struct a2fxxx_eth_info *qi = (struct a2fxxx_eth_info *)sc->driver_private;
+#endif
+    a2fxxx_eth_int(sc);
+#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
+    // Allow interrupts to happen again
+    cyg_drv_interrupt_unmask(qi->vector);
+#endif
+}
+
+#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
+//
+// This ISR is called when the Ethernet interrupt occurs
+//
+static int
+a2fxxx_eth_isr(cyg_vector_t vector, cyg_addrword_t data,
+               HAL_SavedRegisters * regs)
+{
+    a2fxxx_eth_printf("ETH A2Fxxx - ISR\n");
+    cyg_drv_interrupt_mask(vector);
+    cyg_drv_interrupt_acknowledge(vector);
+
+    return (CYG_ISR_HANDLED | CYG_ISR_CALL_DSR);        // Run the DSR
+}
+
+void
+a2fxxx_eth_dsr(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
+{
+    a2fxxx_eth_deliver((struct eth_drv_sc *)data);
+    // Allow interrupts to happen again
+    cyg_drv_interrupt_unmask(vector);
+}
+#endif
+
+//
+// This function add MAC address to the list of allowed MAC address
+// that the MAC layer can deliver to the application layer
+//
+static bool
+a2fxxx_eth_add_mac ( struct eth_drv_sc *sc, cyg_uint8 *mac )
+{
+    struct a2fxxx_eth_info *qi = (struct a2fxxx_eth_info *)sc->driver_private;
+    cyg_uint8 idx = 12;
+    struct a2fxxx_bd setup_bd;
+    cyg_uint32 i, csr4, csr5, csr6, csr7;
+
+    // If no MAC to be added, just re-send the setup frame
+    if( mac != NULL )
+    {
+        // no more room
+        if( qi->valid_mac == (cyg_uint32) -1 )
+            return false;
+
+        // Search empty spot
+        while( qi->valid_mac & i ){
+            i = i << 1;
+            idx += 12;
+        }
+
+        qi->rx_mac[idx+0] = mac[0];
+        qi->rx_mac[idx+1] = mac[1];
+        qi->rx_mac[idx+4] = mac[2];
+        qi->rx_mac[idx+5] = mac[3];
+        qi->rx_mac[idx+8] = mac[4];
+        qi->rx_mac[idx+9] = mac[6];
+    }
+
+    a2fxxx_eth_printf("ETH A2Fxxx - Send setup frame for %s\n", sc->dev_name);
+
+    // Stop TX
+    HAL_READ_UINT32( (qi->base + CYGHWR_HAL_A2FXXX_MAC_CSR6), csr6);
+    csr6 &= ~( CYGHWR_HAL_A2FXXX_MAC_CSR6_ST );
+    HAL_WRITE_UINT32( (qi->base + CYGHWR_HAL_A2FXXX_MAC_CSR6), csr6);
+
+    // Wait until controller is halted
+    do {
+        HAL_READ_UINT32( (qi->base + CYGHWR_HAL_A2FXXX_MAC_CSR5), csr5);
+    } while( ((csr5 & CYGHWR_HAL_A2FXXX_MAC_CSR5_TS_MASK)
+                                     != CYGHWR_HAL_A2FXXX_MAC_CSR5_TS_STOP) );
+
+    // Backup the current BD / interrupt mask
+    HAL_READ_UINT32( (qi->base + CYGHWR_HAL_A2FXXX_MAC_CSR4), csr4);
+    HAL_READ_UINT32( (qi->base + CYGHWR_HAL_A2FXXX_MAC_CSR7), csr7);
+
+    // Disable interrupt
+    HAL_WRITE_UINT32( (qi->base + CYGHWR_HAL_A2FXXX_MAC_CSR7), 0);
+
+    // Setup descriptor for setup frame
+    setup_bd.des0 = CYGHWR_HAL_A2FXXX_MAC_TDES0_OWN;
+    setup_bd.des1 = CYGHWR_HAL_A2FXXX_MAC_TDES1_TER |
+                         CYGHWR_HAL_A2FXXX_MAC_TDES1_SET |
+                         CYGHWR_HAL_A2FXXX_MAC_TDES1_TBS1(192);
+    setup_bd.des2 = (cyg_uint32) &qi->rx_mac[0];
+    setup_bd.des3 = (cyg_uint32) NULL;
+    HAL_WRITE_UINT32( (qi->base + CYGHWR_HAL_A2FXXX_MAC_CSR4),
+                      (cyg_uint32) &setup_bd);
+
+    // Start transmission
+    csr6 |= ( CYGHWR_HAL_A2FXXX_MAC_CSR6_ST );
+    HAL_WRITE_UINT32( (qi->base + CYGHWR_HAL_A2FXXX_MAC_CSR6), csr6);
+
+    // Poll for transmission completed
+    do {
+        HAL_READ_UINT32( (qi->base + CYGHWR_HAL_A2FXXX_MAC_CSR5), csr5);
+    } while( ((csr5 & A2FXXX_MAC_TX_STATE_MASK) != A2FXXX_MAC_TX_STATE(SUSPEND)) );
+
+    // Stop transmission
+    csr6 &= ~( CYGHWR_HAL_A2FXXX_MAC_CSR6_ST );
+    HAL_WRITE_UINT32( (qi->base + CYGHWR_HAL_A2FXXX_MAC_CSR6), csr6);
+
+    // Wait until controller is halted
+    do {
+        HAL_READ_UINT32( (qi->base + CYGHWR_HAL_A2FXXX_MAC_CSR5), csr5);
+    } while( ((csr5 & CYGHWR_HAL_A2FXXX_MAC_CSR5_TS_MASK)
+                                    != CYGHWR_HAL_A2FXXX_MAC_CSR5_TS_STOP) );
+
+    // Restore BD
+    HAL_WRITE_UINT32( (qi->base + CYGHWR_HAL_A2FXXX_MAC_CSR4), csr4);
+
+    HAL_WRITE_UINT32( CYGHWR_HAL_A2FXXX_MAC_CSR5_TI_BB(qi->base_bb), 1);
+
+    // Restore interrupt mask
+    HAL_WRITE_UINT32( (qi->base + CYGHWR_HAL_A2FXXX_MAC_CSR7), csr7);
+
+    return true;
+}
+
+//
+// [re]Initialize the Ethernet controller
+//   Done separately since shutting down the device requires a
+//   full reconfiguration when re-enabling.
+//   when
+static bool
+a2fxxx_eth_setup(struct eth_drv_sc *sc, unsigned char *enaddr, int flags)
+{
+    struct a2fxxx_eth_info *qi = (struct a2fxxx_eth_info *) sc->driver_private;
+    volatile struct a2fxxx_bd *rxbd, *txbd;
+    cyg_uint8 *RxBUF, *TxBUF;
+    cyg_uint32 csr0, csr6 = 0, csr11;
+    int i = 0;
+
+    qi->valid_mac = 0;
+    qi->rx_mac[5] = enaddr[0];
+    qi->rx_mac[4] = enaddr[1];
+    qi->rx_mac[3] = enaddr[2];
+    qi->rx_mac[2] = enaddr[3];
+    qi->rx_mac[1] = enaddr[4];
+    qi->rx_mac[0] = enaddr[5];
+
+    txbd = qi->init_txring;
+    rxbd = qi->init_rxring;
+
+    /* Init Rx / Tx ring base */
+    qi->tbase = qi->txbd = qi->tnext = txbd;
+    qi->rbase = qi->rxbd = qi->rnext = rxbd;
+    qi->txactive = 0;
+
+    RxBUF = qi->init_rxbufs;
+    TxBUF = qi->init_txbufs;
+
+    /* Initialize Rx BDs */
+    for (i = 0; i < CYGNUM_DEVS_ETH_CORTEXM_A2FXXX_RxNUM; i++)
+    {
+        rxbd->des0 = CYGHWR_HAL_A2FXXX_MAC_RDES0_OWN;
+        rxbd->des1 =
+          CYGHWR_HAL_A2FXXX_MAC_RDES1_RBS1(CYGNUM_DEVS_ETH_CORTEXM_A2FXXX_ALIGN_BUFSIZE_RX);
+        rxbd->des2 = (cyg_uint32) RxBUF;
+        rxbd->des3 = (cyg_uint32) NULL;
+        RxBUF += CYGNUM_DEVS_ETH_CORTEXM_A2FXXX_ALIGN_BUFSIZE_RX;
+        rxbd++;
+    }
+    rxbd--;
+    rxbd->des1 |= CYGHWR_HAL_A2FXXX_MAC_RDES1_RER; // Last buffer
+
+    /* Initialize Tx BDs */
+    for (i = 0; i < CYGNUM_DEVS_ETH_CORTEXM_A2FXXX_TxNUM; i++)
+    {
+        txbd->des1 = 0;
+        txbd->des2 = (cyg_uint32) TxBUF;
+        rxbd->des3 = (cyg_uint32) NULL;
+        txbd->private = 0;
+        TxBUF += CYGNUM_DEVS_ETH_CORTEXM_A2FXXX_ALIGN_BUFSIZE_TX;
+        txbd++;
+    }
+    txbd--;
+    txbd->des1 |= CYGHWR_HAL_A2FXXX_MAC_TDES1_TER; // Last buffer
+
+    // Setup automatic polling
+    csr0 = CYGHWR_HAL_A2FXXX_MAC_CSR0_CLEAR | CYGHWR_HAL_A2FXXX_MAC_CSR0_TAP(3) |
+           CYGHWR_HAL_A2FXXX_MAC_CSR0_DSL(1);
+
+    HAL_WRITE_UINT32( (qi->base + CYGHWR_HAL_A2FXXX_MAC_CSR0),
+                      (cyg_uint32) csr0);
+    HAL_WRITE_UINT32( (qi->base + CYGHWR_HAL_A2FXXX_MAC_CSR3),
+                      (cyg_uint32) (qi->rbase));
+    HAL_WRITE_UINT32( (qi->base + CYGHWR_HAL_A2FXXX_MAC_CSR4),
+                      (cyg_uint32) (qi->tbase));
+
+    csr11 = CYGHWR_HAL_A2FXXX_MAC_CSR11_TT(7) |
+            CYGHWR_HAL_A2FXXX_MAC_CSR11_NTP(1) |
+            CYGHWR_HAL_A2FXXX_MAC_CSR11_RT(7) |
+            CYGHWR_HAL_A2FXXX_MAC_CSR11_NRP(1);
+    HAL_WRITE_UINT32( (qi->base + CYGHWR_HAL_A2FXXX_MAC_CSR11), (cyg_uint32) csr11);
+
+    HAL_READ_UINT32( (qi->base + CYGHWR_HAL_A2FXXX_MAC_CSR6), csr6);
+    csr6 |= CYGHWR_HAL_A2FXXX_MAC_CSR6_CLEAR;
+
+    csr6 |= ((flags & RESET_100MB) ? CYGHWR_HAL_A2FXXX_MAC_CSR6_TTM : 0x0) |
+           ((flags & RESET_FULL_DUPLEX) ? CYGHWR_HAL_A2FXXX_MAC_CSR6_FD : 0x0);
+
+#ifdef CYGSEM_DEVS_ETH_CORTEXM_A2FXXX_PROMISCUOUS
+    csr6 |= CYGHWR_HAL_A2FXXX_MAC_CSR6_PR;
+#endif
+
+    HAL_WRITE_UINT32( (qi->base + CYGHWR_HAL_A2FXXX_MAC_CSR6), csr6);
+
+    return true;
+}
+
+//
+// This function is called to shut down the interface.
+//
+static void
+a2fxxx_eth_stop(struct eth_drv_sc *sc)
+{
+    struct a2fxxx_eth_info *qi = (struct a2fxxx_eth_info *)sc->driver_private;
+    cyg_uint32 csr6, csr5;
+
+    a2fxxx_eth_printf("ETH A2Fxxx - Stop\n");
+
+    // Stop RX / TX
+    HAL_READ_UINT32( (qi->base + CYGHWR_HAL_A2FXXX_MAC_CSR6), csr6);
+    csr6 &= ~( CYGHWR_HAL_A2FXXX_MAC_CSR6_SR | CYGHWR_HAL_A2FXXX_MAC_CSR6_ST );
+    HAL_WRITE_UINT32( (qi->base + CYGHWR_HAL_A2FXXX_MAC_CSR6), csr6);
+
+    // Stall until controller is halted
+    do {
+        HAL_READ_UINT32( (qi->base + CYGHWR_HAL_A2FXXX_MAC_CSR5), csr5);
+    } while( ((csr5 & CYGHWR_HAL_A2FXXX_MAC_CSR5_TS_MASK) != CYGHWR_HAL_A2FXXX_MAC_CSR5_TS_STOP) &&
+             ((csr5 & CYGHWR_HAL_A2FXXX_MAC_CSR5_RS_MASK) != CYGHWR_HAL_A2FXXX_MAC_CSR5_RS_STOP));
+
+}
+
+//
+// Setup Ethernet IOs.
+//
+static void
+a2fxxx_eth_io(void)
+{
+    CYGHWR_HAL_A2FXXX_GPIO_SET(CYGHWR_HAL_A2FXXX_MAC0_TXD0);
+    CYGHWR_HAL_A2FXXX_GPIO_SET(CYGHWR_HAL_A2FXXX_MAC0_TXD1);
+    CYGHWR_HAL_A2FXXX_GPIO_SET(CYGHWR_HAL_A2FXXX_MAC0_RXD0);
+    CYGHWR_HAL_A2FXXX_GPIO_SET(CYGHWR_HAL_A2FXXX_MAC0_RXD1);
+    CYGHWR_HAL_A2FXXX_GPIO_SET(CYGHWR_HAL_A2FXXX_MAC0_TXEN);
+    CYGHWR_HAL_A2FXXX_GPIO_SET(CYGHWR_HAL_A2FXXX_MAC0_CRSDV);
+    CYGHWR_HAL_A2FXXX_GPIO_SET(CYGHWR_HAL_A2FXXX_MAC0_RXER);
+}
+
+//
+// Initialize the interface - performed at system startup
+// This function must set up the interface, including arranging to
+// handle interrupts, etc, so that it may be "started" cheaply later.
+//
+static bool
+a2fxxx_eth_init(struct cyg_netdevtab_entry *tab)
+{
+    struct eth_drv_sc *sc = (struct eth_drv_sc *)tab->device_instance;
+    struct a2fxxx_eth_info *qi = (struct a2fxxx_eth_info *)sc->driver_private;
+    cyg_uint32      speed100 = 0, full_duplex = 0;
+    bool            esa_ok;
+    cyg_uint16      phy_state = 0;
+    cyg_uint32      istate, csr7;
+    bool            ret = true;
+
+    a2fxxx_eth_printf("ETH A2Fxxx - Initialization for %s\n", sc->dev_name);
+
+    // Release MAC controller
+    CYGHWR_HAL_A2FXXX_PERIPH_RELEASE(CYGHWR_HAL_A2FXXX_PERIPH_SOFTRST(MAC));
+
+    // Setup IOs
+    a2fxxx_eth_io();
+
+    // Reset MAC
+    A2FXXX_RESET_MAC( qi );
+
+    HAL_DISABLE_INTERRUPTS(istate);
+
+    a2fxxx_eth_stop(sc);
+
+#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
+    cyg_drv_interrupt_create(qi->vector,
+                             CYGNUM_DEVS_ETH_CORTEXM_A2FXXX_ISR_PRIORITY,
+                             (cyg_addrword_t)sc,    //  Data item passed to interrupt handler
+                             (cyg_ISR_t *)a2fxxx_eth_isr,
+                             (cyg_DSR_t *)eth_drv_dsr,
+                             &qi->interrupt_handle, &qi->interrupt);
+    cyg_drv_interrupt_attach(qi->interrupt_handle);
+    cyg_drv_interrupt_unmask(qi->vector);
+#endif
+
+    esa_ok = CYGACC_CALL_IF_FLASH_CFG_OP(CYGNUM_CALL_IF_FLASH_CFG_GET,
+                                         "a2fxxx_esa", enaddr, CONFIG_ESA);
+
+    if (!esa_ok) {
+        a2fxxx_eth_printf("ETH A2Fxxx - Warning! ESA unknown for %s\n", sc->dev_name);
+        memcpy(&enaddr, &qi->mac_address, sizeof(enaddr));
+    }
+
+#ifdef CYGPKG_DEVS_ETH_PHY
+    if (!_eth_phy_init(qi->phy)) {
+        a2fxxx_eth_printf("ETH A2Fxxx - Failed to initialize PHY of %s\n",
+                          sc->dev_name);
+        ret = false;
+        goto exit;
+    }
+    phy_state = _eth_phy_state(qi->phy);
+
+    if (phy_state & ETH_PHY_STAT_100MB)
+        speed100 = 1;
+    if (phy_state & ETH_PHY_STAT_FDX)
+        full_duplex = 1;
+    if (phy_state & ETH_PHY_STAT_LINK) {
+        a2fxxx_eth_printf("Ethernet Mode (%s): %sMbps/%s\n",
+                          sc->dev_name,
+                          (speed100 ? "100" : "10"),
+                          (full_duplex ? "Full" : "Half"));
+    } else {
+        a2fxxx_eth_printf("ETH A2Fxxx - NO LINK on %s\n", sc->dev_name);
+    }
+#else
+    a2fxxx_eth_printf("ETH A2Fxxx - No PHY interface specified for %s\n",
+                      sc->dev_name);
+    ret = false;
+    goto exit;
+#endif
+
+    if (!a2fxxx_eth_setup(sc, enaddr, (full_duplex ? RESET_FULL_DUPLEX
+                         : 0x00000000) | (speed100 ? RESET_100MB : 0x00000000)))
+    {
+      ret = false;
+      goto exit;
+    }
+
+    a2fxxx_eth_add_mac( sc, NULL );
+
+    HAL_WRITE_UINT32( CYGHWR_HAL_A2FXXX_MAC_CSR5_RI_BB(qi->base_bb), 1);
+    HAL_WRITE_UINT32( CYGHWR_HAL_A2FXXX_MAC_CSR5_TI_BB(qi->base_bb), 1);
+
+    csr7 = ( CYGHWR_HAL_A2FXXX_MAC_CSR7_TIE | CYGHWR_HAL_A2FXXX_MAC_CSR7_RIE |
+                    CYGHWR_HAL_A2FXXX_MAC_CSR7_NIE);
+    HAL_WRITE_UINT32( (qi->base + CYGHWR_HAL_A2FXXX_MAC_CSR7), csr7);
+
+    HAL_WRITE_UINT32( CYGHWR_HAL_A2FXXX_MAC_CSR5_NIS_BB(qi->base_bb),
+                                                           (cyg_uint32) 0x1);
+
+    // Call upper layer initialization
+    (sc->funs->eth_drv->init)(sc, (unsigned char *) &enaddr);
+
+exit:
+    HAL_RESTORE_INTERRUPTS(istate);
+    return ret;
+}
+
+//
+// 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
+a2fxxx_eth_start(struct eth_drv_sc *sc, unsigned char *enaddr, int flags)
+{
+    struct a2fxxx_eth_info *qi = (struct a2fxxx_eth_info *)sc->driver_private;
+    cyg_uint32 csr6;
+
+    a2fxxx_eth_printf("ETH A2Fxxx - Start\n");
+
+    // Stop RX / TX
+    HAL_READ_UINT32( (qi->base + CYGHWR_HAL_A2FXXX_MAC_CSR6), csr6);
+    csr6 |= ( CYGHWR_HAL_A2FXXX_MAC_CSR6_SR | CYGHWR_HAL_A2FXXX_MAC_CSR6_ST );
+    HAL_WRITE_UINT32( (qi->base + CYGHWR_HAL_A2FXXX_MAC_CSR6), csr6);
+}
+
+//
+// This function is called for low level "control" operations
+//
+static int
+a2fxxx_eth_control(struct eth_drv_sc *sc, unsigned long key,
+                   void *data, int length)
+{
+    switch (key)
+    {
+    case ETH_DRV_SET_MAC_ADDRESS:
+        return 0;
+        break;
+#ifdef ETH_DRV_SET_MC_ALL
+    case ETH_DRV_SET_MC_ALL:
+    case ETH_DRV_SET_MC_LIST:
+        // TODO
+        return 0;
+        break;
+#endif
+    default:
+        return 1;
+        break;
+    }
+}
+
+//
+// This function is called to see if another packet can be sent.
+// It should return the number of packets which can be handled.
+// Zero should be returned if the interface is busy and can not send any more.
+//
+static int
+a2fxxx_eth_can_send(struct eth_drv_sc *sc)
+{
+    struct a2fxxx_eth_info *qi = (struct a2fxxx_eth_info *) sc->driver_private;
+
+    return (qi->txactive < CYGNUM_DEVS_ETH_CORTEXM_A2FXXX_TxNUM);
+}
+
+//
+// This routine is called to send data to the hardware.
+static void
+a2fxxx_eth_send(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list,
+                int sg_len, int total_len, unsigned long key)
+{
+    struct a2fxxx_eth_info *qi = (struct a2fxxx_eth_info *) sc->driver_private;
+    volatile struct a2fxxx_bd *txbd, *txfirst;
+    cyg_uint32 des1 = CYGHWR_HAL_A2FXXX_MAC_TDES1_TBS1(total_len);
+    volatile cyg_uint8 *bp;
+    cyg_int32 i, txindex, istate;
+
+    a2fxxx_eth_printf("ETH A2Fxxx - transmit pkt, length %d\n", total_len);
+
+    // Continue from current descriptor
+    txbd = txfirst = qi->txbd;
+
+    if ( (txbd->des0 & CYGHWR_HAL_A2FXXX_MAC_TDES0_OWN) == CYGHWR_HAL_A2FXXX_MAC_TDES0_OWN ) {
+        a2fxxx_eth_printf("ETH A2Fxxx - Fatal error, no free BD for %s\n",
+            sc->dev_name);
+        a2fxxx_eth_printf("ETH A2Fxxx - Cannot transmit packet, active pkt in queue:%d\n",
+            qi->txactive);
+        return;
+    }
+
+    // Set up buffer
+    bp = (cyg_uint8 *) txbd->des2;
+    for (i = 0; i < sg_len; i++) {
+        memcpy((void *) bp, (void *) sg_list[i].buf, sg_list[i].len);
+        bp += sg_list[i].len;
+    }
+
+    if (txbd->des1 & CYGHWR_HAL_A2FXXX_MAC_TDES1_TER) {
+        des1 |= CYGHWR_HAL_A2FXXX_MAC_TDES1_TER;
+    }
+    txbd->des1 =
+      ( des1 | CYGHWR_HAL_A2FXXX_MAC_TDES1_LS | CYGHWR_HAL_A2FXXX_MAC_TDES1_FS |
+        CYGHWR_HAL_A2FXXX_MAC_TDES1_IC );
+    txindex = ((unsigned long) txbd - (unsigned long) qi->tbase)
+                / sizeof(*txbd);
+    qi->txkey[txindex] = key;
+
+    qi->txactive++;
+
+    HAL_DISABLE_INTERRUPTS(istate);
+    // Give ownership to the MAC
+    txbd->des0    = CYGHWR_HAL_A2FXXX_MAC_TDES0_OWN;
+    txbd->private = CYGHWR_HAL_A2FXXX_MAC_PRIVATE_SENT;
+
+    // Remember the next buffer to try
+    if (qi->txbd->des1 & CYGHWR_HAL_A2FXXX_MAC_TDES1_TER){
+        qi->txbd = qi->tbase;
+    }
+    else {
+        qi->txbd++;
+    }
+    HAL_RESTORE_INTERRUPTS(istate);
+}
+
+//
+// This function is called for low level "control" operations
+//
+static void
+a2fxxx_eth_TxEvent(struct eth_drv_sc *sc)
+{
+    struct a2fxxx_eth_info *qi = (struct a2fxxx_eth_info *) sc->driver_private;
+    volatile struct a2fxxx_bd *txbd;
+    int key, txindex;
+
+    txbd = qi->tnext;
+
+    while ( ( (txbd->des0 & CYGHWR_HAL_A2FXXX_MAC_TDES0_OWN ) == 0) &&
+              (txbd->private & CYGHWR_HAL_A2FXXX_MAC_PRIVATE_SENT) )
+    {
+        txindex = ((unsigned long) txbd - (unsigned long) qi->tbase)
+                / sizeof(*txbd);
+        if ((key = qi->txkey[txindex]) != 0)
+        {
+            qi->txkey[txindex] = 0;
+            (sc->funs->eth_drv->tx_done)(sc, key, 0);
+        }
+        qi->txactive--;
+#ifdef CYGSEM_DEVS_ETH_CORTEXM_A2FXXX_STATS
+        if( (txbd->des0 & CYGHWR_HAL_A2FXXX_MAC_TDES0_LC) ) {
+            A2FXXX_INC_STATS(tx_late_collision);
+        }
+        if( (txbd->des0 & CYGHWR_HAL_A2FXXX_MAC_TDES0_NC) ) {
+            A2FXXX_INC_STATS(tx_no_carrier);
+        }
+        if( (txbd->des0 & CYGHWR_HAL_A2FXXX_MAC_TDES0_LO) ) {
+            A2FXXX_INC_STATS(tx_loss_carrier);
+        }
+        if( (txbd->des0 & CYGHWR_HAL_A2FXXX_MAC_TDES0_DE) ) {
+            A2FXXX_INC_STATS(tx_frame_deferred);
+        }
+        if( (txbd->des0 & CYGHWR_HAL_A2FXXX_MAC_TDES0_UF) ) {
+            A2FXXX_INC_STATS(tx_fifo_underflow);
+        }
+#endif
+        txbd->private &= ~CYGHWR_HAL_A2FXXX_MAC_PRIVATE_SENT;
+        if (txbd->des1 & CYGHWR_HAL_A2FXXX_MAC_TDES1_TER){
+            txbd = qi->tbase;
+        } else {
+            txbd++;
+        }
+    }
+    // Remember where we left off
+    qi->tnext = (struct a2fxxx_bd *) txbd;
+}
+
+//
+// Interrupt vector
+//
+static int
+a2fxxx_eth_int_vector(struct eth_drv_sc *sc)
+{
+#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
+    struct a2fxxx_eth_info *qi = (struct a2fxxx_eth_info *) sc->driver_private;
+    return (qi->vector);
+#else
+    return 0;
+#endif
+}
+
+//
+// This function is called when a packet has been received.  It's job is
+// to prepare to unload the packet from the hardware.  Once the length of
+// the packet is known, the upper layer of the driver can be told.  When
+// the upper layer is ready to unload the packet, the internal function
+// 'fec_eth_recv' will be called to actually fetch it from the hardware.
+//
+static void
+a2fxxx_eth_RxEvent(struct eth_drv_sc *sc)
+{
+  struct a2fxxx_eth_info *qi = (struct a2fxxx_eth_info *) sc->driver_private;
+  volatile struct a2fxxx_bd *rxbd, *rxfirst;
+  int cnt_bd = 0;
+  cyg_int32 len;
+
+  rxbd = rxfirst = qi->rnext;
+  while ((rxbd->des0 & CYGHWR_HAL_A2FXXX_MAC_RDES0_OWN) == 0)
+  {
+    cnt_bd++;
+    qi->rxbd = rxbd; // Save for callback
+
+#ifdef CYGSEM_DEVS_ETH_CORTEXM_A2FXXX_STATS
+    if( (rxbd->des0 & CYGHWR_HAL_A2FXXX_MAC_RDES0_CE) ) {
+        A2FXXX_INC_STATS(rx_crc_err);
+    }
+    if( (rxbd->des0 & CYGHWR_HAL_A2FXXX_MAC_RDES0_CS) ) {
+        A2FXXX_INC_STATS(rx_collision);
+    }
+    if( (rxbd->des0 & CYGHWR_HAL_A2FXXX_MAC_RDES0_TL) ) {
+        A2FXXX_INC_STATS(rx_too_long);
+    }
+    if( (rxbd->des0 & CYGHWR_HAL_A2FXXX_MAC_RDES0_MF) ) {
+        A2FXXX_INC_STATS(rx_mcast);
+    }
+    if( (rxbd->des0 & CYGHWR_HAL_A2FXXX_MAC_RDES0_DE) ) {
+        A2FXXX_INC_STATS(rx_des_err);
+    }
+#endif
+
+    len = (rxbd->des0 >> 16) & 0x3fff;
+    a2fxxx_eth_printf("ETH A2Fxxx - Receive pkt, length %d\n", len);
+
+    (sc->funs->eth_drv->recv)(sc, len - 4);
+    if (rxbd->des1 & CYGHWR_HAL_A2FXXX_MAC_RDES1_RER){
+        rxbd = qi->rbase;
+    } else {
+        rxbd++;
+    }
+  }
+  // Remember where we left off
+  qi->rnext = (struct a2fxxx_bd *) rxbd;
+}
+
+//
+// Interrupt processing
+//
+static void
+a2fxxx_eth_int(struct eth_drv_sc *sc)
+{
+    struct a2fxxx_eth_info *qi = (struct a2fxxx_eth_info *) sc->driver_private;
+    cyg_uint32 csr5;
+
+     // Check interrupt status
+     HAL_READ_UINT32( (qi->base + CYGHWR_HAL_A2FXXX_MAC_CSR5), csr5);
+
+     if (( csr5 & CYGHWR_HAL_A2FXXX_MAC_CSR5_TI ) != 0)
+     {
+         a2fxxx_eth_printf("%s - Transmit packet irq\n", sc->dev_name);
+         a2fxxx_eth_TxEvent(sc);
+         HAL_WRITE_UINT32( CYGHWR_HAL_A2FXXX_MAC_CSR5_TI_BB(qi->base_bb), 1);
+     }
+
+     if (( csr5 & CYGHWR_HAL_A2FXXX_MAC_CSR5_RI ) != 0)
+     {
+         a2fxxx_eth_printf("%s - Receive packet irq\n", sc->dev_name);
+         a2fxxx_eth_RxEvent(sc);
+         HAL_WRITE_UINT32( CYGHWR_HAL_A2FXXX_MAC_CSR5_RI_BB(qi->base_bb), 1);
+     }
+}
+
+//
+// Polling function
+//
+static void
+a2fxxx_eth_poll(struct eth_drv_sc *sc)
+{
+     a2fxxx_eth_int(sc);
+     CYGACC_CALL_IF_DELAY_US(500);
+}
+
+static void
+a2fxxx_eth_recv(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len)
+{
+    struct a2fxxx_eth_info *qi = (struct a2fxxx_eth_info *) sc->driver_private;
+    cyg_uint8 *bp;
+    int i;
+
+    bp = (cyg_uint8 *) qi->rxbd->des2;
+    for (i = 0; i < sg_len; i++)
+    {
+        if (sg_list[i].buf != 0)
+        {
+            memcpy((void *) sg_list[i].buf, bp, sg_list[i].len);
+            bp += sg_list[i].len;
+        }
+    }
+    qi->rxbd->des0 |= CYGHWR_HAL_A2FXXX_MAC_RDES0_OWN;
+}
+
+
+//
+// Display ETH statistics
+//
+#ifdef CYGSEM_DEVS_ETH_CORTEXM_A2FXXX_STATS
+externC void
+a2fxxx_disp_stats(struct eth_drv_sc *sc , char flag )
+{
+    struct a2fxxx_eth_info *qi = (struct a2fxxx_eth_info *) sc->driver_private;
+
+    diag_printf("Ethernet Statistics\n");
+
+    diag_printf("%17s %10d\n",
+        "RX MCAST",
+        qi->stats.rx_mcast);
+    diag_printf("%17s %10d\n",
+        "RX DESC ERR",
+        qi->stats.rx_des_err);
+    diag_printf("%17s %10d\n",
+        "RX COLLISION",
+        qi->stats.rx_collision);
+    diag_printf("%17s %10d\n",
+        "RX CRC ERR",
+        qi->stats.rx_crc_err);
+    diag_printf("%17s %10d\n",
+        "RX TOO LONG",
+        qi->stats.rx_too_long);
+    diag_printf("%17s %10d\n",
+        "TX FIFO UNDERFLOW",
+        qi->stats.tx_fifo_underflow);
+    diag_printf("%17s %10d\n",
+        "TX FRAME DEFERRED",
+        qi->stats.tx_frame_deferred);
+    diag_printf("%17s %10d\n",
+        "TX LATE COLLISION",
+        qi->stats.tx_late_collision);
+    diag_printf("%17s %10d\n",
+        "TX NO CARRIER",
+        qi->stats.tx_no_carrier);
+    diag_printf("%17s %10d\n",
+        "TX LOSS CARRIER",
+        qi->stats.tx_loss_carrier);
+}
+#endif
+
+// EOF if_a2fxxx.c