changeset 2548:571e0ca40e73

* LPC2xxx ethernet driver package created * cdl/lpc2xxx_eth.cdl * src/if_lpc2xxx.c
author asl
date Thu, 11 Sep 2008 17:51:53 +0000
parents 3cff44606584
children 841d400dc642
files packages/devs/eth/arm/lpc2xxx/current/ChangeLog packages/devs/eth/arm/lpc2xxx/current/cdl/lpc2xxx_eth.cdl packages/devs/eth/arm/lpc2xxx/current/doc/readme.txt packages/devs/eth/arm/lpc2xxx/current/src/if_lpc2xxx.c
diffstat 4 files changed, 1555 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/packages/devs/eth/arm/lpc2xxx/current/ChangeLog
@@ -0,0 +1,38 @@
+2008-09-09  Uwe Kindler  <uwe_kindler@web.de>
+
+	* LPC2xxx ethernet driver package created
+	* cdl/lpc2xxx_eth.cdl
+	* src/if_lpc2xxx.c
+
+//===========================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 2008 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.,
+// 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.
+//
+// -------------------------------------------
+//####ECOSGPLCOPYRIGHTEND####
+//===========================================================================
new file mode 100644
--- /dev/null
+++ b/packages/devs/eth/arm/lpc2xxx/current/cdl/lpc2xxx_eth.cdl
@@ -0,0 +1,181 @@
+#==========================================================================
+#
+#      lpc2xxx_eth.cdl
+#
+#      Ethernet driver for NXP LPC2xxx Cores
+#
+#==========================================================================
+#####ECOSGPLCOPYRIGHTBEGIN####
+## -------------------------------------------
+## This file is part of eCos, the Embedded Configurable Operating System.
+## Copyright (C) 2008 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.,
+## 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.
+## -------------------------------------------
+#####ECOSGPLCOPYRIGHTEND####
+#==========================================================================
+######DESCRIPTIONBEGIN####
+#
+# Author(s):    Uwe Kindler
+# Date:         2008-08-05
+#
+#####DESCRIPTIONEND####
+#
+#========================================================================*/
+
+cdl_package CYGPKG_DEVS_ETH_ARM_LPC2XXX {
+    display       "NXP LPC2xxx ethernet driver"
+    parent        CYGPKG_IO_ETH_DRIVERS
+    active_if     CYGPKG_IO_ETH_DRIVERS
+    implements    CYGHWR_NET_DRIVERS
+    implements    CYGHWR_NET_DRIVER_ETH0
+
+    include_dir   net
+    description   "
+       Ethernet driver for NXP LPC2xxx core. This has been tested with the 
+       LPC2468, but should be easy to get to work on other LPC2xxx cores that
+       have the EMAC peripheral."
+
+    compile       -library=libextras.a if_lpc2xxx.c
+
+    cdl_option CYGPKG_DEVS_ETH_ARM_LPC2XXX_DEBUG_LEVEL {
+         display "Driver debug output level"
+         flavor  data
+         legal_values {0 1 2 3}
+         default_value 1
+         description   "
+             This option specifies the level of debug data output by
+             the LPC2XXX ethernet device driver. A value of 0 signifies
+             no debug data output; 1 signifies normal debug data
+             output; and 2 signifies maximum debug data output (not
+             suitable when GDB and application are sharing an ethernet
+             port). Debug level 3 is a special kind of debug level. 
+             It prints the same debug messages like level 2 but it forces 
+             the debug output to the second serial port. This is 
+             required for debugging the ethernet driver if the stand-alone 
+             network stack from redboot is used. It is possible to print 
+             debug messages while an application is loaded and run via GDB
+             TCP/IP connection"
+    }
+    
+    cdl_option CYGPKG_DEVS_ETH_ARM_LPC2XXX_INTPRIO {
+        display       "Interrupt priority"
+        flavor        data
+        legal_values  0 to 15
+        default_value 15
+        description   "
+            This option selects the interrupt priority for the EMAC interrupts. 
+            There are 16 priority levels corresponding to the values 0 through 
+            15 decimal, of which 15 is the lowest priority. The reset value of 
+            these registers defaults all interrupts to the lowest priority, 
+            allowing a single write to elevate the priority of an 
+            individual interrupt."
+    }
+
+    cdl_option CYGNUM_DEVS_ETH_ARM_LPC2XXX_RX_BUFS {
+        display       "Number of RX buffers"
+        flavor        data
+        default_value 4
+        legal_values  2 to 4
+        description   "
+            Number of receive buffers. The number of receive buffers defines 
+            the number of descriptors in the descriptor array. Each receive 
+            descriptor element in the descriptor array has an associated status 
+            field which consists of the HashCRC word and Status Information 
+            word. Because of a device errata the status words are updated 
+            incorrectly if the number of descriptors set is greater than or 
+            equal to 5. Therefore the Rx buffers are limited to a maximum 
+            of 4. Each buffer is 1536 (0x600) bytes in size"
+    }
+
+    cdl_option CYGNUM_DEVS_ETH_ARM_LPC2XXX_TX_BUFS {
+        display       "Number of TX descriptors"
+        flavor        data
+        default_value 10
+        legal_values  2 to 10
+        description   "
+            Number of transmit buffer descriptors. We need one descriptor
+            for each element in the scatter/gather list."
+    }
+
+
+    cdl_component CYGPKG_DEVS_ETH_ARM_LPC2XXX_REDBOOT_HOLDS_ESA {
+        display         "RedBoot manages ESA initialization data"
+        flavor          bool
+        default_value   0
+
+        active_if     CYGSEM_HAL_VIRTUAL_VECTOR_SUPPORT
+        active_if     (CYGPKG_REDBOOT || CYGSEM_HAL_USE_ROM_MONITOR)
+
+        description   "
+            Enabling this option will allow the ethernet station
+            address to be acquired from RedBoot's configuration data,
+            stored in flash memory.  It can be overridden individually
+            by the 'Set the ethernet station address' option for each
+            interface."
+
+
+        cdl_component CYGPKG_DEVS_ETH_ARM_LPC2XXX_REDBOOT_HOLDS_ESA_VARS {
+            display        "Export RedBoot command to set ESA in FLASH config"
+            flavor         none
+            no_define
+            description "
+                This component contains options which, when enabled, allow
+                RedBoot to support the setting of the ESA in the FLASH
+                configuration. This can then subsequently be accessed by
+                applications using virtual vector calls if those applications
+                are also built with
+                CYGPKG_DEVS_ETH_ARM_LPC2XXX_REDBOOT_HOLDS_ESA enabled."
+
+            cdl_option CYGSEM_DEVS_ETH_ARM_LPC2XXX_REDBOOT_HOLDS_ESA_ETH0 {
+                display         "RedBoot manages ESA for eth0"
+                flavor          bool
+                default_value   1
+                active_if       CYGSEM_REDBOOT_FLASH_CONFIG
+                active_if       CYGPKG_REDBOOT_NETWORKING
+            }
+        }
+    }
+    cdl_option CYGPKG_DEVS_ETH_ARM_LPC2XXX_MACADDR {
+        display "Ethernet station (MAC) address for eth0"
+        flavor  data
+        default_value {"0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC"}
+        description   "The default ethernet station address. This is the
+                       MAC address used when no value is found in the
+                       RedBoot FLASH configuration field."
+    }
+
+    cdl_option  CYGPKG_DEVS_ETH_ARM_LPC2XXX_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 Atmel LPC2XXX ethernet driver package.
+            These flags are used in addition to the set of global flags."
+    }
+}
+
+# EOF LPC2XXX_eth.cdl
new file mode 100644
--- /dev/null
+++ b/packages/devs/eth/arm/lpc2xxx/current/doc/readme.txt
@@ -0,0 +1,35 @@
+Important note:
+--------------
+
+The LPC2xxx EMAC driver was tested with the LPC2468 OEM board from Embedded
+Artists. The driver was tested with the lwIP TCP/IP stack and with Redboot.
+
+While the driver works fine for the lwIP TCP/IP stack and in Redboot, it
+is not possible to debug eCos applications via GDB ethernet connection or 
+run eCos applications from Redboot command line if you are connected to 
+Redboot via TCP/IP. You can test this, if you run or debug the tm_basic
+test. The tm_basic test crashes, as soon as cyg_thread_delay() is executed
+from the main thread (line 1678 in tm_basic.cxx). This call would cause a 
+context switch.
+
+I did a lot of debugging to identify the reason for this strange behaviour
+but could not get a final result. The EMAC driver contains a lot of debug
+messages. From reading the debug messages I'm quite sure that there is
+no memory or stack violation from the ethernet driver. The multi-threaded
+lwIP TCP echo test works without any poblems and also the programm upload
+via GDB or the Redboot communication vi TCP/IP works fine.
+
+It is no problem to start the tm_basic test from the Redboot command line
+if you are connected via serial line. If you are connected via TCP/IP then
+the tm_basic test crashes as soon as it comes to the cyg_thread_delay()
+in line 1687. If the tm_basic test is started from Redboot (connected
+via TCP/IP) with the -n option that forces Redboot to stop the ethernet
+driver, then the test does not crash. So there seems to be a problem if
+the TCP/IP stack is active if Redboot starts a RAM application. I did a
+lot of JTAG debugging to find the reason for this behaviour but was not
+succesful.
+
+I decided to release the driver because I'm quite sure that the driver
+itself works well. Maybe this is a board related issue or a startup code
+related issue. Maybe someone will find the reason.
+ 
new file mode 100644
--- /dev/null
+++ b/packages/devs/eth/arm/lpc2xxx/current/src/if_lpc2xxx.c
@@ -0,0 +1,1301 @@
+//==========================================================================
+//
+//      if_lpc2xxx.c
+//
+//==========================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 2008 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.,
+// 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.
+// -------------------------------------------
+//####ECOSGPLCOPYRIGHTEND####
+//==========================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s):    Uwe Kindler
+// Contributors:
+// Date:         2008-08-10
+// Description:  Hardware driver for LPC2xxx on-chip EMAC peripheral
+//
+//               This driver was originally written for LPC2468 and may
+//               require some modifications to work on other LPC2xxx
+//               variants as well.
+//
+//####DESCRIPTIONEND####
+//
+//========================================================================*/
+
+
+#include <pkgconf/system.h>
+#include <pkgconf/hal.h>
+#include <pkgconf/devs_eth_arm_lpc2xxx.h>
+#include <pkgconf/io_eth_drivers.h>
+#if defined(CYGPKG_REDBOOT)
+   #include <pkgconf/redboot.h>
+#endif
+
+#include <cyg/hal/hal_io.h>
+#include <cyg/hal/hal_intr.h>
+#include <cyg/hal/hal_arch.h>
+#include <cyg/hal/drv_api.h>
+#include <cyg/hal/hal_diag.h>
+#include <cyg/infra/cyg_type.h>
+#include <cyg/infra/cyg_ass.h>
+#include <cyg/infra/diag.h>
+#include <cyg/io/eth/netdev.h>
+#include <cyg/io/eth/eth_drv.h>
+#include <cyg/io/eth/eth_drv_stats.h>
+#include <cyg/io/eth_phy.h>
+#include <errno.h>
+#include <string.h>
+#ifdef CYGPKG_NET
+#include <pkgconf/net.h>
+#include <net/if.h>  /* Needed for struct ifnet */
+#endif
+#include <cyg/io/eth/eth_drv_stats.h>
+
+
+//===========================================================================
+//                                DEFINES
+//===========================================================================
+#ifndef CYGARC_HAL_LPC2XXX_REG_EMAC_BASE
+#error "CYGARC_HAL_LPC2XXX_REG_EMAC_BASE not defined by varaint/platform."
+#else
+#define EMAC_BASE CYGARC_HAL_LPC2XXX_REG_EMAC_BASE
+#endif
+
+//
+// MAC registers
+//
+#define EMAC_MAC1   (EMAC_BASE + 0x0000)
+#define EMAC_MAC2   (EMAC_BASE + 0x0004)
+#define EMAC_IPGT   (EMAC_BASE + 0x0008)
+#define EMAC_IPGR   (EMAC_BASE + 0x000C)
+#define EMAC_CLRT   (EMAC_BASE + 0x0010)
+#define EMAC_MAXF   (EMAC_BASE + 0x0014)
+#define EMAC_SUPP   (EMAC_BASE + 0x0018)
+#define EMAC_TEST   (EMAC_BASE + 0x001C)
+#define EMAC_MCFG   (EMAC_BASE + 0x0020)
+#define EMAC_MCMD   (EMAC_BASE + 0x0024)
+#define EMAC_MADR   (EMAC_BASE + 0x0028)
+#define EMAC_MWTD   (EMAC_BASE + 0x002C)
+#define EMAC_MRDD   (EMAC_BASE + 0x0030)
+#define EMAC_MIND   (EMAC_BASE + 0x0034)
+#define EMAC_SA0    (EMAC_BASE + 0x0040)
+#define EMAC_SA1    (EMAC_BASE + 0x0044)
+#define EMAC_SA2    (EMAC_BASE + 0x0048)
+
+//
+// Control registers
+//
+#define EMAC_CMD            (EMAC_BASE + 0x0100)
+#define EMAC_STAT           (EMAC_BASE + 0x0104)
+#define EMAC_RXDESC         (EMAC_BASE + 0x0108)
+#define EMAC_RXSTAT         (EMAC_BASE + 0x010C)
+#define EMAC_RXDESC_NUM     (EMAC_BASE + 0x0110)
+#define EMAC_RX_PROD_IDX    (EMAC_BASE + 0x0114)
+#define EMAC_RX_CONSUME_IDX (EMAC_BASE + 0x0118)
+#define EMAC_TXDESC         (EMAC_BASE + 0x011C)
+#define EMAC_TXSTAT         (EMAC_BASE + 0x0120)
+#define EMAC_TXDESC_NUM     (EMAC_BASE + 0x0124)
+#define EMAC_TX_PROD_IDX    (EMAC_BASE + 0x0128)
+#define EMAC_TX_CONSUME_IDX (EMAC_BASE + 0x012C)
+#define EMAC_TSV0           (EMAC_BASE + 0x0158)
+#define EMAC_TSV1           (EMAC_BASE + 0x015C)
+#define EMAC_RSV            (EMAC_BASE + 0x0160)
+#define EMAC_FLOWCTRL_CNT   (EMAC_BASE + 0x0170)
+#define EMAC_FLOWCTRL_STAT  (EMAC_BASE + 0x0174)
+
+//
+// Rx filter registers
+//
+#define EMAC_RXFILT_CTRL      (EMAC_BASE + 0x0200)
+#define EMAC_RXFILT_WOL_STAT  (EMAC_BASE + 0x0204)
+#define EMAC_RXFILT_WOL_CLR   (EMAC_BASE + 0x0204)
+#define EMAC_HASH_FILT_L      (EMAC_BASE + 0x0210)
+#define EMAC_HASH_FILT_H      (EMAC_BASE + 0x0214)
+
+//
+// Module control registers
+//
+#define EMAC_INT_STATUS       (EMAC_BASE + 0x0FE0)
+#define EMAC_INT_EN           (EMAC_BASE + 0x0FE4)
+#define EMAC_INT_CLR          (EMAC_BASE + 0x0FE8)
+#define EMAC_INT_SET          (EMAC_BASE + 0x0FEC)
+#define EMAC_POWER_DOWN       (EMAC_BASE + 0x0FF4)
+#define EMAC_MODULE_ID        (EMAC_BASE + 0x0FFC)
+
+//
+// Register bits MCMD
+//
+#define  MCMD_WRITE           0x00
+#define  MCMD_READ            0x01
+
+//
+// Register bits COMMAND register
+//
+#define CMD_RX_ENABLE        (1 << 0)
+#define CMD_TX_ENABLE        (1 << 1)
+#define CMD_REG_RESET        (1 << 3)
+#define CMD_TX_RESET         (1 << 4)
+#define CMD_RX_RESET         (1 << 5)
+#define CMD_PASS_RUNT_FRAME  (1 << 6)
+#define CMD_PASS_RX_FILTER   (1 << 7)
+#define CMD_TX_FLOW_CONTROL  (1 << 8)
+#define CMD_RMII             (1 << 9)
+#define CMD_FULL_DUPLEX      (1 << 10)
+
+//
+// Register bits SUPP register
+//
+#define SUPP_SPEED_100MB     (1 << 8)
+
+#define  EMAC_OLD_MODULE_ID  ((0x3902 << 16) | 0x2000)
+
+//
+// Register bits MAC1
+//
+#define MAC1_RX_EN                (1 << 0)
+#define MAC1_PASS_ALL_RX_FRAMES   (1 << 1)
+#define MAC1_RX_FLOW_CTRL         (1 << 2)
+#define MAC1_TX_FLOW_CTRL         (1 << 3)
+#define MAC1_LOOPBACK             (1 << 4)
+#define MAC1_RESET_TX             (1 << 8)
+#define MAC1_RESET_MCS_TX         (1 << 9)
+#define MAC1_RESET_RX             (1 << 10)
+#define MAC1_RESET_MCS_RX         (1 << 11)
+#define MAC1_RESET_SIM            (1 << 14)
+#define MAC1_SOFT_RESET           (1 << 15)
+
+//
+// Register bits MAC2
+//
+#define MAC2_FDX                  (1 << 0)
+#define MAC2_FRAME_LEN_CHECK      (1 << 1)
+#define MAC2_HUGE_FRAME_EN        (1 << 2)
+#define MAC2_DELAYED_CRC          (1 << 3)
+#define MAC2_CRC_EN               (1 << 4)
+#define MAC2_PAD_CRC_EN           (1 << 5)
+#define MAC2_VLAN_PAD_EN          (1 << 6)
+#define MAC2_AUTO_DETECT_PAD_EN   (1 << 7)
+#define MAC2_PURE_PREAMBLE        (1 << 8)
+#define MAC2_LONG_PREAMBLE        (1 << 9)
+#define MAC2_NO_BACKOFF           (1 << 12)
+#define MAC2_BACK_PRESS           (1 << 13)
+#define MAC2_EXCESS_DEFER         (1 << 14)
+
+//
+// Register bits RXFILT_CTRL
+//
+#define RXFILT_CTRL_ACC_UNICAST            (1 << 0)
+#define RXFILT_CTRL_ACC_BROADCAST          (1 << 1)
+#define RXFILT_CTRL_ACC_MULTICAST          (1 << 2)
+#define RXFILT_CTRL_ACC_UNICAST_HASH       (1 << 3)
+#define RXFILT_CTRL_ACC_MULTICAST_HASH     (1 << 4)
+#define RXFILT_CTRL_ACC_PERFECT            (1 << 5)
+#define RXFILT_CTRL_MAGIG_PKT_WOL          (1 << 12)
+#define RXFILT_CTRL_RXFILT_WOL             (1 << 13)
+
+//
+// Register bits MCFG
+//
+#define MCFG_SCAN_INCR      (1 << 0)
+#define MCFG_SUPP_PREAMBLE  (1 << 1)
+#define MCFG_RESET_MII_MGMT (1 << 15)
+
+//
+// Register bits MIND
+//
+#define MIND_BUSY      (1 << 0)
+#define MIND_SCANNING  (1 << 1)
+#define MIND_NOT_VALID (1 << 2)
+#define MIND_LINK_FAIL (1 << 3)
+
+//
+// EMAC interrupts
+//
+#define EMAC_INT_RXOVERRUN  (1 << 0)
+#define EMAC_INT_RXERROR    (1 << 1)
+#define EMAC_INT_RXFINISHED (1 << 2)
+#define EMAC_INT_RXDONE     (1 << 3)
+#define EMAC_INT_TXUNDERRUN (1 << 4)
+#define EMAC_INT_TXERROR    (1 << 5)
+#define EMAC_INT_TXFINISHED (1 << 6)
+#define EMAC_INT_TXDONE     (1 << 7)
+#define EMAC_INT_SOFTINT    (1 << 12)
+#define EMAC_INT_WOL        (1 << 13)
+
+
+//===========================================================================
+//                ETHERNET RAM AND DMA CONFIGURATION
+//===========================================================================
+#define EMAC_RAM_BASE          0x7FE00000
+#define EMAC_RAM_SIZE          0x00004000
+#define EMAC_BLOCK_SIZE        0x600
+
+//
+// EMAC Descriptor TX and RX Control fields
+//
+#define EMAC_TX_DESC_INT        0x80000000
+#define EMAC_TX_DESC_LAST       0x40000000
+#define EMAC_TX_DESC_CRC        0x20000000
+#define EMAC_TX_DESC_PAD        0x10000000
+#define EMAC_TX_DESC_HUGE       0x08000000
+#define EMAC_TX_DESC_OVERRIDE   0x04000000
+#define EMAC_RX_DESC_INT        0x80000000
+
+//
+// EMAC Descriptor status related definition
+//
+#define TX_DESC_STATUS_ERR      0x80000000
+#define TX_DESC_STATUS_NODESC   0x40000000
+#define TX_DESC_STATUS_UNDERRUN 0x20000000
+#define TX_DESC_STATUS_LCOL     0x10000000
+#define TX_DESC_STATUS_ECOL     0x08000000
+#define TX_DESC_STATUS_EDEFER   0x04000000
+#define TX_DESC_STATUS_DEFER    0x02000000
+#define TX_DESC_STATUS_COLCNT   0x01E00000
+
+#define RX_DESC_STATUS_ERR      0x80000000
+#define RX_DESC_STATUS_LAST     0x40000000
+#define RX_DESC_STATUS_NODESC   0x20000000
+#define RX_DESC_STATUS_OVERRUN  0x10000000
+#define RX_DESC_STATUS_ALGNERR  0x08000000
+#define RX_DESC_STATUS_RNGERR   0x04000000
+#define RX_DESC_STATUS_LENERR   0x02000000
+#define RX_DESC_STATUS_SYMERR   0x01000000
+#define RX_DESC_STATUS_CRCERR   0x00800000
+#define RX_DESC_STATUS_BCAST    0x00400000
+#define RX_DESC_STATUS_MCAST    0x00200000
+#define RX_DESC_STATUS_FAILFLT  0x00100000
+#define RX_DESC_STATUS_VLAN     0x00080000
+#define RX_DESC_STATUS_CTLFRAM  0x00040000
+
+#define DESC_SIZE_MASK          0x000007FF
+
+//
+// This type defines a descriptor
+//
+typedef struct descriptor_st
+{
+    cyg_uint32 packet;
+    cyg_uint32 control;
+} descriptor_t;
+
+//
+// This type defines a rx status
+// (double word aligned)
+//
+typedef struct rx_status_st
+{
+    cyg_uint32 info;
+    cyg_uint32 hash_crc;
+} rx_status_t;
+
+//
+// This type defines a tx status
+//
+typedef cyg_uint32 tx_status_t; 
+typedef union eth_buf_st
+{
+    cyg_uint32 dwords[EMAC_BLOCK_SIZE / sizeof(cyg_uint32)];
+    cyg_uint8  bytes[EMAC_BLOCK_SIZE];
+} eth_buf_t;
+
+
+//
+// Ethernet RAM and buffer configuration type
+// The descriptors and statuses require a certain alignment. By defining each
+// data type we assure this alignment.
+// The TX descriptor array, the TX status array and the RX descriptor array
+// need to be aligned on a 4 Byte (32 bit) address boundary. The RX status
+// array need to be aligned on a 8 Byte (64 bit) address boundary.
+//
+typedef struct eth_ram_cfg_st
+{
+    volatile rx_status_t  rx_status[CYGNUM_DEVS_ETH_ARM_LPC2XXX_RX_BUFS];
+    volatile descriptor_t rx_descr[CYGNUM_DEVS_ETH_ARM_LPC2XXX_RX_BUFS];
+    volatile tx_status_t  tx_status[CYGNUM_DEVS_ETH_ARM_LPC2XXX_TX_BUFS];
+    volatile descriptor_t tx_descr[CYGNUM_DEVS_ETH_ARM_LPC2XXX_TX_BUFS];
+    volatile eth_buf_t    rx_buf[CYGNUM_DEVS_ETH_ARM_LPC2XXX_RX_BUFS];
+} eth_ram_cfg_t;
+
+
+#if CYGPKG_DEVS_ETH_ARM_LPC2XXX_DEBUG_LEVEL < 3
+#if CYGPKG_DEVS_ETH_ARM_LPC2XXX_DEBUG_LEVEL > 0
+   #define debug1_printf(args...) diag_printf(args)
+#else
+   #define debug1_printf(args...)
+#endif
+#if CYGPKG_DEVS_ETH_ARM_LPC2XXX_DEBUG_LEVEL > 1
+   #define debug2_printf(args...) diag_printf(args)
+#else
+   #define debug2_printf(args...)
+#endif
+#else // CYGPKG_DEVS_ETH_ARM_LPC2XXX_DEBUG_LEVEL > 3
+//
+// Debug level 3 is a special kind of debug level. It prints the same debug
+// messages like level 2 but it forces the debug output to the second serial
+// port. This may be necessary for debugging the ethernet driver if the
+// stand-alone network stack from redboot is used. It is possible to
+// print debug messages while an application is loaded and run via GDB
+// TCP/IP connection
+//
+static int cur;
+#define debug1_printf(args...)                                               \
+{                                                                            \
+   cur =                                                                     \
+   CYGACC_CALL_IF_SET_CONSOLE_COMM(CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT);\
+   CYGACC_CALL_IF_SET_CONSOLE_COMM(1);                                       \
+   diag_printf(args);                                                        \
+   CYGACC_CALL_IF_SET_CONSOLE_COMM(cur);                                     \
+}
+#define debug2_printf(args...)                                               \
+{                                                                            \
+   cur =                                                                     \
+   CYGACC_CALL_IF_SET_CONSOLE_COMM(CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT);\
+   CYGACC_CALL_IF_SET_CONSOLE_COMM(1);                                       \
+   diag_printf(args);                                                        \
+   CYGACC_CALL_IF_SET_CONSOLE_COMM(cur);                                     \
+}
+#endif // CYGPKG_DEVS_ETH_ARM_LPC2XXX_DEBUG_LEVEL > 3
+
+
+//Driver interface callbacks
+#define _eth_drv_init(sc,mac)           \
+  (sc->funs->eth_drv->init)(sc,(unsigned char *)mac)
+#define _eth_drv_tx_done(sc,key,status)     \
+  (sc->funs->eth_drv->tx_done)(sc,key,status)
+#define _eth_drv_recv(sc,len)           \
+  (sc->funs->eth_drv->recv)(sc,len)
+
+#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
+static cyg_uint32
+lpc2xxx_eth_isr (cyg_vector_t vector, cyg_addrword_t data);
+#endif
+
+
+// --------------------------------------------------------------
+// RedBoot configuration options for managing ESAs for us
+
+// Decide whether to have redboot config vars for it...
+#if defined(CYGSEM_REDBOOT_FLASH_CONFIG) && defined(CYGPKG_REDBOOT_NETWORKING)
+#include <redboot.h>
+#include <flash_config.h>
+
+#ifdef CYGSEM_DEVS_ETH_ARM_LPC2XXX_REDBOOT_HOLDS_ESA_ETH0
+RedBoot_config_option("Network hardware address [MAC] for eth0",
+                      eth0_esa_data,
+                      ALWAYS_ENABLED, true,
+                      CONFIG_ESA, 0);
+#endif // CYGSEM_DEVS_ETH_ARM_LPC2XXX_REDBOOT_HOLDS_ESA_ETH0
+#endif  // CYGPKG_REDBOOT_NETWORKING && CYGSEM_REDBOOT_FLASH_CONFIG
+
+// and initialization code to read them
+// - independent of whether we are building RedBoot right now:
+#ifdef CYGPKG_DEVS_ETH_ARM_LPC2XXX_REDBOOT_HOLDS_ESA
+#include <cyg/hal/hal_if.h>
+#ifndef CONFIG_ESA
+    #define CONFIG_ESA (6)
+#endif
+
+#define CYGHWR_DEVS_ETH_ARM_LPC2XXX_GET_ESA( mac_address, ok )        \
+  CYG_MACRO_START                                                     \
+  ok = CYGACC_CALL_IF_FLASH_CFG_OP( CYGNUM_CALL_IF_FLASH_CFG_GET,     \
+                                    "eth0_esa_data",                  \
+                                    mac_address,                      \
+                                    CONFIG_ESA);                      \
+  CYG_MACRO_END
+#endif // CYGPKG_DEVS_ETH_ARM_LPC2XXX_REDBOOT_HOLDS_ESA
+
+//
+// LPC2xxx Ethernet private data
+// The LPC2468 has a single on-chip ethernet device, so all device-specific
+// data could be held in statics. However, in case this driver ever gets
+// re-used for a chip with multiple ethernet devices that data is held
+// in a driver-specific structure.
+//
+typedef struct lpc2xxx_eth_priv_s
+{
+   cyg_uint32              intr_vector;
+   cyg_uint8              *enaddr;
+   cyg_uint32              base;      // Base address of device
+   eth_phy_access_t       *phy;
+   cyg_uint32              cur_tx_key;
+   cyg_uint32              total_len;
+   cyg_uint8               iterator;
+   cyg_bool                tx_busy;
+   volatile eth_ram_cfg_t *ram;
+#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
+   cyg_interrupt intr;
+   cyg_handle_t  intr_handle;
+#endif
+} lpc2xxx_eth_priv_t;
+
+
+//===========================================================================
+//                           PHY SPECIFIC STUFF
+//===========================================================================
+
+
+//===========================================================================
+// Write one of the PHY registers via the MII bus
+//===========================================================================
+static void
+lpc2xxx_write_phy(int reg_addr, int phy_addr, unsigned short data)
+{
+    cyg_uint32 regval;
+
+    HAL_WRITE_UINT32(EMAC_MCMD, MCMD_WRITE);
+    regval = (phy_addr << 8) | reg_addr;
+    HAL_WRITE_UINT32(EMAC_MADR, regval);
+    HAL_WRITE_UINT32(EMAC_MWTD, data);
+
+    do
+    {
+        HAL_READ_UINT32(EMAC_MIND, regval);
+    }
+    while (regval != 0);
+}
+
+
+//===========================================================================
+// Read one of the PHY registers via the MII bus
+//===========================================================================
+static bool
+lpc2xxx_read_phy(int reg_addr, int phy_addr, unsigned short *data)
+{
+    cyg_uint32 regval;
+
+    HAL_WRITE_UINT32(EMAC_MCMD, 0);
+    regval = (phy_addr << 8) | reg_addr;
+    HAL_WRITE_UINT32(EMAC_MADR, regval);
+    HAL_WRITE_UINT32(EMAC_MCMD, MCMD_READ);
+
+    do
+    {
+        HAL_READ_UINT32(EMAC_MIND, regval);
+    }
+    while ((regval & ~MIND_LINK_FAIL) != 0);
+
+    if (regval & MIND_LINK_FAIL)
+    {
+        debug1_printf("LPC2XXX_ETH: PHY link fail\n");
+    }
+
+    HAL_WRITE_UINT32(EMAC_MCMD, 0x0000);
+    HAL_READ_UINT32(EMAC_MRDD, regval);
+    debug2_printf("LPC2XXX_ETH: read result %08x\n", regval);
+    *data = regval & 0xFFFF;
+    return true;
+}
+
+
+//===========================================================================
+// Init device for phy access
+//===========================================================================
+static void lpc2xxx_init_phy(void)
+{
+    int        i;
+    cyg_uint32 regval;
+
+    //
+    // host clock divided by 20, no suppress preamble, no scan increment
+    // Reset management hardware
+    //
+    HAL_WRITE_UINT32(EMAC_MCFG, 0x0018 | MCFG_RESET_MII_MGMT);
+    for (i = 0; i < 0x40; ++i)
+    {
+        asm volatile ("  nop");
+    }
+    HAL_WRITE_UINT32(EMAC_MCFG, 0x0018);
+    HAL_WRITE_UINT32(EMAC_MCMD, MCMD_WRITE);
+
+    HAL_READ_UINT32(EMAC_CMD, regval);
+    HAL_WRITE_UINT32(EMAC_CMD, regval | CMD_RMII); // RMII configuration
+    HAL_WRITE_UINT32(EMAC_SUPP, 0x800 | SUPP_SPEED_100MB);
+    HAL_DELAY_US(50);
+    HAL_WRITE_UINT32(EMAC_SUPP, SUPP_SPEED_100MB);
+    HAL_DELAY_US(1000);
+}
+
+
+//===========================================================================
+// PHY specific data structures
+//===========================================================================
+ETH_PHY_REG_LEVEL_ACCESS_FUNS(lpc2xxx_phy,
+                              lpc2xxx_init_phy,
+                              NULL,
+                              lpc2xxx_write_phy,
+                              lpc2xxx_read_phy);
+
+
+//===========================================================================
+// Hardware driver specific data
+//===========================================================================                              
+lpc2xxx_eth_priv_t lpc2xxx_priv_data =
+{
+   .intr_vector = CYGNUM_HAL_INTERRUPT_ETH,
+   .base        = EMAC_BASE,
+   .phy         = &lpc2xxx_phy,
+   .ram         =  (volatile eth_ram_cfg_t*)EMAC_RAM_BASE,
+   .total_len   = 0,
+   .iterator    = 0,
+   .tx_busy     = false
+};
+
+
+//===========================================================================
+// Initialize TX descriptors
+// To avoid copy operations the higher-level sg_list entries will be 
+// directly put into the device's TX descriptors (ring buffer).
+//===========================================================================
+static void lpc2xxx_eth_txdesc_init(lpc2xxx_eth_priv_t * priv)
+{
+    int i;
+
+    debug1_printf("\nLPC2XXX_ETH: Initialising TX descriptors\n");
+    debug2_printf("&priv->ram->tx_descr[0]:  0x%08X\n", 
+                  (cyg_uint32)priv->ram->tx_descr);
+    debug2_printf("&priv->ram->tx_status[0]: 0x%08X\n",
+                  (cyg_uint32)priv->ram->tx_descr);
+    debug2_printf("No of TX descr: %d\n", 
+                  CYGNUM_DEVS_ETH_ARM_LPC2XXX_TX_BUFS);
+
+    //
+    // Init status for all TX descriptors
+    //
+    for (i = 0; i < CYGNUM_DEVS_ETH_ARM_LPC2XXX_TX_BUFS; i++)
+    {
+        priv->ram->tx_status[i] = 0;
+    }
+
+    //
+    // Initialise TX pointers
+    //
+    HAL_WRITE_UINT32(EMAC_TXDESC, (cyg_uint32)priv->ram->tx_descr);
+    HAL_WRITE_UINT32(EMAC_TXSTAT, (cyg_uint32)priv->ram->tx_status);
+    HAL_WRITE_UINT32(EMAC_TXDESC_NUM, CYGNUM_DEVS_ETH_ARM_LPC2XXX_TX_BUFS - 1);
+    HAL_WRITE_UINT32(EMAC_TX_PROD_IDX, 0);
+}
+
+
+//===========================================================================
+// Initialize RX descriptors
+// The driver currently supports up to 4 rx buffers. In the current 
+// implementation the size of the RX buffers is fixed to 1536 (0x600) bytes.
+// Incoming frames are then copied to higher-level's code sg lists.
+//===========================================================================
+static void lpc2xxx_eth_rxdesc_init(lpc2xxx_eth_priv_t * priv)
+{
+    int i;
+
+    debug1_printf("\nLPC2XXX_ETH: Initialising RX descriptors\n");
+    debug2_printf("&priv->ram->rx_descr[0]:  0x%08X\n",
+                  (cyg_uint32)priv->ram->rx_descr);
+    debug2_printf("&priv->ram->rx_status[0]: 0x%08X\n",
+                  (cyg_uint32)priv->ram->rx_status);
+    debug2_printf("No of TX descr: %d\n", CYGNUM_DEVS_ETH_ARM_LPC2XXX_RX_BUFS);
+
+    //
+    // Initialise RX descriptors - store pointers to receive buffers and
+    // RX status, two words, status info. and status hash CRC.
+    //
+    for (i = 0; i < CYGNUM_DEVS_ETH_ARM_LPC2XXX_RX_BUFS; ++i)
+    {
+        priv->ram->rx_descr[i].packet  = (cyg_uint32)priv->ram->rx_buf[i].bytes;
+        debug2_printf("priv->ram->rx_descr[%d].packet (0x%08X) "
+                      "= &priv->ram->rx_buf[%d].bytes[0] (0x%08X)\n", 
+                      i, (cyg_uint32)&priv->ram->rx_descr[i].packet, 
+                      i, (cyg_uint32)priv->ram->rx_buf[i].bytes);
+        priv->ram->rx_descr[i].control = (EMAC_RX_DESC_INT 
+                                       | ((EMAC_BLOCK_SIZE - 1)
+                                          & DESC_SIZE_MASK));
+        debug2_printf("priv->ram->rx_descr[%d].control (0x%08X) = 0x%08X\n", 
+                      i, (cyg_uint32)&priv->ram->rx_descr[i].control, 
+                      priv->ram->rx_descr[i].control);
+        priv->ram->rx_status[i].info     = 0;
+        priv->ram->rx_status[i].hash_crc = 0;
+    }
+
+    //
+    // Initialise RX pointers
+    //
+    HAL_WRITE_UINT32(EMAC_RXDESC, (cyg_uint32)priv->ram->rx_descr);
+    HAL_WRITE_UINT32(EMAC_RXSTAT, (cyg_uint32)priv->ram->rx_status);
+    HAL_WRITE_UINT32(EMAC_RXDESC_NUM, CYGNUM_DEVS_ETH_ARM_LPC2XXX_RX_BUFS - 1);
+    HAL_WRITE_UINT32(EMAC_RX_CONSUME_IDX, 0);
+}
+
+
+//===========================================================================
+// Set a specific address match to a given address. Packets received which
+// match this address will be passed on.
+//===========================================================================
+static void lpc2xxx_set_mac(lpc2xxx_eth_priv_t * priv, cyg_uint8 * enaddr)
+{
+    HAL_WRITE_UINT32(EMAC_SA0, (enaddr[5] << 8 | enaddr[4]));
+    HAL_WRITE_UINT32(EMAC_SA1, (enaddr[3] << 8 | enaddr[2]));
+    HAL_WRITE_UINT32(EMAC_SA2, (enaddr[1] << 8 | enaddr[0]));
+}
+
+
+//===========================================================================
+// This function is called to stop the interface.
+//===========================================================================
+static void lpc2xxx_eth_stop(struct eth_drv_sc *sc)
+{
+    cyg_uint32          regval;
+
+    // Disable the receiver and transmitter
+    HAL_READ_UINT32(EMAC_MAC1, regval);
+    HAL_WRITE_UINT32(EMAC_MAC1, regval & ~MAC1_RX_EN);
+    HAL_READ_UINT32(EMAC_CMD, regval);
+    HAL_WRITE_UINT32(EMAC_CMD, regval & ~(CMD_RX_ENABLE | CMD_TX_ENABLE));
+}
+
+
+//===========================================================================
+// This function is called to "start up" the interface. It may be called
+// multiple times, even when the hardware is already running.
+//===========================================================================
+static void
+lpc2xxx_eth_start(struct eth_drv_sc *sc, unsigned char *enaddr, int flags)
+{
+    cyg_uint32  regval;
+    
+    //
+    // To prevent overflow in the receive DMA engine the receive DMA engine 
+    // should be enabled by setting the RxEnable bit in the Command register 
+    // before enabling the receive datapath in the MAC by setting the 
+    // RECEIVE ENABLE bit in the MAC1 register
+    //
+    HAL_READ_UINT32(EMAC_CMD, regval);
+    HAL_WRITE_UINT32(EMAC_CMD, regval | CMD_RX_ENABLE | CMD_TX_ENABLE);
+    HAL_READ_UINT32(EMAC_MAC1, regval);
+    HAL_WRITE_UINT32(EMAC_MAC1, regval | MAC1_RX_EN);
+}
+
+
+//===========================================================================
+// Enable and Disable of the receiver and transmitter.
+// Initialize the interface. This configures the interface ready for use.
+// Interrupts are grabbed etc. This means the start function has
+// little to do except enable the receiver
+//===========================================================================
+static bool lpc2xxx_eth_init(struct cyg_netdevtab_entry *tab)
+{
+    struct eth_drv_sc *sc    = (struct eth_drv_sc *)tab->device_instance;
+    lpc2xxx_eth_priv_t *priv = (lpc2xxx_eth_priv_t *)sc->driver_private;
+    bool esa_ok = false;
+    unsigned char enaddr[6] = { CYGPKG_DEVS_ETH_ARM_LPC2XXX_MACADDR};
+    unsigned short phy_state = 0;
+    cyg_uint32 regval;
+
+    debug1_printf("\nLPC2XXX_ETH: Initialising 0x%08X\n",priv->base);
+
+    //
+    // turn on the ethernet MAC clock in PCONP, bit 30 and then do
+    // a short delay
+    //
+    HAL_READ_UINT32(CYGARC_HAL_LPC24XX_REG_SCB_BASE +
+                    CYGARC_HAL_LPC24XX_REG_PCONP, regval);
+    HAL_WRITE_UINT32(CYGARC_HAL_LPC24XX_REG_SCB_BASE +
+                     CYGARC_HAL_LPC24XX_REG_PCONP,
+                     regval | CYGARC_HAL_LPC24XX_REG_PCONP_ENET);
+    HAL_DELAY_US(50);
+
+    //
+    // Because of a device errate in Rev '-' devices we need to check the
+    // module ID to setup the RMII mode properly
+    //
+    HAL_READ_UINT32(EMAC_MODULE_ID, regval);
+    if (EMAC_OLD_MODULE_ID == regval)
+    {
+        //
+        // On Rev. '-', EMAC_MODULE_ID should be equal to
+        // EMAC_OLD_MODULE_ID, P1.6 should be set -
+        // selects P1[0,1,4,6,8,9,10,14,15]
+        //
+        HAL_WRITE_UINT32(CYGARC_HAL_LPC24XX_REG_PIN_BASE +
+                        CYGARC_HAL_LPC24XX_REG_PINSEL2, 0x50151105);
+    }
+   else
+   {
+        //
+        // on Rev. 'A', EMAC_MODULE_ID should not equal to
+        // OLD_EMAC_MODULE_ID, P1.6 should not be set.
+        // selects P1[0,1,4,8,9,10,14,15]
+	    //
+        HAL_WRITE_UINT32(CYGARC_HAL_LPC24XX_REG_PIN_BASE +
+                         CYGARC_HAL_LPC24XX_REG_PINSEL2, 0x50150105);
+
+    } // if (EMAC_OLD_MODULE_ID == regval)
+
+    // selects P1[17:16]
+    HAL_WRITE_UINT32(CYGARC_HAL_LPC24XX_REG_PIN_BASE +
+                     CYGARC_HAL_LPC24XX_REG_PINSEL3, 0x00000005);
+    //
+    // reset MAC modules, tx, mcs_tx, rx, mcs_rx, simulation and soft reset,
+    // reset datapaths and host registers, disable Tx and RX and do a
+    // short delay
+    //
+    HAL_WRITE_UINT32(EMAC_MAC1, MAC1_RESET_TX
+    		                  | MAC1_RESET_MCS_TX
+    		                  | MAC1_RESET_RX
+    		                  | MAC1_RESET_MCS_RX
+    		                  | MAC1_RESET_SIM
+    		                  | MAC1_SOFT_RESET);
+    HAL_WRITE_UINT32(EMAC_CMD, CMD_REG_RESET
+    		                 | CMD_TX_RESET
+    		                 | CMD_RX_RESET);
+    HAL_DELAY_US(10);
+    HAL_WRITE_UINT32(EMAC_MAC1, 0x0);
+    lpc2xxx_eth_stop(sc); // disable transmitter and receiver
+    
+    HAL_WRITE_UINT32(EMAC_MAC2, 0); // initialize MAC2 register to default val.
+    HAL_WRITE_UINT32(EMAC_IPGR, 0x12);   // manual recommends value 0x12
+    HAL_WRITE_UINT32(EMAC_CLRT, 0x370F); // Using recommended value from manual.
+    HAL_WRITE_UINT32(EMAC_MAXF, 0x0600); // manual recommends value 0x600
+
+#ifdef CYGHWR_DEVS_ETH_ARM_LPC2XXX_GET_ESA
+    // Get MAC address from RedBoot configuration variables
+    CYGHWR_DEVS_ETH_ARM_LPC2XXX_GET_ESA(&enaddr[0], esa_ok);
+    // If this call fails myMacAddr is unchanged and MAC address from
+    // CDL is used
+#endif
+
+    if (!esa_ok)
+    {
+       // Can't figure out ESA
+       debug1_printf("LPC2XXX_ETH - Warning! ESA unknown\n");
+    }
+    debug1_printf("LPC2XXX_ETH: %02x:%02x:%02x:%02x:%02x:%02x\n",
+                  enaddr[0],enaddr[1],enaddr[2],
+                  enaddr[3],enaddr[4],enaddr[5]);
+
+    // Give the EMAC its address
+    lpc2xxx_set_mac(priv, enaddr);
+   
+    // Setup the PHY
+    CYG_ASSERTC(priv->phy);
+    if (!_eth_phy_init(priv->phy))
+    {
+       return (false);
+    }
+
+    // Get the current mode and print it
+    phy_state = _eth_phy_state(priv->phy);
+    if (phy_state & ETH_PHY_STAT_LINK)
+    {
+        cyg_uint32 cmd;
+        HAL_READ_UINT32(EMAC_CMD, cmd);
+        cmd |= CMD_PASS_RUNT_FRAME;
+    	HAL_READ_UINT32(EMAC_SUPP, regval);
+        if (phy_state & ETH_PHY_STAT_100MB)
+        {
+            HAL_WRITE_UINT32(EMAC_SUPP, regval | SUPP_SPEED_100MB);
+            debug1_printf("LPC2XXX_ETH: 100Mbyte/s");
+        }
+        else
+        {
+            HAL_WRITE_UINT32(EMAC_SUPP, regval &~ SUPP_SPEED_100MB);
+            debug1_printf("LPC2XXX_ETH: 10Mbyte/s");
+        }
+        if(phy_state & ETH_PHY_STAT_FDX)
+        {
+            HAL_WRITE_UINT32(EMAC_MAC2, MAC2_FDX | MAC2_CRC_EN 
+                             | MAC2_PAD_CRC_EN);
+            // manual recommends value of 0x15 for FDX
+            HAL_WRITE_UINT32(EMAC_IPGT, 0x15); 
+            cmd |= CMD_FULL_DUPLEX;
+            debug1_printf(" Full Duplex\n");
+        }
+        else
+        {
+            HAL_WRITE_UINT32(EMAC_MAC2, MAC2_CRC_EN | MAC2_PAD_CRC_EN);
+            // manual recommends value of 0x12 for HDX
+            HAL_WRITE_UINT32(EMAC_IPGT, 0x12); 
+            debug1_printf(" Half Duplex\n");
+        }
+        HAL_WRITE_UINT32(EMAC_CMD, cmd);
+    }
+    else
+    {
+       debug1_printf("LPC2XXX_ETH: No Link\n");
+    }
+    
+    lpc2xxx_eth_txdesc_init(priv);
+    lpc2xxx_eth_rxdesc_init(priv);
+    
+    // set up the Rx filter
+    // [0]-AllUnicast, [1]-AllBroadCast, [2]-AllMulticast, [3]-UnicastHash
+    // [4]-MulticastHash, [5]-Perfect, [12]-MagicPacketEnWoL, 
+    // [13]-RxFilterEnWoL
+    HAL_WRITE_UINT32(EMAC_RXFILT_CTRL, RXFILT_CTRL_ACC_BROADCAST
+    		                         | RXFILT_CTRL_ACC_PERFECT);
+    //
+    // If we are building an interrupt enabled version, install the
+    // interrupt handler
+#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
+    debug1_printf("LPC2XXX_ETH: Installing Interrupts on IRQ %d\n",
+                  priv->intr_vector);
+    cyg_drv_interrupt_create(priv->intr_vector,
+                             CYGPKG_DEVS_ETH_ARM_LPC2XXX_INTPRIO,
+                             (cyg_addrword_t)sc,
+                             lpc2xxx_eth_isr,
+                             eth_drv_dsr,
+                             &priv->intr_handle,
+                             &priv->intr);
+
+    cyg_drv_interrupt_attach(priv->intr_handle);
+    cyg_drv_interrupt_unmask(priv->intr_vector);
+#endif // CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
+
+    // Initialize the upper layer driver
+    _eth_drv_init(sc, enaddr);
+    
+    // clear all interrupts
+    HAL_WRITE_UINT32(EMAC_INT_CLR, 0xFFFF);
+    
+    //
+    // Enable the interrupts we are interested in (not SoftInt and WoL)
+    // We only want interrupts if a whole frame was transmitted so we 
+    // enable only the TXFINISHED interrupt and not the TXDONE interrupt
+    //
+    HAL_WRITE_UINT32(EMAC_INT_EN, EMAC_INT_RXOVERRUN
+    		                    | EMAC_INT_RXFINISHED
+    		                    | EMAC_INT_RXDONE
+    		                    | EMAC_INT_TXUNDERRUN
+    		                    | EMAC_INT_TXERROR
+    		                    | EMAC_INT_TXFINISHED);
+
+    return (true);
+}
+
+
+//===========================================================================
+// This function is called for low level "control" operations
+//===========================================================================
+static int
+lpc2xxx_eth_control(struct eth_drv_sc *sc, unsigned long key,
+                    void *data, int length)
+{
+    lpc2xxx_eth_priv_t *priv = (lpc2xxx_eth_priv_t *)sc->driver_private;
+    switch (key)
+    {
+        case ETH_DRV_SET_MAC_ADDRESS:
+        {
+            if(length >= ETHER_ADDR_LEN)
+            {
+               lpc2xxx_eth_stop(sc);
+
+               cyg_uint8 * enaddr = (cyg_uint8 *)data;
+               debug1_printf("LPC2XXX_ETH: %02x:%02x:%02x:%02x:%02x:%02x\n",
+                             enaddr[0],enaddr[1],enaddr[2],
+                             enaddr[3],enaddr[4],enaddr[5]);
+
+               lpc2xxx_set_mac(priv, enaddr);
+               lpc2xxx_eth_start(sc,enaddr,0);
+               return 0;
+            }
+            return 1;
+        }
+
+#ifdef CYGPKG_NET
+        case ETH_DRV_GET_IF_STATS:
+        {
+            // todo
+        }
+#endif
+
+        default:
+        {
+            diag_printf("%s.%d: key %lx\n", __FUNCTION__, __LINE__, key);
+            return (1);
+        }
+   } // switch (key)
+}
+
+
+//===========================================================================
+// 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 lpc2xxx_eth_can_send(struct eth_drv_sc *sc)
+{
+    int                 can_send;
+    lpc2xxx_eth_priv_t *priv = (lpc2xxx_eth_priv_t *)sc->driver_private;
+    if (priv->tx_busy)
+    {
+        can_send = 0;
+    }
+    else
+    {
+        can_send = 1;
+    }
+
+    debug2_printf("can_send: %d\n", can_send);
+    return (can_send);
+}
+
+//===========================================================================
+// This routine is called to send data to the hardware
+// For tx the current code supports a single outgoing transmission at a
+// time. This does limit outgoing bandwidth a bit, but only a bit, and
+// saves memory and complexity.
+//===========================================================================
+static void
+lpc2xxx_eth_send(struct        eth_drv_sc *sc,
+                 struct        eth_drv_sg *sg_list,
+                 int           sg_len,
+                 int           total_len,
+                 unsigned long key)
+{
+    cyg_uint32 tx_producer_idx;
+    cyg_uint32 tx_consumer_idx;
+    cyg_uint32 tx_desc_ctrl;
+    lpc2xxx_eth_priv_t *priv = (lpc2xxx_eth_priv_t *)sc->driver_private;
+    int i;
+
+    priv->tx_busy = true;
+    //
+    // Store addresses of entries in scatter/gather list into tx descriptors
+    //
+    HAL_READ_UINT32(EMAC_TX_PROD_IDX, tx_producer_idx);
+    HAL_READ_UINT32(EMAC_TX_CONSUME_IDX, tx_consumer_idx);
+    debug2_printf("TPI: %d TCI: %d\n", tx_producer_idx, tx_consumer_idx);
+    debug2_printf("TX: sg_len %d, total_len %d, key 0x%08lX\n",
+                  sg_len, total_len, key);
+    for(i = 0; i < sg_len; i++)
+    {
+        priv->ram->tx_descr[tx_producer_idx].packet = sg_list[i].buf;
+        tx_desc_ctrl = (sg_list[i].len - 1) | EMAC_TX_DESC_INT;
+        if (i == (sg_len - 1))
+        {
+            tx_desc_ctrl |= EMAC_TX_DESC_LAST;
+        }
+        priv->ram->tx_descr[tx_producer_idx].control = tx_desc_ctrl;
+        priv->ram->tx_status[tx_producer_idx] = 0;
+
+        tx_producer_idx = (tx_producer_idx + 1) %
+                           CYGNUM_DEVS_ETH_ARM_LPC2XXX_TX_BUFS;
+    }
+
+    debug2_printf("TPI: %d TCI: %d\n", tx_producer_idx, tx_consumer_idx);
+    // Store away the key for the time when the transmit has completed
+    // and we need to tell the stack which transmit has completed
+    // and then start the transmitter by incrementing the tx producer
+    // index
+    priv->cur_tx_key = key;
+    HAL_WRITE_UINT32(EMAC_TX_PROD_IDX, tx_producer_idx);
+}
+
+
+//===========================================================================
+// EMAC ISR
+//===========================================================================
+#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
+static cyg_uint32
+lpc2xxx_eth_isr (cyg_vector_t vector, cyg_addrword_t data)
+{
+    debug2_printf("ISR\n");
+    cyg_drv_interrupt_mask(vector);
+    cyg_interrupt_acknowledge(vector);
+    return CYG_ISR_CALL_DSR;
+}
+#endif // CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
+
+
+//===========================================================================
+// The high level DSR thaqt does all the work
+//===========================================================================
+static void lpc2xxx_eth_deliver(struct eth_drv_sc *sc)
+{
+    lpc2xxx_eth_priv_t *priv = (lpc2xxx_eth_priv_t *)sc->driver_private;
+    cyg_uint32          intstatus;
+
+    HAL_READ_UINT32(EMAC_INT_STATUS, intstatus);   
+#ifndef CYGPKG_REDBOOT
+    //
+    // In redbot the ethernet driver is polled and this debug message would
+    // pollute the redboot console
+    //
+    debug2_printf("INT_STATUS: %x\n", intstatus);
+#endif // CYGPKG_REDBOOT
+
+    if (intstatus & EMAC_INT_RXOVERRUN)
+    {
+        HAL_WRITE_UINT32(EMAC_INT_CLR, EMAC_INT_RXOVERRUN);
+        debug1_printf("LPC2XXX_ETH: RX overrun\n");
+    }
+
+    if (intstatus & EMAC_INT_TXUNDERRUN)
+    {
+        HAL_WRITE_UINT32(EMAC_INT_CLR, EMAC_INT_TXUNDERRUN);
+        debug1_printf("LPC2XXX_ETH: TX underrun\n");
+    }
+
+    if (intstatus & EMAC_INT_TXERROR)
+    {
+        cyg_uint32 tx_consumer_idx;
+        HAL_WRITE_UINT32(EMAC_INT_CLR, EMAC_INT_TXERROR);
+        HAL_READ_UINT32(EMAC_TX_CONSUME_IDX, tx_consumer_idx);
+        debug1_printf("LPC2XXX_ETH: TX error, status %08x\n",
+                      priv->ram->tx_status[tx_consumer_idx]);
+    }
+
+    if (intstatus & EMAC_INT_SOFTINT)
+    {
+        HAL_WRITE_UINT32(EMAC_INT_CLR, EMAC_INT_SOFTINT);
+    }
+
+    if (intstatus & EMAC_INT_WOL)
+    {
+        HAL_WRITE_UINT32(EMAC_INT_CLR, EMAC_INT_WOL);
+        debug1_printf("LPC2XXX_ETH: WOL\n");
+    }
+
+    //
+    // TxFinishedInt
+    // Interrupt triggered when all transmit descriptors have been
+    // processed i.e. on the transition to the situation where
+    // ProduceIndex == ConsumeIndex.
+    //
+    if (intstatus & EMAC_INT_TXFINISHED)
+    {
+        HAL_WRITE_UINT32(EMAC_INT_CLR, EMAC_INT_TXFINISHED);
+        _eth_drv_tx_done(sc, priv->cur_tx_key, 0);
+        debug2_printf("TX finished - key: 0x%08X\n", priv->cur_tx_key);
+        priv->tx_busy = false;
+    }
+
+    //
+    // RxDoneInt
+    // Interrupt triggered when a receive descriptor has been processed
+    // while the Interrupt bit in the Control field of the descriptor was set.
+    //
+    if (intstatus & (EMAC_INT_RXDONE | EMAC_INT_RXFINISHED))
+    {
+        cyg_uint32 rx_producer_idx;
+        cyg_uint32 rx_consumer_idx;
+
+        HAL_WRITE_UINT32(EMAC_INT_CLR, EMAC_INT_RXDONE);
+        HAL_WRITE_UINT32(EMAC_INT_CLR, EMAC_INT_RXFINISHED);
+        HAL_READ_UINT32(EMAC_RX_PROD_IDX, rx_producer_idx);
+        HAL_READ_UINT32(EMAC_RX_CONSUME_IDX, rx_consumer_idx);
+        debug2_printf("\n\nRPIdx %d RCIdx %d\n", rx_producer_idx, 
+                      rx_consumer_idx);
+
+        //
+        // Iterate through array of rx descriptors to check if the end of
+        // frame was received.
+        //
+        while (priv->iterator != rx_producer_idx)
+        {
+            priv->total_len += ((priv->ram->rx_status[priv->iterator].info
+                	             & DESC_SIZE_MASK) + 1);
+            debug2_printf("total_len %d rx_status[%d] = %x rx_len %d\n",
+               		      priv->total_len, priv->iterator,
+               		      priv->ram->rx_status[priv->iterator].info,
+               		      ((priv->ram->rx_status[priv->iterator].info
+                	      & DESC_SIZE_MASK) + 1));
+            if (priv->ram->rx_status[priv->iterator].info & RX_DESC_STATUS_LAST)
+            {
+                //
+                // We found the end of the frame and have something to receive
+                // so we can call _eth_drv_recv() now
+                //
+                if (priv->total_len)
+                {
+                    _eth_drv_recv(sc, priv->total_len);
+                    HAL_READ_UINT32(EMAC_RX_CONSUME_IDX, rx_consumer_idx);
+                    debug2_printf("RPIdx %d RCIdx %d Iterator %d\n",
+                    		      rx_producer_idx, rx_consumer_idx,
+                    		      priv->iterator);
+                    priv->total_len = 0;
+                } // if (total_len)
+                else
+                {
+                    //
+                    // If we do not call _eth_drv_recv then we need to set the
+                    // consumer index here to indicate that the buffers are
+                    // free for reception
+                    //
+                    HAL_WRITE_UINT32(EMAC_RX_CONSUME_IDX, priv->iterator);
+                } // if (priv->total_len)
+            } // if (priv->ram->rx_status[iterator].info & RX_DESC_STATUS_LAST)
+            priv->iterator = (priv->iterator + 1) % 
+                              CYGNUM_DEVS_ETH_ARM_LPC2XXX_RX_BUFS;
+        }//if (priv->ram->rx_status[priv->iterator].info & RX_DESC_STATUS_LAST)
+
+    } // if (intstatus & EMAC_INT_RXDONE)
+#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
+    cyg_drv_interrupt_unmask(priv->intr_vector);
+#endif // CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
+}
+
+
+//===========================================================================
+// This routine is called to receive data from the hardware
+//===========================================================================
+static void lpc2xxx_eth_recv(struct eth_drv_sc *sc,
+                             struct eth_drv_sg *sg_list,
+                             int    sg_len)
+{
+    lpc2xxx_eth_priv_t *priv = (lpc2xxx_eth_priv_t *)sc->driver_private;
+    int i;
+    cyg_uint32 rx_producer_idx;
+    cyg_uint32 rx_consumer_idx;
+    cyg_uint32 bytes_in_buffer;
+    cyg_uint32 bytes_in_list = 0;
+    cyg_uint32 bytes_needed_list = 0;
+    cyg_uint32 buffer_pos = 0;
+    cyg_uint32 total_bytes = 0;
+    cyg_uint8 *sg_buf;
+    cyg_uint8 *hw_rxbuf;
+
+    HAL_READ_UINT32(EMAC_RX_PROD_IDX, rx_producer_idx);
+    HAL_READ_UINT32(EMAC_RX_CONSUME_IDX, rx_consumer_idx);
+    debug2_printf("lpc2xxx_eth_recv: sg_len %d\n", sg_len);
+
+    for(i = 0; i < sg_len; ++i)
+    {
+        bytes_in_list = 0;
+        while (bytes_in_list < sg_list[i].len)
+        {
+            bytes_needed_list = sg_list[i].len - bytes_in_list;
+            debug2_printf("bil: %d sg_list[%d].len: %d bnl: %d\n",
+                          bytes_in_list,
+                          i,
+                          sg_list[i].len,
+                          bytes_needed_list); 
+            bytes_in_buffer = (priv->ram->rx_status[rx_consumer_idx].info &
+                               DESC_SIZE_MASK) + 1;
+            debug2_printf("bib: %d buffer_pos: %d\n", bytes_in_buffer, 
+                          buffer_pos);
+            bytes_in_buffer -= buffer_pos;
+            sg_buf   = (cyg_uint8 *)(sg_list[i].buf);
+            hw_rxbuf = (cyg_uint8 *)priv->ram->rx_buf[rx_consumer_idx].bytes;
+            if(bytes_needed_list < bytes_in_buffer)
+            {
+            	if(sg_buf)
+                {
+                    debug2_printf("(1) memcpy(0x%08X, 0x%08X, %d) "
+                                  "[0x%08X - 0x%08X]\n", 
+                                  (cyg_uint32)&sg_buf[bytes_in_list], 
+                                  (cyg_uint32)&hw_rxbuf[buffer_pos],
+                                   bytes_needed_list, 
+                                  (cyg_uint32)&sg_buf[bytes_in_list],
+                                  (cyg_uint32)&sg_buf[bytes_in_list] + 
+                                  bytes_needed_list);
+                                  
+                    memcpy(&sg_buf[bytes_in_list], 
+                           (cyg_uint8 *)&hw_rxbuf[buffer_pos],
+                           bytes_needed_list);
+                }
+                bytes_in_list += bytes_needed_list;
+                buffer_pos += bytes_needed_list;
+                total_bytes += bytes_needed_list;
+            }
+            else
+            {
+            	if(sg_buf)
+                {
+                    debug2_printf("(2) memcpy(0x%08X, 0x%08X, %d)" 
+                                  "[0x%08X - 0x%08X]\n", 
+                                  (cyg_uint32)&sg_buf[bytes_in_list], 
+                                  (cyg_uint32)&hw_rxbuf[buffer_pos],
+                                  bytes_in_buffer, 
+                                  (cyg_uint32)&sg_buf[bytes_in_list],
+                                  (cyg_uint32)&sg_buf[bytes_in_list] + 
+                                  bytes_in_buffer); 
+                                                     
+                    memcpy(&sg_buf[bytes_in_list], 
+                           (cyg_uint8 *)&hw_rxbuf[buffer_pos],
+                           bytes_in_buffer);
+                }
+                priv->ram->rx_status[rx_consumer_idx].info = 0;
+                priv->ram->rx_status[rx_consumer_idx].hash_crc = 0;
+                rx_consumer_idx = (rx_consumer_idx + 1) %
+                                  CYGNUM_DEVS_ETH_ARM_LPC2XXX_RX_BUFS;
+                HAL_WRITE_UINT32(EMAC_RX_CONSUME_IDX, rx_consumer_idx);
+                debug2_printf("RCIdx++ %d\n", rx_consumer_idx);
+                bytes_in_list += bytes_in_buffer;
+                total_bytes += bytes_in_buffer;
+                buffer_pos = 0;
+                if (bytes_in_list < sg_list[i].len)
+                {
+                    debug2_printf("bytes_in_list < sg_list[i].len");
+                }
+            }
+        } // while (bytes_in_list < sg_list[i].len)
+    } // for(i = 0; i < sg_len; ++i)
+}
+
+
+//===========================================================================
+// routine called to handle ethernet controller in polled mode
+//===========================================================================
+static void lpc2xxx_eth_poll(struct eth_drv_sc *sc)
+{
+    lpc2xxx_eth_deliver(sc);
+}
+
+
+//===========================================================================
+// routine called to handle ethernet controller in polled mode
+//===========================================================================
+static int lpc2xxx_eth_int_vector(struct eth_drv_sc *sc)
+{
+   return CYGNUM_HAL_INTERRUPT_ETH;
+}
+
+
+//===========================================================================
+// Ethernet driver instance
+//===========================================================================
+ETH_DRV_SC(lpc2xxx_sc,
+           &lpc2xxx_priv_data,       // Driver specific data
+           "eth0",                   // Name for this interface
+           lpc2xxx_eth_start,
+           lpc2xxx_eth_stop,
+           lpc2xxx_eth_control,
+           lpc2xxx_eth_can_send,
+           lpc2xxx_eth_send,
+           lpc2xxx_eth_recv,
+           lpc2xxx_eth_deliver,
+           lpc2xxx_eth_poll,
+           lpc2xxx_eth_int_vector);
+
+NETDEVTAB_ENTRY(lpc2xxx_netdev,
+                "lpc2xxx",
+                lpc2xxx_eth_init,
+                &lpc2xxx_sc);
+//---------------------------------------------------------------------------
+// EOF if_lpc2xxx.c