This is the mail archive of the ecos-patches@sourceware.org mailing list for the eCos project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

ColdFire ethernet driver


This patch contributes eCosCentric's ethernet device driver for the
Freescale MCFxxxx ColdFire family.

Bart

Index: ChangeLog
===================================================================
RCS file: ChangeLog
diff -N ChangeLog
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ChangeLog	20 Nov 2008 22:08:04 -0000
@@ -0,0 +1,186 @@
+2008-11-17  Bart Veer  <bartv@ecoscentric.com>
+
+	* cdl/mcf5272.cdl, doc/mcf5272_eth.sgml, src/if_mcf5272.c: minor
+	clean-ups.
+
+2008-09-22  Bart Veer  <bartv@ecoscentric.com>
+
+	* src/if_mcf5272.c (mcf52xx_eth_deliver): avoid cpp warning when
+	building for processors without a data cache.
+
+2008-09-02  Bart Veer  <bartv@ecoscentric.com>
+
+	* src/if_mcf5272.c, (mcf52xx_eth_init): add
+	support for run-time device dection.
+
+2008-06-02  Bart Veer  <bartv@ecoscentric.com>
+
+	* cdl/mcf5272_eth.cdl: add constraints so net tests run with
+	sensible performance.
+
+2008-03-18  Bart Veer  <bartv@ecoscentric.com>
+
+	* src/if_mcf5272.c: rearrange RX interrupt masking to avoid
+	spurious interrupt exceptions.
+
+2008-03-03  Bart Veer  <bartv@ecoscentric.com>
+
+	* src/if_mcf5272.c: encapsulate stats gathering in a macro.
+
+2008-02-28  Bart Veer  <bartv@ecoscentric.com>
+
+	* src/if_mcf5272.c: cleanup and more coping with unified and
+	copyback caches.
+
+	* cdl/mcf5272_eth.cdl: the ethernet driver must be built with at
+	least -O1 to ensure some data stays in registers when manipulating
+	the cache.
+
+2008-02-14  Bart Veer  <bartv@ecoscentric.com>
+
+	* src/if_mcf5272.c: cope with unified and copyback caches.
+
+2007-04-25  Jonathan Larmour  <jifl@eCosCentric.com>
+
+	* cdl/mcf5272_eth.cdl: memset/memcpy are part of infra and always
+	declared in isoinfra, so don't need to require CYGPKG_LIBC_STRING.
+	Require CYGPKG_ISOINFRA instead, although that's in every template
+	anyway.
+
+2006-09-25  Bart Veer  <bartv@ecoscentric.com>
+
+	* src/if_mcf5272.c: remove unnecessary #error
+
+	* cdl/mcf5272_eth.cdl, src/if_mcf5272, doc/mcf5272_eth.sgml:
+	Motorola -> Freescale.
+
+2006-09-08  Bart Veer  <bartv@ecoscentric.com>
+
+	* src/if_mcf5272.c: more/enhanced diagnostics. Update to use
+	current var_io.h definitions. Add partial support for multiple
+	devices.
+
+2006-03-10  John Dallaway  <jld@ecoscentric.com>
+
+	* cdl/mcf5272_eth.cdl: Add reference to package documentation.
+
+2004-10-07  Bart Veer  <bartv@ecoscentric.com>
+
+	* src/if_mcf5272.c (mcf52xx_eth_init): add optional platform init
+	function
+
+2004-06-25  Bart Veer  <bartv@ecoscentric.com>
+
+	* src/if_mcf5272.c (DEBUG): add some debug/tracing support
+
+2004-05-20  Bart Veer  <bartv@ecoscentric.com>
+
+	* src/if_mcf5272.c, cdl/mcf5272_eth.cdl: add a dependency
+	on CYGPKG_LIBC_STRING to get memcpy()
+
+2004-04-21  Bart Veer  <bartv@ecoscentric.com>
+
+	* src/if_mcf5272.c (mcf52xx_eth_init): use the right macro if the
+	platform has its own way of supplying a MAC address.
+
+2004-03-25  Bart Veer  <bartv@ecoscentric.com>
+
+	* src/if_mcf5272.c (mcf52xx_eth_deliver): make the rx ring scan
+	more robust. The mcf5282 appears to occasionally place incoming
+	frames in unexpected slots.
+
+2004-03-17  Bart Veer  <bartv@ecoscentric.com>
+
+	* doc/mcf5272_eth.sgml: update documentation following port to an
+	mcf5282
+
+2004-03-10  Bart Veer  <bartv@ecoscentric.com>
+
+	* src/if_mcf5272.c (mcf52xx_eth_poll): update tx poll code to
+	match previous changes. Ignore the MAC address passed to _start(),
+	it is not needed. During _init(), if running on top of RedBoot,
+	check for an outgoing transmit before resetting and wait a bit if
+	necessary.
+
+2004-03-08  Bart Veer  <bartv@ecoscentric.com>
+
+	* src/if_mcf5272.c: cope with processor variants where tx buffers
+	need to be more aligned than is guaranteed by higher-level code.
+	Provide a MAC address even if virtual vectors are not available,
+	e.g. when debugging via BDM. Reorganize the init code as per the
+	recommendations in the mcf5282 manual. Add statistics counting for
+	the mcf5282. Disable RX interrupts between the ISR and the
+	received packet(s) being processed, since those just add to the
+	load. Use separate ISR priorities for RX and TX.
+
+	* cdl/mcf5272_eth.cdl: At least two rx buffers are needed to cope
+	with oversized incoming frames. ISR priorities now have to be
+	configured separately for RX and TX.
+
+2004-02-11  Bart Veer  <bartv@ecoscentric.com>
+
+	* src/if_mcf5272.c: The driver now works on coldfire processors
+	other than just the 5272. Use new per-processor initialization
+	macro to set up GPIO pins. Also cope with incompatible renaming
+	of flash configury within hal/common.
+
+	* cdl/mcf5272_eth.cdl: update descriptions to reflect that the
+	driver now works on other coldfire processors as well.
+
+2003-07-22  Bart Veer  <bartv@ecoscentric.com>
+
+	* doc/mcf5272_eth.sgml: fix various typos etc
+
+	* src/if_mcf5272.c (mcf5272_eth_deliver): eliminate warning
+
+	* cdl/mcf5272_eth.cdl: RedBoot now needs more than one buffer.
+
+2003-07-18  Bart Veer  <bartv@ecoscentric.com>
+
+	* doc/mcf5272_eth.sgml: add documentation
+
+2003-07-13  Bart Veer  <bartv@ecoscentric.com>
+
+	* cdl/mcf5272_eth.cdl:
+	Increase default number of receive buffers.
+
+	* src/if_mcf5272.c:
+	Implement SNMP statistics
+	Improve handling of overrun conditions
+	Preserve order of incoming packets
+
+2003-06-04  Bart Veer  <bartv@ecoscentric.com>
+
+	* New version of the M68K support
+
+//===========================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 2003, 2004, 2007, 2008 eCosCentric Limited
+//
+// 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####
+//===========================================================================
Index: cdl/mcf5272_eth.cdl
===================================================================
RCS file: cdl/mcf5272_eth.cdl
diff -N cdl/mcf5272_eth.cdl
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ cdl/mcf5272_eth.cdl	20 Nov 2008 22:08:04 -0000
@@ -0,0 +1,192 @@
+# ====================================================================
+#
+#	mcfxxxx_eth.cdl
+#
+#	Ethernet driver for Freescale MCFxxxx ColdFires
+#
+# ====================================================================
+#####ECOSGPLCOPYRIGHTBEGIN####
+# -------------------------------------------
+# This file is part of eCos, the Embedded Configurable Operating System.
+# Copyright (C) 2003,2004,2006,2007,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):      bartv
+# Date:           2003-06-04
+#
+#####DESCRIPTIONEND####
+# ====================================================================
+
+cdl_package CYGPKG_DEVS_ETH_MCFxxxx {
+    display	    "MCFxxxx ethernet driver"
+    doc         ref/devs-eth-m68k-mcfxxxx-part.html
+    parent	    CYGPKG_IO_ETH_DRIVERS
+    active_if	CYGPKG_IO_ETH_DRIVERS
+    active_if	CYGPKG_HAL_M68K_MCFxxxx
+    include_dir cyg/io/eth
+    
+    implements	CYGHWR_NET_DRIVERS
+    implements	CYGHWR_NET_DRIVER_ETH0
+    implements	CYGINT_IO_ETH_MULTICAST
+    requires    CYGPKG_INFRA CYGPKG_HAL CYGPKG_ISOINFRA
+    compile     -library=libextras.a if_mcf5272.c
+    
+    description "
+        This driver supports the on-chip ethernet provided by some
+        Freescale MCFxxxx coldfire processors."
+    
+
+    cdl_option CYGDAT_DEVS_ETH_MCFxxxx_NAME {
+        display		    "Device name for this ethernet device"
+        flavor		    data
+        default_value	{"\"eth0\""}
+        description "
+            This option controls the name of the on-chip ethernet device.
+            By default the on-chip device is labelled eth0, and any
+            additional devices can be named eth1 onwards."
+    }
+
+    cdl_option CYGNUM_DEVS_ETH_MCFxxxx_RXBUFFERS {
+        display		    "Number of receive buffers"
+        flavor		    data
+        default_value	4
+        legal_values	2 to 16
+        description "
+            This ethernet driver statically allocates a number of buffers
+            for incoming packets. Usually each buffer requires 1528 bytes.
+            Increasing the number of buffers reduces the probability of packets getting
+            lost under heavy processor or network load. Reducing the number
+            of buffers makes lost packets more likely, potentially causing
+            significant TCP/IP delays."
+    }
+
+    cdl_option CYGNUM_DEVS_ETH_MCFxxxx_ISR_RX_PRIORITY {
+        display		"Interrupt priority for incoming packets"
+        flavor		data
+        default_value	{ is_loaded(CYGNUM_HAL_M68K_MCFxxxx_ISR_DEFAULT_PRIORITY_ETH_RX) ?
+            CYGNUM_HAL_M68K_MCFxxxx_ISR_DEFAULT_PRIORITY_ETH_RX :
+            CYGNUM_HAL_M68K_MCFxxxx_ISR_PRIORITY_MIN }
+        legal_values	CYGNUM_HAL_M68K_MCFxxxx_ISR_PRIORITY_MIN to CYGNUM_HAL_M68K_MCFxxxx_ISR_PRIORITY_MAX
+        description "
+            By default the ethernet is given an interrupt priority of 1,
+            in other words it will interrupt at IPL level 1. The device can
+            be made to interrupt at a higher priority but this is rarely
+            useful since nearly all processing happens at thread level
+            or DSR level rather than ISR level."
+    }
+
+    cdl_option CYGNUM_DEVS_ETH_MCFxxxx_ISR_TX_PRIORITY {
+        display		"Interrupt priority for packet transmits"
+        flavor		data
+        default_value	{ is_loaded(CYGNUM_HAL_M68K_MCFxxxx_ISR_DEFAULT_PRIORITY_ETH_TX) ?
+            CYGNUM_HAL_M68K_MCFxxxx_ISR_DEFAULT_PRIORITY_ETH_TX :
+            CYGNUM_HAL_M68K_MCFxxxx_ISR_PRIORITY_MIN }
+        legal_values	CYGNUM_HAL_M68K_MCFxxxx_ISR_PRIORITY_MIN to CYGNUM_HAL_M68K_MCFxxxx_ISR_PRIORITY_MAX
+        description "
+            By default the ethernet is given an interrupt priority of 1,
+            in other words it will interrupt at IPL level 1. The device can
+            be made to interrupt at a higher priority but this is rarely
+            useful since nearly all processing happens at thread level
+            or DSR level rather than ISR level."
+    }
+
+    cdl_interface CYGINT_DEVS_ETH_MCFxxxx_PLATFORM_MAC {
+        display		"Platform provides network MAC address"
+        flavor		bool
+    }
+
+    cdl_option CYGDAT_DEVS_ETH_MCFxxxx_PLATFORM_MAC {
+        display		"Default MAC address"
+        active_if	!CYGINT_DEVS_ETH_MCFxxxx_PLATFORM_MAC
+        flavor		data
+        default_value	{ "0x00, 0xFF, 0x12, 0x34, 0x56, 0x78" }
+        description "
+            If the target board does not provide a fixed unique ethernet
+            MAC address assigned by the IEEE then it is necessary to invent one.
+            If RedBoot is in use and provides configuration data held in
+            flash then that will be used. If RedBoot is not in use or if the
+            fconfig flash block does not contain an entry for the MAC address
+            then the code will use a default value, determined by this option.
+            If the local network has multiple boards attached then it is
+            essential that each one has a unique MAC address."
+    }
+
+    cdl_option CYGFUN_DEVS_ETH_MCFxxxx_STATISTICS {
+        display		"Maintain traffic statistics"
+        flavor		bool
+        default_value	CYGPKG_SNMPAGENT
+        description "
+            The MCFxxxx ethernet driver can maintain some statistics
+            about traffic, for example the number of incoming and
+	        outgoing packets. These statistics are intended mainly
+            for SNMP agent software."
+    }
+    
+    cdl_component CYGPKG_DEVS_ETH_MCFxxxx_OPTIONS {
+        display     "Build options"
+        flavor      none
+        description "
+	        Package-specific build options including control over compiler
+            flags used only in building this package."
+
+        cdl_option CYGPKG_DEVS_ETH_MCFxxxx_CFLAGS_ADD {
+            display "Additional compiler flags"
+            flavor  data
+            no_define
+            requires {
+                is_substr(CYGBLD_GLOBAL_CFLAGS, "-O1") || is_substr(CYGBLD_GLOBAL_CFLAGS, "-O2") ||
+                is_substr(CYGBLD_GLOBAL_CFLAGS, "-O3") || is_substr(CYGBLD_GLOBAL_CFLAGS, "-Os") ||
+                is_substr(CYGPKG_DEVS_ETH_MCFxxxx_CFLAGS_ADD, "-O1") ||
+                is_substr(CYGPKG_DEVS_ETH_MCFxxxx_CFLAGS_ADD, "-O2") ||
+                is_substr(CYGPKG_DEVS_ETH_MCFxxxx_CFLAGS_ADD, "-O3") ||
+                is_substr(CYGPKG_DEVS_ETH_MCFxxxx_CFLAGS_ADD, "-Os")
+            }
+            default_value { ((is_substr(CYGBLD_GLOBAL_CFLAGS, "-O1") || is_substr(CYGBLD_GLOBAL_CFLAGS, "-O2") ||
+                              is_substr(CYGBLD_GLOBAL_CFLAGS, "-O3") || is_substr(CYGBLD_GLOBAL_CFLAGS, "-Os")) ? "" : "-O2 ") .
+                             "-D_KERNEL -D__ECOS" }
+            description   "
+                This option modifies the set of compiler flags for
+                building this package. These flags are used in addition
+                to the set of global flags. Note that the package cannot
+                be built with -O0: some of the cache-related code depends
+                on certain values being held in registers instead of
+                memory."
+        }
+        cdl_option CYGPKG_DEVS_ETH_MCFxxxx_CFLAGS_REMOVE {
+            display "Suppressed compiler flags"
+            flavor  data
+            no_define
+            default_value { "" }
+            description   "
+                This option modifies the set of compiler flags for
+                building this package. These flags are removed from
+                the set of global flags if present."
+        }
+    }
+}
Index: doc/mcf5272_eth.sgml
===================================================================
RCS file: doc/mcf5272_eth.sgml
diff -N doc/mcf5272_eth.sgml
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ doc/mcf5272_eth.sgml	20 Nov 2008 22:08:05 -0000
@@ -0,0 +1,207 @@
+<!-- DOCTYPE refentry  PUBLIC "-//OASIS//DTD DocBook V3.1//EN" -->
+
+<!-- {{{ Banner                         -->
+
+<!-- =============================================================== -->
+<!--                                                                 -->
+<!--     mcfxxxxeth.sgml                                             -->
+<!--                                                                 -->
+<!--     mcfxxxx ethernet driver documentation.                      -->
+<!--                                                                 -->
+<!-- =============================================================== -->
+<!-- ####COPYRIGHTBEGIN####                                          -->
+<!--                                                                 -->
+<!-- Copyright (C) 2003,2004,2006,2008                               -->
+<!--   Free Software Foundation, Inc.                                -->
+<!-- This material may be distributed only subject to the terms      -->
+<!-- and conditions set forth in the Open Publication License, v1.0  -->
+<!-- or later (the latest version is presently available at          -->
+<!-- http://www.opencontent.org/openpub/)                            -->
+<!-- Distribution of the work or derivative of the work in any       -->
+<!-- standard (paper) book form is prohibited unless prior           -->
+<!-- permission obtained from the copyright holder                   -->
+<!--                                                                 -->      
+<!-- ####COPYRIGHTEND####                                            -->
+<!-- =============================================================== -->
+<!-- #####DESCRIPTIONBEGIN####                                       -->
+<!--                                                                 -->
+<!-- Author(s):   bartv                                              -->
+<!-- Contact(s):  bartv                                              -->
+<!-- Date:        2003/07/15                                         -->
+<!-- Version:     0.01                                               -->
+<!--                                                                 -->
+<!-- ####DESCRIPTIONEND####                                          -->
+<!-- =============================================================== -->
+
+<!-- }}} -->
+
+<part id="devs-eth-m68k-mcfxxxx-part"><title>Freescale ColdFire Ethernet Driver</title>
+
+<refentry id="devs-eth-m68k-mcfxxxx">
+  <refmeta>
+    <refentrytitle>Freescale ColdFire Ethernet Driver</refentrytitle>
+  </refmeta>
+  <refnamediv>
+    <refname><varname>CYGPKG_DEVS_ETH_MCFxxxx</varname></refname>
+    <refpurpose>eCos Support for Freescale ColdFire On-chip Ethernet Devices</refpurpose>
+  </refnamediv>
+
+  <refsect1 id="devs-eth-m68k-mcfxxxx-description"><title>Description</title>
+    <para>
+Some members of the Freescale ColdFire family of processors come with
+an on-chip ethernet device. This package provides an eCos driver for
+that device. The driver supports both polled mode for use by RedBoot
+and interrupt-driven mode for use by a full TCP/IP stack.
+    </para>
+    <para>
+The original version of the driver was written specifically for the
+MCF5272 processor. It has since been made to work on other members of
+the ColdFire family.
+    </para>
+  </refsect1>
+
+  <refsect1 id="devs-eth-m68k-mcfxxxx-config"><title>Configuration Options</title>
+    <para>
+This ethernet package should be loaded automatically when
+selecting a target containing a ColdFire processor with on-chip
+ethernet, and it should never be necessary to load it explicitly. If
+the application does not actually require ethernet functionality then
+the package is inactive and the final executable will not suffer any
+overheads from unused functionality. This is determined by the
+presence of the generic ethernet I/O package
+<varname>CYGPKG_IO_ETH_DRIVERS</varname>. Typically the choice of eCos
+template causes the right thing to happen. For example the default
+template does not include any TCP/IP stack so
+<varname>CYGPKG_IO_ETH_DRIVERS</varname> is not included, but the
+net, redboot and lwip_eth templates do include a TCP/IP stack so will
+specify that package and hence enable the ethernet driver.
+    </para>
+    <para>
+All eCos network devices need a unique name. By default the on-chip
+ethernet device is assigned the name <literal>eth0</literal> but
+can be changed through the configuration option
+<varname>CYGDAT_DEVS_ETH_MCFxxxx_NAME</varname>. This is useful if for
+example the target hardware includes a number of additional off-chip
+ethernet devices.
+    </para>
+    <para>
+The hardware requires that incoming ethernet frames are received
+into one of a small number of buffers, arranged in a ring. Once a
+frame has been received and its size is known the driver will pass it
+up to higher-level code for further processing. The number of these
+buffers is configurable via the option
+<varname>CYGNUM_DEVS_ETH_MCFxxxx_RXBUFFERS</varname>. Each receive
+buffer requires 1528 bytes of memory. A smaller number of buffers
+increases the probability that incoming ethernet frames have to be
+discarded. TCP/IP stacks are designed to cope with the occasional lost
+packet, but if too many frames are discarded then this will greatly
+affect performance. A key issue here is that passing the incoming
+frames up to higher-level code typically happens at thread level and
+hence the system behaviour is defined in large part by the priority of
+various threads running in the TCP/IP stack. If application code has
+high-priority threads that take up much of the available cpu time and
+the TCP/IP stack gets little chance to run then there will be little
+opportunity to pass received frames up the stack. Balancing out the
+various thread priorities and the number of receive buffers is the
+responsibility of the application developer.
+    </para>
+    <para>
+By default the ethernet driver will raise interrupts using a low
+priority level. The exact value will depend on the processor being
+used, for example the MCF5282 interrupt controllers impose specific
+constraints on interrupt priorities. The driver does very little at
+interrupt level, instead the real work is done via threads inside the
+TCP/IP stack. Hence the interrupt priority has little or no effect on
+the system's behaviour. If the default priorities are inappropriate for
+some reason then they can be changed through the configuration options
+<varname>CYGNUM_DEVS_ETH_MCFxxxx_ISR_RX_PRIORITY</varname> and
+<varname>CYGNUM_DEVS_ETH_MCFxxxx_ISR_TX_PRIORITY</varname>.
+    </para>
+    <para>
+There is an option related to the default network MAC address,
+<varname>CYGDAT_DEVS_ETH_MCFxxxx_PLATFORM_MAC</varname>. This is
+discussed in more detail <link
+linkend="devs-eth-m68k-mcfxxxx-mac">below</link>.
+    </para>
+    <para>
+Optionally the ethernet driver can maintain statistics about the
+number of incoming and transmitted ethernet frames, receive overruns,
+collisions, and other conditions. Maintaining and providing these
+statistics involves some overhead, and is controlled by the
+configuration option
+<varname>CYGFUN_DEVS_ETH_MCFxxxx_STATISTICS</varname>. Typically these
+statistics are only accessed through SNMP, so by default statistics
+gathering is enabled if the configuration includes
+<varname>CYGPKG_SNMPAGENT</varname> and disabled otherwise.
+    </para>
+  </refsect1>
+
+  <refsect1 id="devs-eth-m68k-mcfxxxx-mac"><title>MAC Address</title>
+    <para>
+The ColdFire processors do not have a built-in unique network MAC
+address since that would require slightly different manufacturing for
+each chip. All ethernet devices should have a unique address so this
+has to come from elsewhere. There are a number of possibilities:
+    </para>
+    <orderedlist>
+      <listitem><para>
+The platform HAL can provide the address. For example the target board
+may have a small serial EPROM or similar which is initialized during
+board manufacture. The platform HAL can read the serial EPROM during
+system startup and provide the information to the ethernet driver. If
+this is the case then the platform HAL should implement the CDL
+interface <varname>CYGINT_DEVS_ETH_MCFxxxx_PLATFORM_MAC</varname> and
+provide a macro <function>HAL_MCFxxxx_ETH_GET_MAC_ADDRESS</function>
+in the exported header <filename
+class="headerfile">cyg/hal/plf_arch.h</filename>. 
+      </para></listitem>
+      <listitem><para>
+There is a configuration option
+<varname>CYGDAT_DEVS_ETH_MCFxxxx_PLATFORM_MAC</varname> which
+specifies the default MAC address. Manipulating this option is fine if
+the configuration will only be used on a single board. However if
+multiple boards run applications with the same configuration then they
+would all have the same MAC address, and the resulting behaviour is
+undefined.
+      </para></listitem>
+      <listitem><para>
+If the target hardware boots via RedBoot and uses a block of flash to
+hold configuration variables then one of these variables will be the
+MAC address. It can be manipulated at the RedBoot prompt using the
+<command>fconfig</command> command, thus giving each board a unique
+address. An eCos application containing the ethernet driver will
+automatically pick up this address.
+      </para></listitem>
+    </orderedlist>
+    <para>
+When designing a new target board it is recommended that the board
+comes with a unique network address supported by the platform HAL,
+rather than relying on users to change the address. The latter
+approach can be error-prone and will lead to failures that are
+difficult to track down.
+    </para>
+  </refsect1>
+
+  <refsect1 id="devs-eth-m68k-mcfxxxx-phy"><title>Platform-specific PHY</title>
+    <para>
+The on-chip ethernet hardware relies on an external media independent
+interface (MII), also known as a PHY chip. This separate chip handles
+the low-level details of ethernet communication, for example
+negotiating a link speed with the hub. In most scenarios the PHY chip
+simply does the right thing and needs no support from the ethernet
+driver. If there are special requirements, for example if the board
+has to be hardwired to communicate at 10Mbps rather than autonegotiate
+the link speed, then usually this is handled by fixed logic levels on
+some of the PHY pins or by using jumpers. 
+    </para>
+    <para>
+The eCos ethernet driver assumes that the PHY is already fully
+operational and does not interact with it in any way. If the target
+hardware does require software initialization of the PHY chip then
+usually this will be done in the platform HAL, because the choice of
+PHY chip is a characteristic of the platform.
+    </para>
+  </refsect1>
+
+</refentry>
+</part>
Index: src/if_mcf5272.c
===================================================================
RCS file: src/if_mcf5272.c
diff -N src/if_mcf5272.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/if_mcf5272.c	20 Nov 2008 22:08:07 -0000
@@ -0,0 +1,1351 @@
+//==========================================================================
+//
+//      if_mcfxxxx.c
+//
+//	    Ethernet driver for Freescale MCFxxxx coldfires
+//
+//==========================================================================
+//###ECOSGPLCOPYRIGHTBEGIN####
+//-------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 2003,2004,2006,2007,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):    bartv
+// Contributors:
+// Date:         2003-06-04
+// Description:  hardware driver for MCFxxxx ethernet devices.
+//
+//####DESCRIPTIONEND####
+//==========================================================================
+
+#include <pkgconf/system.h>
+#include CYGBLD_HAL_PLATFORM_H
+#include <pkgconf/devs_eth_mcfxxxx.h>
+#include <pkgconf/io_eth_drivers.h>
+
+#include <cyg/infra/cyg_type.h>
+#include <cyg/infra/cyg_ass.h>
+#include <cyg/hal/hal_arch.h>
+#include <cyg/hal/hal_io.h>
+#include <cyg/hal/hal_intr.h>
+#include <cyg/hal/hal_cache.h>
+#include <cyg/hal/hal_if.h>
+#include <cyg/infra/diag.h>
+#include <cyg/hal/drv_api.h>
+#include <cyg/io/eth/netdev.h>
+#include <cyg/io/eth/eth_drv.h>
+#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>
+
+// ----------------------------------------------------------------------------
+// Support for multiple devices - still incomplete. 
+# define EIR(_eth_)             (HAL_MCFxxxx_ETH0_BASE + HAL_MCFxxxx_ETHx_EIR)
+# define EIMR(_eth_)            (HAL_MCFxxxx_ETH0_BASE + HAL_MCFxxxx_ETHx_EIMR)
+# define RDAR(_eth_)            (HAL_MCFxxxx_ETH0_BASE + HAL_MCFxxxx_ETHx_RDAR)
+# define TDAR(_eth_)            (HAL_MCFxxxx_ETH0_BASE + HAL_MCFxxxx_ETHx_TDAR)
+# define ECR(_eth_)             (HAL_MCFxxxx_ETH0_BASE + HAL_MCFxxxx_ETHx_ECR)
+# define MMFR(_eth_)            (HAL_MCFxxxx_ETH0_BASE + HAL_MCFxxxx_ETHx_MMFR)
+# define MSCR(_eth_)            (HAL_MCFxxxx_ETH0_BASE + HAL_MCFxxxx_ETHx_MSCR)
+# define MIBC(_eth_)            (HAL_MCFxxxx_ETH0_BASE + HAL_MCFxxxx_ETHx_MIBC)
+# define RCR(_eth_)             (HAL_MCFxxxx_ETH0_BASE + HAL_MCFxxxx_ETHx_RCR)
+# define TCR(_eth_)             (HAL_MCFxxxx_ETH0_BASE + HAL_MCFxxxx_ETHx_TCR)
+# define PALR(_eth_)            (HAL_MCFxxxx_ETH0_BASE + HAL_MCFxxxx_ETHx_PALR)
+# define PAUR(_eth_)            (HAL_MCFxxxx_ETH0_BASE + HAL_MCFxxxx_ETHx_PAUR)
+# define OPD(_eth_)             (HAL_MCFxxxx_ETH0_BASE + HAL_MCFxxxx_ETHx_OPD)
+# define IAUR(_eth_)            (HAL_MCFxxxx_ETH0_BASE + HAL_MCFxxxx_ETHx_IAUR)
+# define IALR(_eth_)            (HAL_MCFxxxx_ETH0_BASE + HAL_MCFxxxx_ETHx_IALR)
+# define GAUR(_eth_)            (HAL_MCFxxxx_ETH0_BASE + HAL_MCFxxxx_ETHx_GAUR)
+# define GALR(_eth_)            (HAL_MCFxxxx_ETH0_BASE + HAL_MCFxxxx_ETHx_GALR)
+# define TFWR(_eth_)            (HAL_MCFxxxx_ETH0_BASE + HAL_MCFxxxx_ETHx_TFWR)
+# define FRBR(_eth_)            (HAL_MCFxxxx_ETH0_BASE + HAL_MCFxxxx_ETHx_FRBR)
+# define FRSR(_eth_)            (HAL_MCFxxxx_ETH0_BASE + HAL_MCFxxxx_ETHx_FRSR)
+# define ERDSR(_eth_)           (HAL_MCFxxxx_ETH0_BASE + HAL_MCFxxxx_ETHx_ERDSR)
+# define ETDSR(_eth_)           (HAL_MCFxxxx_ETH0_BASE + HAL_MCFxxxx_ETHx_ETDSR)
+# define EMRBR(_eth_)           (HAL_MCFxxxx_ETH0_BASE + HAL_MCFxxxx_ETHx_EMRBR)
+
+// ----------------------------------------------------------------------------
+// Debug support, in the form of diagnostics and a simple trace
+// buffer. Also make it easy to enable stats without messing about
+// with the configuration.
+
+#define DIAG_LEVEL     0
+#define DIAG_BUFFERED  0
+#define DIAG_WRAP      1
+#ifndef CYGFUN_DEVS_ETH_MCFxxxx_STATISTICS
+//# define CYGFUN_DEVS_ETH_MCFxxxx_STATISTICS 1
+#endif
+
+#if (DIAG_LEVEL <= 0)
+
+# define DIAG(_level_, _fmt_, ...)                  \
+    CYG_MACRO_START                                 \
+    CYG_MACRO_END
+
+# define DIAGPKT(_level_, _msg_, _pkt_, _len_)      \
+    CYG_MACRO_START                                 \
+    CYG_MACRO_END
+
+#elif (! DIAG_BUFFERED)
+
+#  define DIAG(_level_, _fmt_, ...)                                             \
+    CYG_MACRO_START                                                             \
+    if ((_level_) <= DIAG_LEVEL) {                                              \
+        diag_printf("mcfxxxx_eth %s : " _fmt_ "\n", __func__, ## __VA_ARGS__);  \
+    }                                                                           \
+    CYG_MACRO_END
+
+# define DIAGPKT(_level_, _msg_, _pkt_, _len_)                                  \
+    CYG_MACRO_START                                                             \
+    if ((_level_) <= DIAG_LEVEL) {                                              \
+        diag_printf("mcfxxxx_eth %s : pkt %p, len %d\n", __func__,              \
+                    (cyg_uint8*)_pkt_, _len_);                                  \
+    }                                                                           \
+    CYG_MACRO_END
+
+#else
+// Trace buffer size. This has to be kept smallish or RedBoot will overrun
+// its RAM allocation. With 24 bytes per entry, 300 entries require a bit
+// under 8K.
+# define DIAG_BUFFER_SIZE      300
+typedef struct mcfxxxx_eth_trace {
+    const char*     fn;
+    const char*     msg;
+    cyg_uint32      len;
+    cyg_uint8       packet[14];
+} mcfxxxx_eth_trace_entry;
+static mcfxxxx_eth_trace_entry  mcfxxxx_eth_trace_data[DIAG_BUFFER_SIZE];
+static int                      mcfxxxx_eth_trace_next      = DIAG_BUFFER_SIZE - 1;
+static cyg_bool                 mcfxxxx_eth_trace_wrapped   = false;
+
+static void
+mcfxxxx_eth_trace(const char* fn, const char* msg, cyg_uint8* packet, cyg_uint32 len)
+{
+    mcfxxxx_eth_trace_entry* entry = &(mcfxxxx_eth_trace_data[mcfxxxx_eth_trace_next]);
+
+# ifdef DIAG_WRAP
+    if (0 == mcfxxxx_eth_trace_next) {
+        mcfxxxx_eth_trace_next = DIAG_BUFFER_SIZE - 1;
+        mcfxxxx_eth_trace_wrapped = true;
+    } else {
+        mcfxxxx_eth_trace_next -= 1;
+    }
+# else
+    if (mcfxxxx_eth_trace_next < 0) {
+        return;
+    }
+    mcfxxxx_eth_trace_next -= 1;
+# endif
+
+    entry->fn   = fn;
+    entry->msg  = msg;
+    entry->len  = len;
+    if ((cyg_uint8*)0 == packet) {
+        memset(entry->packet, 0, 14);
+    } else {
+        memcpy(entry->packet, packet, 14);
+    }
+}
+
+#  define DIAG(_level_, _fmt_, ...)                                         \
+    CYG_MACRO_START                                                         \
+    if ((_level_) <= DIAG_LEVEL) {                                          \
+        mcfxxxx_eth_trace(__func__, _fmt_, (cyg_uint8*)0, 0);               \
+    }                                                                       \
+    CYG_MACRO_END
+
+# define DIAGPKT(_level_, _msg_, _pkt_, _len_)                              \
+    CYG_MACRO_START                                                         \
+    if ((_level_) <= DIAG_LEVEL) {                                          \
+        mcfxxxx_eth_trace(__func__, _msg_, (cyg_uint8*)_pkt_, _len_);       \
+    }                                                                       \
+    CYG_MACRO_END
+
+#endif
+
+#if (DIAG_LEVEL < 3)
+# define WRITE32(_addr_, _val_)         HAL_WRITE_UINT32(_addr_, _val_)
+# define READ32( _addr_, _var_)         HAL_READ_UINT32(_addr_, _var_)
+#else
+
+# define WRITE32(_addr_, _val_)                                             \
+    CYG_MACRO_START                                                         \
+    DIAG(DIAG_LEVEL, "WRITE %s %08x <= 0x%08x", # _addr_, _addr_, _val_) ;  \
+    HAL_WRITE_UINT32(_addr_, _val_);                                        \
+    CYG_MACRO_END
+
+#define READ32(_addr_, _var_)                                               \
+    CYG_MACRO_START                                                         \
+    HAL_READ_UINT32(_addr_, _var_);                                         \
+    DIAG(DIAG_LEVEL, "READ  %s %08x == 0x%08x", # _addr_, _addr_, _var_) ;  \
+    CYG_MACRO_END
+
+#endif
+
+#if 0
+// A function that gets placed in RAM. This can be called by flash-resident
+// code, e.g. RedBoot, when I need a breakpoint on a specific condition.
+static void mcfxxxx_eth_ramfn(void) __attribute__((section (".2ram.mcfxxxx_eth_ramfn")));
+static int  mcfxxxx_eth_ramfn_calls;
+static void
+mcfxxxx_eth_ramfn(void)
+{
+    mcfxxxx_eth_ramfn_calls += 1;
+}
+#endif
+
+// ----------------------------------------------------------------------------
+#ifdef CYGFUN_DEVS_ETH_MCFxxxx_STATISTICS
+# define INCR_STAT(_a_)                 \
+    CYG_MACRO_START                     \
+    _a_ += 1;                           \
+    CYG_MACRO_END
+#else
+# define INCR_STAT(_a_)
+#endif
+
+// ----------------------------------------------------------------------------
+// Most existing MCFxxxx chips have 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.
+//
+// The driver currently supports n rx buffers, configurable, each of
+// maximum size. Incoming frames are then copied to higher-level's code
+// sg lists.
+//
+// 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.
+//
+// There are actually two implementations of the TX code. The MCF5272
+// has no alignment restrictions for the data being transmitted, so it
+// is possible to avoid a copy and put the higher-level sg_list entries
+// directly into the device's ring buffer. The MCF5282 does have
+// alignment restrictions and the sg_list entries may not be suitably
+// aligned, so a copy operation is necessary.
+// NOTE: a possible optimization is to detect aligned vs. misaligned
+// data and only copy the bits that need aligning. This would significantly
+// complicate the code and it is not clear that data would be aligned
+// sufficiently often to make it worthwhile, at least not without work
+// inside the TCP/IP stack.
+//
+// With only one outgoing transmission at a time a full ring buffer seems
+// overkill. However it appears that the hardware can get confused if the
+// ring buffer consists of only a single entry, so the code has to
+// implement a full ring buffer anyway
+
+#if defined(HAL_DCACHE_LINE_SIZE) && (HAL_DCACHE_LINE_SIZE != 16)
+# error Driver code needs adjusting for cache line sizes other than 16 bytes.
+#endif
+
+#if defined(CYGPKG_HAL_M68K_MCF5272)
+
+# undef TX_NEEDS_ALIGNMENT
+# define TX_BUFFER_SIZE 0
+
+// Always use an even number of buffer descriptors, to preserve
+// alignment to a 16-byte boundary. We need one more entry in the
+// ring buffer than SG_LIST_SIZE.
+# if (0 == (CYGNUM_IO_ETH_DRIVERS_SG_LIST_SIZE & 0x01))
+#  define TX_BUFFER_DESCRIPTOR_COUNT   (CYGNUM_IO_ETH_DRIVERS_SG_LIST_SIZE + 2)
+# else
+#  define TX_BUFFER_DESCRIPTOR_COUNT   (CYGNUM_IO_ETH_DRIVERS_SG_LIST_SIZE + 1)
+# endif
+
+// This #elif should probably become a #else
+#elif defined(CYGPKG_HAL_M68K_MCF5282) || defined(CYGPKG_HAL_M68K_MCF532x)
+
+# define TX_NEEDS_ALIGNMENT             1
+# define TX_BUFFER_SIZE                 1520
+# define TX_BUFFER_DESCRIPTOR_COUNT     2
+
+#else
+
+# error Current processor unsupported
+
+#endif
+
+#if (0 == (CYGNUM_DEVS_ETH_MCFxxxx_RXBUFFERS & 0x01)) || (0 == HAL_DCACHE_SIZE)
+# define RX_BUFFER_DESCRIPTOR_PADDING   0
+#else
+# define RX_BUFFER_DESCRIPTOR_PADDING   16
+#endif
+
+typedef struct mcfxxxx_eth {
+    
+    // There are two separate interrupt vectors to worry about, RXF
+    // and TXF. Other interrupt sources such as error conditions are
+    // handled by the main tx/rx code, not by separate interrupt
+    // handlers.
+    cyg_handle_t    interrupt_handle_rx;
+    cyg_interrupt   interrupt_data_rx;
+    cyg_handle_t    interrupt_handle_tx;
+    cyg_interrupt   interrupt_data_tx;
+
+    // Pointers to the buffer descriptors. These index the data[] array
+    // below.
+    hal_mcfxxxx_eth_buffer_descriptor*  txbds;
+    hal_mcfxxxx_eth_buffer_descriptor*  rxbds;
+    
+#ifdef CYGFUN_DEVS_ETH_MCFxxxx_STATISTICS
+    cyg_uint32      interrupts;
+    cyg_uint32      tx_count;
+    cyg_uint32      tx_good;
+    cyg_uint32      tx_max_collisions;
+    cyg_uint32      tx_late_collisions;
+    cyg_uint32      tx_underrun;
+    cyg_uint32      tx_carrier_loss;
+    cyg_uint32      tx_deferred;
+    cyg_uint32      tx_single_collisions;
+    cyg_uint32      tx_mult_collisions;
+    cyg_uint32      tx_total_collisions;
+    
+    cyg_uint32      rx_count;
+    cyg_uint32      rx_good;
+    cyg_uint32      rx_crc_errors;
+    cyg_uint32      rx_align_errors;
+    cyg_uint32      rx_overrun_errors;
+    cyg_uint32      rx_short_frames;
+    cyg_uint32      rx_too_long_frames;
+#endif
+
+    unsigned long   tx_key;
+
+    cyg_uint8       tx_index;
+    cyg_uint8       started;
+    cyg_uint8       tx_can_send;
+    cyg_uint8       tx_done;
+    cyg_uint8       rx_rdy;
+    cyg_uint8       rx_next_buffer;
+    cyg_uint16      rx_len;
+    cyg_uint8       mac[6];
+
+    // We need:
+    // 1) padding to get the data aligned to a 16-byte boundary. A
+    //    16 byte alignment for the rx and tx buffer descriptors is
+    //    recommended, and should also help with flushing/invalidating
+    //    cache lines when necessary.
+    // 2) n 1520 byte rx buffers. 1520 is a multiple of 16 so maintains
+    //    alignment.
+    // 3) possibly a 1520 byte tx buffer, as per TX_ALIGNED_BUF_SIZE.
+    // 4) some number of tx buffer descriptors, as per TX_BUFFER_COUNT.
+    //    This number will be even, maintaining alignment to 16 bytes.
+    // 5) some number of rx buffer descriptors. The number of RX buffers
+    //    need not be even since each buffer is large.
+    // 6) padding for the RXBUFFERS to ensure that cache lines are not
+    //    shared between buffer descriptors and other data.
+    cyg_uint8   data[15 +
+                     (1520 * CYGNUM_DEVS_ETH_MCFxxxx_RXBUFFERS) +
+                     TX_BUFFER_SIZE +
+                     (TX_BUFFER_DESCRIPTOR_COUNT * sizeof(hal_mcfxxxx_eth_buffer_descriptor)) +
+                     (CYGNUM_DEVS_ETH_MCFxxxx_RXBUFFERS * sizeof(hal_mcfxxxx_eth_buffer_descriptor)) +
+                     RX_BUFFER_DESCRIPTOR_PADDING
+        ];
+} mcfxxxx_eth;
+
+// This structure is held in bss so all stats etc. are automatically zeroed.
+static mcfxxxx_eth  mcfxxxx_eth_info;
+
+ETH_DRV_SC(mcfxxxx_eth_sc0,
+           (void*) &mcfxxxx_eth_info,
+           CYGDAT_DEVS_ETH_MCFxxxx_NAME,
+           mcfxxxx_eth_start,
+           mcfxxxx_eth_stop,
+           mcfxxxx_eth_ioctl,
+           mcfxxxx_eth_can_send,
+           mcfxxxx_eth_send,
+           mcfxxxx_eth_recv,
+           mcfxxxx_eth_deliver,
+           mcfxxxx_eth_poll,
+           mcfxxxx_eth_int_vector
+    );
+
+NETDEVTAB_ENTRY(mcfxxxx_eth_netdev0,
+                "mcfxxxx-" CYGDAT_DEVS_ETH_MCFxxxx_NAME,
+                mcfxxxx_eth_init,
+                &mcfxxxx_eth_sc0);
+
+static cyg_uint32   mcfxxxx_eth_isr_rx(cyg_vector_t, cyg_addrword_t);
+static void         mcfxxxx_eth_dsr_rx(cyg_vector_t, cyg_ucount32, cyg_addrword_t);
+static cyg_uint32   mcfxxxx_eth_isr_tx(cyg_vector_t, cyg_addrword_t);
+static void         mcfxxxx_eth_dsr_tx(cyg_vector_t, cyg_ucount32, cyg_addrword_t);
+
+// ----------------------------------------------------------------------------
+// The MAC address. Usually this will be supplied by the platform HAL, but some
+// platforms may not have appropriate hardware. In that case the address will
+// normally come from RedBoot's config info, with a fallback configurable
+// value.
+#ifdef CYGINT_DEVS_ETH_MCFxxxx_PLATFORM_MAC
+
+# if !defined(HAL_MCFxxxx_ETH_GET_MAC_ADDRESS) && !defined(HAL_MCFxxxx_ETH_GET_MAC_ADDRESS)
+#  error Platform HAL should have provided a MAC address macro
+# endif
+
+#else
+
+static cyg_uint8    mcfxxxx_eth_default_mac[6]  = { CYGDAT_DEVS_ETH_MCFxxxx_PLATFORM_MAC } ;
+
+# ifdef CYGPKG_REDBOOT
+#  include <pkgconf/redboot.h>
+#  ifdef CYGSEM_REDBOOT_FLASH_CONFIG
+#   include <redboot.h>
+#   include <flash_config.h>
+RedBoot_config_option("Network hardware address [MAC]",
+                      mcfxxxx_mac,
+                      ALWAYS_ENABLED, true,
+                      CONFIG_ESA, 0
+    );
+#  endif
+# endif
+
+static void
+mcfxxxx_eth_get_config_mac_address(cyg_uint8* mac)
+{
+    int mac_ok  = 0;
+#ifdef CYGPKG_REDBOOT
+# ifdef CYGSEM_REDBOOT_FLASH_CONFIG
+    mac_ok  = flash_get_config("mcfxxxx_mac", mac, CONFIG_ESA);
+# endif    
+#else
+    // Not in RedBoot. Do we have virtual vectors?
+# ifdef CYGSEM_HAL_VIRTUAL_VECTOR_SUPPORT
+    mac_ok  = CYGACC_CALL_IF_FLASH_CFG_OP(CYGNUM_CALL_IF_FLASH_CFG_GET, "mcfxxxx_mac", mac, CYGNUM_FLASH_CFG_TYPE_CONFIG_ESA);
+# endif    
+#endif
+    if (! mac_ok) {
+        memcpy(mac, mcfxxxx_eth_default_mac, 6);
+    }
+}
+
+#endif // CYGINT_DEVS_ETH_MCFxxxx_PLATFORM_MAC
+
+// ----------------------------------------------------------------------------
+
+static bool
+mcfxxxx_eth_init(struct cyg_netdevtab_entry* ndp)
+{
+    struct eth_drv_sc*      sc  = (struct eth_drv_sc*)  ndp->device_instance;
+    struct  mcfxxxx_eth*    eth = (struct mcfxxxx_eth*) sc->driver_private;
+    CYG_ADDRWORD            ptr;
+    cyg_uint8*              rxbuffers;
+    cyg_uint8*              txbuffer;
+    cyg_uint32              i;
+
+    DIAG(1, "entry");
+
+#ifdef HAL_MCFxxxx_ETH_DETECT_DEVICE
+    if (! HAL_MCFxxxx_ETH_DETECT_DEVICE(HAL_MCFxxxx_ETH0_BASE) ) {
+        DIAG(1, "device disabled by HAL");
+        return 0;
+    }
+#endif
+    
+#ifdef CYGSEM_HAL_USE_ROM_MONITOR
+    // If we are debugging over RedBoot, it is possible that there is an
+    // outgoing packet still queued up. Give it a chance to get out, a
+    // couple of milliseconds should suffice.
+    for (i = 0; i < 20; i++) {
+        cyg_uint32              x_des_active;
+        READ32(TDAR(eth), x_des_active);
+        if (0 == (x_des_active & HAL_MCFxxxx_ETHx_TDAR_X_DES_ACTIVE)) {
+            break;
+        }
+        HAL_DELAY_US(100);
+    }
+#endif
+
+    // Set the whole ethernet device to disabled. Then the other registers
+    // can be manipulated safely. The device gets activated in the start
+    // function. The reset also sets all registers to their default values.
+    WRITE32(ECR(eth), HAL_MCFxxxx_ETHx_ECR_RESET);
+
+    eth->started        = 0;
+    // While the reset is going on, sort out the buffers and buffer descriptors.
+    // These should be aligned to a 16-byte boundary.
+    ptr          = (CYG_ADDRWORD) eth->data;
+    ptr          = (ptr + 15) & ~15;
+    // First the rx buffers, n * 1520 bytes.
+    rxbuffers    = (cyg_uint8*) ptr;
+    ptr         += (CYGNUM_DEVS_ETH_MCFxxxx_RXBUFFERS * 1520);
+    // We are still aligned to a 16-byte boundary. Now for the tx buffer if necessary.
+    txbuffer     = (cyg_uint8*) ptr;
+    ptr         += TX_BUFFER_SIZE;
+    // Still aligned. The tx buffer descriptors. This will be an even number
+    // so alignment is maintained.
+    eth->txbds   = (hal_mcfxxxx_eth_buffer_descriptor*) ptr;
+    ptr         += (TX_BUFFER_DESCRIPTOR_COUNT * sizeof(hal_mcfxxxx_eth_buffer_descriptor));
+    // And finally the rx buffer descriptors.
+    eth->rxbds   = (hal_mcfxxxx_eth_buffer_descriptor*) ptr;
+
+    // We can fill in the buffer fields in the various rx buffer descriptors.
+    // The length and flag fields are handled by _start().
+    for (i = 0; i < CYGNUM_DEVS_ETH_MCFxxxx_RXBUFFERS; i++) {
+        eth->rxbds[i].ethbd_buffer   = rxbuffers;
+        rxbuffers                   += 1520;
+    }
+
+    // Ditto for the tx buffers, if tx involves copying into an
+    // aligned buffer. Only one entry in the ring buffer will be used
+    // at a time, so the two buffer descriptors can share the same
+    // buffer.
+#ifdef TX_NEEDS_ALIGNMENT
+    eth->txbds[0].ethbd_buffer  = txbuffer;
+    eth->txbds[1].ethbd_buffer  = txbuffer;
+#endif
+    
+    // rx_next_buffer tracks the next rx buffer descriptor which the
+    // hardware may process.
+    eth->rx_next_buffer     = 0;
+
+    // Determine the MAC address. This has to be done by the platform HAL if possible
+#ifdef CYGINT_DEVS_ETH_MCFxxxx_PLATFORM_MAC
+    HAL_MCFxxxx_ETH_GET_MAC_ADDRESS(eth->mac);
+#else
+    mcfxxxx_eth_get_config_mac_address(eth->mac);
+#endif
+    
+    // The interrupt handlers can also be installed here. All
+    // interrupts are masked after the reset so nothing will
+    // actually happen. Separate ISR's and DSR's are used for
+    // the various interrupt sources.
+    cyg_drv_interrupt_create(CYGNUM_HAL_ISR_ERX,
+                             CYGNUM_DEVS_ETH_MCFxxxx_ISR_RX_PRIORITY,
+                             (CYG_ADDRWORD) sc,
+                             &mcfxxxx_eth_isr_rx,
+                             &mcfxxxx_eth_dsr_rx,
+                             &(eth->interrupt_handle_rx),
+                             &(eth->interrupt_data_rx));
+    cyg_drv_interrupt_attach(eth->interrupt_handle_rx);
+    cyg_drv_interrupt_unmask(CYGNUM_HAL_ISR_ERX);
+    
+    cyg_drv_interrupt_create(CYGNUM_HAL_ISR_ETX,
+                             CYGNUM_DEVS_ETH_MCFxxxx_ISR_TX_PRIORITY,
+                             (CYG_ADDRWORD) sc,
+                             &mcfxxxx_eth_isr_tx,
+                             &mcfxxxx_eth_dsr_tx,
+                             &(eth->interrupt_handle_tx),
+                             &(eth->interrupt_data_tx));
+    cyg_drv_interrupt_attach(eth->interrupt_handle_tx);
+    cyg_drv_interrupt_unmask(CYGNUM_HAL_ISR_ETX);
+
+    // An EBERR bus error will halt the ethernet device and raise an
+    // interrupt. Arguably this should be handled here, but the assumption
+    // is that the interrupt will never be raised because the driver will
+    // never be given a bogus pointer.
+    
+    // Now the rest of the hardware can be initialized. First control
+    // the GPIO pins, or whatever else needs doing on a per-processor
+    // basis. For now the PHY is enabled unconditionally.
+    // NOTE: much of this initialization code should be moved to a
+    // separate reset function which can be called during start-up or
+    // after an internal bus error.
+#ifdef HAL_MCFxxxx_ETH0_PROC_INIT
+    HAL_MCFxxxx_ETH0_PROC_INIT(1);
+#endif    
+#ifdef HAL_MCFxxxx_ETH0_PLATFORM_INIT
+    HAL_MCFxxxx_ETH0_PLATFORM_INIT();
+#endif    
+
+    // Now for the ethernet device itself. The order used here is as
+    // per the MCF5282 User's Manual.
+    //
+    // EIMR:     We want interrupts for complete frame transfers only
+    WRITE32(EIMR(eth), HAL_MCFxxxx_ETHx_EIMR_TXF  | HAL_MCFxxxx_ETHx_EIMR_RXF);
+
+    // EIR:     Cleared by the reset, but just in case.
+    WRITE32(EIR(eth), 0xFFF80000);
+    // TFWR:     Leave to its default value
+#if defined(HAL_MCFxxxx_ETHx_IAUR)
+    // IAUR & IALR.     The MCF5282 also has a unicast hash table, allowing the
+    //                  device to accept frames sent to several different addresses.
+    //                  This is not supported, only an exact match is used.
+    WRITE32(IAUR(eth), 0x0);
+    WRITE32(IALR(eth), 0x0);
+#endif
+    // Multicast hash table (GAUR & GALR). All 1's should be equivalent
+    // to multi-all. All 0's should be equivalent to no-multi.
+    WRITE32(GAUR(eth), 0x0);
+    WRITE32(GALR(eth),  0x0);
+    // MAC address (PAUR & PALR)
+    i = (eth->mac[4] << 24) | (eth->mac[5] << 16);
+    WRITE32(PAUR(eth), i);
+    i = (eth->mac[0] << 24) | (eth->mac[1] << 16) | (eth->mac[2] << 8) | (eth->mac[3] << 0);
+    WRITE32(PALR(eth), i);
+#if defined(HAL_MCFxxxx_ETHx_OPD)
+    // OPD: not actually used, but reset it to its default anyway
+    WRITE32(OPD(eth), 0x00010000);
+#endif
+    // RCR:     Default to half-duplex mode. Promiscuous mode may get
+    //          set by start().
+    //          MII mode should be platform-specific, 7-wire mode is possible.
+    //          On an mcf5282 the register also contains the max frame length
+#if defined(HAL_MCFxxxx_ETHx_RCR_MAX_FL_VALUE)
+    WRITE32(RCR(eth), HAL_MCFxxxx_ETHx_RCR_MAX_FL_VALUE | HAL_MCFxxxx_ETHx_RCR_MII_MODE | HAL_MCFxxxx_ETHx_RCR_DRT);
+#else 
+    WRITE32(RCR(eth), HAL_MCFxxxx_ETHx_RCR_MII_MODE | HAL_MCFxxxx_ETHx_RCR_DRT);
+#endif    
+    // TCR:     default to half-duplex
+    WRITE32(TCR(eth), 0);
+
+    // MSCR/MII_SPEED.
+    // MDC_FREQUENCY == HZ / (4 * reg value)
+    // MDC_FREQUENCY should be <= 2.5MHz
+    WRITE32(MSCR(eth), ((CYGHWR_HAL_SYSTEM_CLOCK_HZ - 1) / (4 * 2500000)) + 1);
+    
+    // FRSR:       Leave to its default value
+    // EMRBR:   The receiver buffer size is 1520 (must be a multiple of 16 and > frame size)
+    WRITE32(EMRBR(eth), 1520);
+    // The buffer descriptor rings, ERDSR & ETDSR
+    WRITE32(ERDSR(eth), (cyg_uint32)eth->rxbds);
+    WRITE32(ETDSR(eth), (cyg_uint32)eth->txbds);
+
+    // Other registers:
+    // IVEC: readonly
+    // RDAR:        Set by start() and by the receive code
+    // TDAR:        Set by start() and by the transmit code
+    // MMFR:        There is no need to access the phy
+    // R_BOUND:     Read-only
+    // MFLR:        The default length is already 1518 bytes, so leave it.
+    //              (This register does not exist on the 5282)
+    
+    // The ethernet device gets enabled in _start()
+    
+    DIAG(1, "calling higher-level");
+    
+    // Initialize the upper level driver
+    (sc->funs->eth_drv->init)(sc, eth->mac);
+
+    DIAG(1, "done");
+    
+    return 1;
+}
+
+// ----------------------------------------------------------------------------
+
+// The driver only supports one transmit at a time, and a global is used
+// to keep track of whether or not a transmit is in progress.
+static int
+mcfxxxx_eth_can_send(struct eth_drv_sc* sc)
+{
+    struct  mcfxxxx_eth*    eth = (struct mcfxxxx_eth*) sc->driver_private;
+    if (eth->tx_can_send) {
+        DIAG(1, "yes");
+    } else {
+        DIAG(3, "no");
+    }
+    return eth->tx_can_send;
+}
+
+// There are two implementations of the transmit code, one for the case
+// where the data needs to be copied into an aligned buffer, the other
+// for when the data can be used in place.
+
+static void
+mcfxxxx_eth_send(struct eth_drv_sc* sc,
+                 struct eth_drv_sg* sg_list, int sg_len, int total_len,
+                 unsigned long key)
+{
+    struct  mcfxxxx_eth*    eth     = (struct mcfxxxx_eth*) sc->driver_private;
+    cyg_uint32              index   = eth->tx_index;
+    int                     i;
+
+    DIAG(1, " total_len %d", total_len);
+    eth->tx_can_send    = 0;
+    eth->tx_key         = key;
+
+#ifdef TX_NEEDS_ALIGNMENT
+    {
+        // The aligned buffer is permanently held in the buffer descriptor.
+        cyg_uint8*   buf = eth->txbds[index].ethbd_buffer;
+        for (i = 0; i < sg_len; i++) {
+            memcpy(buf, (cyg_uint8*) sg_list[i].buf, sg_list[i].len);
+            buf += sg_list[i].len;
+        }
+        eth->txbds[index].ethbd_length  = total_len;
+        eth->txbds[index].ethbd_flags  |= HAL_MCFxxxx_ETHx_TXBD_R + HAL_MCFxxxx_ETHx_TXBD_L + HAL_MCFxxxx_ETHx_TXBD_TC;
+        DIAGPKT(1, "start", buf, total_len);
+        
+# ifdef HAL_DCACHE_STORE
+        // Make sure the data has been written to memory.
+        HAL_DCACHE_STORE(eth->txbds[index].ethbd_buffer, total_len);
+# endif
+    }
+#else
+    {
+        cyg_uint32 last_index = index;
+        for (i = 0; i < sg_len; i++) {
+            eth->txbds[index].ethbd_flags   &= ~(HAL_MCFxxxx_ETHx_TXBD_L | HAL_MCFxxxx_ETHx_TXBD_TC);
+            // The R bit can be set immediately, rather than in the
+            // usual reverse order to prevent an underrun. This is
+            // because a new send is only initiated when the transmit
+            // engine is idle.
+            eth->txbds[index].ethbd_flags   |= HAL_MCFxxxx_ETHx_TXBD_R;
+            eth->txbds[index].ethbd_length   = sg_list[i].len;
+            eth->txbds[index].ethbd_buffer   = (cyg_uint8*) sg_list[i].buf;
+# ifdef HAL_DCACHE_STORE
+            HAL_DCACHE_STORE(eth->txbds[index].ethbd_buffer, sg_list[i].len);
+# endif            
+            last_index  = index;
+            if (index == (TX_BUFFER_DESCRIPTOR_COUNT - 1)) {
+                index = 0;
+            } else {
+                index += 1;
+            }
+        }
+        eth->txbds[last_index].ethbd_flags |= HAL_MCFxxxx_ETHx_TXBD_L | HAL_MCFxxxx_ETHx_TXBD_TC;
+
+        eth->tx_index  = last_index;
+        DIAGPKT(1, "start", sg_list[0].buf, total_len);
+    }
+#endif
+    
+    // Make sure all the buffer descriptors have been written to memory.
+    // No need to invalidate the cache entries, the driver does not look
+    // for any bits changed by the hardware except on the xxxx which
+    // does not have a data cache at all.
+#ifdef HAL_DCACHE_STORE    
+    HAL_DCACHE_STORE(&(eth->txbds[0]), TX_BUFFER_DESCRIPTOR_COUNT * sizeof(hal_mcfxxxx_eth_buffer_descriptor));
+#endif    
+    WRITE32(TDAR(eth), HAL_MCFxxxx_ETHx_TDAR_X_DES_ACTIVE);
+}
+
+// The TX interrupt should only trigger when a whole frame has been
+// transmitted. There is no need to worry about nested tx interrupts,
+// at most one transmit can be in progress at any one time. Just
+// clear the interrupt pending bit and leave the rest to the DSR.
+static cyg_uint32
+mcfxxxx_eth_isr_tx(cyg_vector_t vector, cyg_addrword_t data)
+{
+    HAL_WRITE_UINT32(EIR(eth), HAL_MCFxxxx_ETHx_EIR_TXF | HAL_MCFxxxx_ETHx_EIR_TXB);
+    return CYG_ISR_CALL_DSR;
+}
+
+static void
+mcfxxxx_eth_dsr_tx(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
+{
+    struct eth_drv_sc*  sc      = (struct eth_drv_sc*) data;
+    mcfxxxx_eth*        eth     = (mcfxxxx_eth*)(sc->driver_private);
+
+    INCR_STAT(eth->interrupts);
+#ifdef CYGFUN_DEVS_ETH_MCFxxxx_STATISTICS
+    
+# if defined(CYGPKG_HAL_M68K_MCF5272)
+    {
+        cyg_uint16          flags;
+        // The MCF5272 has all appropriate error flags in the tx buffer descriptor.
+        flags = eth->txbds[eth->tx_index].ethbd_flags & (HAL_MCFxxxx_ETHx_TXBD_DEF | HAL_MCFxxxx_ETHx_TXBD_LC | HAL_MCFxxxx_ETHx_TXBD_RL |
+                                                         HAL_MCFxxxx_ETHx_TXBD_RC_MASK | HAL_MCFxxxx_ETHx_TXBD_UN | HAL_MCFxxxx_ETHx_TXBD_CSL);
+        if (0 == flags) {
+            INCR_STAT(eth->tx_good);
+        } else {
+            if (flags & HAL_MCFxxxx_ETHx_TXBD_RL) {
+                eth->tx_max_collisions    = 16;
+                INCR_STAT(eth->tx_total_collisions);
+            } else {
+                int collisions = (flags & HAL_MCFxxxx_ETHx_TXBD_RC_MASK) >> HAL_MCFxxxx_ETHx_TXBD_RC_SHIFT;
+                if (collisions > 0) {
+                    INCR_STAT(eth->tx_total_collisions);
+                    if (collisions > eth->tx_max_collisions) {
+                        eth->tx_max_collisions = collisions;
+                    }
+                    if (1 == collisions) {
+                        INCR_STAT(eth->tx_single_collisions);
+                    } else {
+                        INCR_STAT(eth->tx_mult_collisions);
+                    }
+                }
+            }
+            if (flags & HAL_MCFxxxx_ETHx_TXBD_LC) {
+                INCR_STAT(eth->tx_late_collisions);
+            }
+            if (flags & HAL_MCFxxxx_ETHx_TXBD_UN) {
+                INCR_STAT(eth->tx_underrun);
+            }
+            if (flags & HAL_MCFxxxx_ETHx_TXBD_CSL) {
+                INCR_STAT(eth->tx_carrier_loss);
+            }
+            if (flags & HAL_MCFxxxx_ETHx_TXBD_DEF) {
+                INCR_STAT(eth->tx_deferred);
+            }
+        }
+    }
+# else
+    {
+        // Assume MCF5282-compatible.
+        // There are no error bits in the tx buffer descriptor. Instead it is
+        // possible to check the interrupt status register, look for error
+        // flags, and clear them.
+        cyg_uint32  flags;
+        HAL_READ_UINT32(EIR(eth), flags);
+        flags &= (HAL_MCFxxxx_ETHx_EIR_LC | HAL_MCFxxxx_ETHx_EIR_RL | HAL_MCFxxxx_ETHx_EIR_UN);
+        if (0 == flags) {
+            INCR_STAT(eth->tx_good);
+        } else {
+            if (flags & HAL_MCFxxxx_ETHx_EIR_LC) {
+                INCR_STAT(eth->tx_late_collisions);
+            }
+            if (flags & HAL_MCFxxxx_ETHx_EIR_RL) {
+                INCR_STAT(eth->tx_total_collisions);
+                INCR_STAT(eth->tx_mult_collisions);
+            }
+            if (flags & HAL_MCFxxxx_ETHx_EIR_UN) {
+                INCR_STAT(eth->tx_underrun);
+            }
+            // Clear these interrupt flags
+            HAL_WRITE_UINT32(EIR(eth), flags);
+        }
+    }
+# endif
+#endif
+    
+    eth->tx_done    = 1;
+    eth->tx_index   = eth->tx_index + 1;
+    if (TX_BUFFER_DESCRIPTOR_COUNT == eth->tx_index) {
+        eth->tx_index = 0;
+    }
+    // There is no need to worry about cleaning up the buffer descriptors.
+    // The hardware will have cleared the R bit, which is the only one that
+    // must be cleared.
+    DIAG(1, "packet sent");
+    eth_drv_dsr(vector, count, data);
+}
+
+// ----------------------------------------------------------------------------
+
+// A whole ethernet frame has been received and reported to higher-level
+// code, which has allocated an mbuf. The frame is identified by the
+// rx_next_buffer field of the mcfxxxx_eth structure, which gets set in
+// deliver(). The data must be copied into the mbuf, and then the
+// hardware needs to be told that this buffer is available again.
+// The hardware stores a 4-byte checksum in the receive buffer
+// which must be discarded.
+static void
+mcfxxxx_eth_recv(struct eth_drv_sc* sc, struct eth_drv_sg* sg_list, int sg_len)
+{
+    mcfxxxx_eth*    eth     = (mcfxxxx_eth*) sc->driver_private;
+    int             index   = eth->rx_next_buffer;
+    cyg_uint8*      buf     = eth->rxbds[index].ethbd_buffer;
+    int             len     = eth->rx_len - 4;
+    int             i;
+
+    DIAGPKT(1, "packet received", buf, len);
+    
+    for (i = 0; (i < sg_len) && (len > 0); i++) {
+        if (0 == sg_list[i].buf) {
+            break;
+        } else if (len <= sg_list[i].len) {
+            memcpy((void*) sg_list[i].buf, buf, len);
+            break;
+        } else {
+            memcpy((void*) sg_list[i].buf, buf, sg_list[i].len);
+            buf += sg_list[i].len;
+            len -= sg_list[i].len;
+        }
+    }
+
+    // Once the data has been copied out of the RX buffer, clear the
+    // cache lines ready for the next packet. Use an invalidate if
+    // available, otherwise a flush.
+#if defined(HAL_DCACHE_INVALIDATE)
+    HAL_DCACHE_INVALIDATE(eth->rxbds[index].ethbd_buffer, eth->rx_len);
+#elif defined(HAL_DCACHE_FLUSH)
+    HAL_DCACHE_FLUSH(eth->rxbds[index].ethbd_buffer, eth->rx_len);
+#endif
+}
+
+// The RX interrupt triggers whenever a whole frame has been received (or
+// when an error has occurred). The hardware may be busy receiving into the
+// next buffer.
+//
+// Because of the ring buffer several packets may arrive before the cpu gets
+// around to processing them. It is possible to save a little bit of load
+// by masking the rxf interrupt inside the ethernet device, then doing the
+// acknowledge and an unmask inside the deliver() code.
+static cyg_uint32
+mcfxxxx_eth_isr_rx(cyg_vector_t vector, cyg_addrword_t data)
+{
+    WRITE32(EIMR(eth), HAL_MCFxxxx_ETHx_EIMR_TXF);
+    return CYG_ISR_CALL_DSR;
+}
+
+static void
+mcfxxxx_eth_dsr_rx(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
+{
+    struct eth_drv_sc*  sc  = (struct eth_drv_sc*) data;
+    mcfxxxx_eth*        eth = (mcfxxxx_eth*)(sc->driver_private);
+
+    INCR_STAT(eth->interrupts);
+    eth->rx_rdy = 1;
+    eth_drv_dsr(vector, count, data);
+}
+
+// ----------------------------------------------------------------------------
+// deliver() is usually called from thread context, after the DSR has woken
+// up the packet handling thread. It needs to report completed transmits so
+// that higher-level code can release the mbuf, and report all received
+// frames.
+
+static void
+mcfxxxx_eth_deliver(struct eth_drv_sc* sc)
+{
+    mcfxxxx_eth*    eth = (mcfxxxx_eth*) sc->driver_private;
+
+    DIAG(1, "entry");
+    
+    if (eth->tx_done) {
+        DIAG(1, "tx_done");
+        INCR_STAT(eth->tx_count);
+        eth->tx_done        = 0;
+        eth->tx_can_send    = 1;
+        (*sc->funs->eth_drv->tx_done)(sc, eth->tx_key, 1);
+    }
+    
+    if (eth->rx_rdy) {
+        int bd_emptied  = 0;
+        int frame_found = 0;
+
+        DIAG(1, "rx_rdy");
+        eth->rx_rdy = 0;
+
+        // Acknowledge any packets that have been received and will be processed
+        // by the loop below. There is a small possibility of a packet arriving
+        // in the middle of this loop, causing an rxf interrupt to become pending
+        // even though the packet will get processed by the loop. This is harmless.
+        // The rxb interrupt is cleared as well because that comes for free, making
+        // debugging slightly easier.
+        WRITE32(EIR(eth), HAL_MCFxxxx_ETHx_EIR_RXF | HAL_MCFxxxx_ETHx_EIR_RXB);
+
+        DIAG(1, "updated eir");
+        // Loop while there are non-empty packets. This has to be done with a bit
+        // of care. Packets should be processed in the right order where possible,
+        // which means keeping track of the last packet received. However it is
+        // necessary to check all the buffers - for some reason the hardware does
+        // not always put the next frame in the expected buffer.
+        //
+        // This code interacts with any data cache in unpleasant ways. A buffer
+        // descriptor is only 8 bytes, i.e. half a cache line, and since
+        // typically the system is configured with several rx buffers some of
+        // these may get updated by the hardware at any time. The only safe
+        // way to handle this is to disable the data cache when accessing the
+        // rx buffer descriptors and thus make sure that there are never any
+        // modified cachelines for the tx buffer descriptors.
+        do {
+            int         current;
+            int         i;
+
+            current     = eth->rx_next_buffer;
+            frame_found = 0;
+            
+            for (i = 0; i < CYGNUM_DEVS_ETH_MCFxxxx_RXBUFFERS; i++) {
+                // Note: these absolutely must be held in registers. The package's
+                // CDL imposes constraints to ensure the driver is built with
+                // sufficient optimization.
+                register cyg_uint16 flags;
+                register cyg_uint16 len;
+                register hal_mcfxxxx_eth_buffer_descriptor* rxbd = &(eth->rxbds[current]);
+#if defined(HAL_DCACHE_SIZE) && (HAL_DCACHE_SIZE > 0)
+                register int cache_enabled;
+                HAL_DCACHE_IS_ENABLED(cache_enabled);
+                if (cache_enabled) {
+                    register int ints_enabled;
+                    HAL_DISABLE_INTERRUPTS(ints_enabled);
+                    HAL_DCACHE_DISABLE();
+                    flags   = rxbd->ethbd_flags;
+                    len     = rxbd->ethbd_length;
+                    if (! (flags & HAL_MCFxxxx_ETHx_RXBD_E)) {
+                        rxbd->ethbd_flags  = (flags & HAL_MCFxxxx_ETHx_RXBD_W) | HAL_MCFxxxx_ETHx_RXBD_E;
+                        rxbd->ethbd_length = 1518;
+                    }
+                    HAL_DCACHE_ENABLE();
+                    HAL_RESTORE_INTERRUPTS(ints_enabled);
+                } else 
+#endif
+                {
+                    flags   = rxbd->ethbd_flags;
+                    len     = rxbd->ethbd_length;
+                    if (! (flags & HAL_MCFxxxx_ETHx_RXBD_E)) {
+                        rxbd->ethbd_flags  = (flags & HAL_MCFxxxx_ETHx_RXBD_W) | HAL_MCFxxxx_ETHx_RXBD_E;
+                        rxbd->ethbd_length = 1518;
+                    }
+                }
+
+                if (flags & HAL_MCFxxxx_ETHx_RXBD_E) {
+                    current = (current == (CYGNUM_DEVS_ETH_MCFxxxx_RXBUFFERS - 1)) ? 0 : (current + 1);
+                    continue;
+                }
+
+                // Full frames should fill one buffer. However if an over-sized frame
+                // is received then the first 1520 bytes will go into one buffer, the
+                // remaining into the next. The LG flag will get set only in the last
+                // buffer descriptor. Just mark the first buffer as empty after the
+                // if.
+                if (flags & HAL_MCFxxxx_ETHx_RXBD_L) {
+                    // We have the last frame in a packet.
+#ifdef CYGFUN_DEVS_ETH_MCFxxxx_STATISTICS
+                    INCR_STAT(eth->rx_count);
+                    if (flags & HAL_MCFxxxx_ETHx_RXBD_OV) {
+                        // An overrun does not invalidate the current frame
+                        INCR_STAT(eth->rx_overrun_errors);
+                    }
+#endif
+
+                    if (flags & (HAL_MCFxxxx_ETHx_RXBD_LG | HAL_MCFxxxx_ETHx_RXBD_NO | HAL_MCFxxxx_ETHx_RXBD_CR | HAL_MCFxxxx_ETHx_RXBD_TR)) {
+                        // The packet is invalid and should be discarded.
+#ifdef CYGFUN_DEVS_ETH_MCFxxxx_STATISTICS
+                        if (flags & (HAL_MCFxxxx_ETHx_RXBD_LG | HAL_MCFxxxx_ETHx_RXBD_TR)) {
+                            INCR_STAT(eth->rx_too_long_frames);
+                        }
+                        if (flags & HAL_MCFxxxx_ETHx_RXBD_NO) {
+                            INCR_STAT(eth->rx_align_errors);
+                        }
+                        if (flags & HAL_MCFxxxx_ETHx_RXBD_CR) {
+                            INCR_STAT(eth->rx_crc_errors);
+                        }
+#endif
+                    } else {
+                        // A valid packet
+                        INCR_STAT(eth->rx_good);
+                        eth->rx_len         = len;
+                        eth->rx_next_buffer = current;
+                        (*sc->funs->eth_drv->recv)(sc, len - 4);
+                    }
+                }
+
+                // Move on to the next buffer descriptor.
+                frame_found                 = 1;
+                bd_emptied                  = 1;
+                eth->rx_next_buffer         = (current == (CYGNUM_DEVS_ETH_MCFxxxx_RXBUFFERS - 1)) ? 0 : (current + 1);
+                break;
+            }
+        } while(frame_found);
+
+        // If at least one buffer was marked as now empty, inform the hardware.
+        if (bd_emptied) {
+            WRITE32(RDAR(eth), HAL_MCFxxxx_ETHx_RDAR_R_DES_ACTIVE);
+        }
+    }
+
+    // And make sure rx interrupts are unmasked again.
+    WRITE32(EIMR(eth), HAL_MCFxxxx_ETHx_EIMR_TXF  | HAL_MCFxxxx_ETHx_EIMR_RXF);
+}
+
+// ----------------------------------------------------------------------------
+
+// Polling does not need any special action, just check for pending interrupts
+// and act as if an interrupt had occurred.
+static void
+mcfxxxx_eth_poll(struct eth_drv_sc* sc)
+{
+    struct  mcfxxxx_eth*    eth = (struct mcfxxxx_eth*) sc->driver_private;
+    cyg_uint32              eir;
+
+    DIAG(4, "entry");
+    
+    READ32(EIR(eth), eir);
+    
+    if (eir & HAL_MCFxxxx_ETHx_EIR_TXF) {
+        WRITE32(EIR(eth), HAL_MCFxxxx_ETHx_EIR_TXF | HAL_MCFxxxx_ETHx_EIR_TXB);
+        eth->tx_done    = 1;
+        eth->tx_index   = eth->tx_index + 1;
+        if (TX_BUFFER_DESCRIPTOR_COUNT == eth->tx_index) {
+            eth->tx_index = 0;
+        }
+    }
+    if (eir & HAL_MCFxxxx_ETHx_EIR_RXF) {
+        eth->rx_rdy     = 1;
+    }
+    if (eth->tx_done || eth->rx_rdy) {
+        mcfxxxx_eth_deliver(sc);
+    }
+}
+
+// intvector() is used by RedBoot/stubs to ensure ctrl-C will get through
+// if ethernet is used for the debug channel.
+static int
+mcfxxxx_eth_int_vector(struct eth_drv_sc* sc)
+{
+    return CYGNUM_HAL_ISR_ERX;
+}
+
+// ----------------------------------------------------------------------------
+
+// SET_MAC_ADDRESS is straightforward, it just requires updating two registers.
+// SET_MC_ALL and SET_MC_LIST can be implemented by setting all bits in the
+// hash table. That should cause the hardware to match all multicast packets.
+
+static int
+mcfxxxx_eth_ioctl(struct eth_drv_sc* sc, unsigned long key, void* data, int data_length)
+{
+    mcfxxxx_eth*    eth = (mcfxxxx_eth*) sc->driver_private;
+
+    DIAG(1, "entry");
+    
+    switch(key) {
+      case ETH_DRV_SET_MAC_ADDRESS:
+        {
+            cyg_uint32  reg;
+            memcpy(eth->mac, data, 6);
+            reg = (eth->mac[4] << 24) | (eth->mac[5] << 16);
+            WRITE32(PAUR(eth), reg);
+            reg = (eth->mac[0] << 24) | (eth->mac[1] << 16) | (eth->mac[2] << 8) | (eth->mac[3] << 0);
+            WRITE32(PALR(eth), reg);
+            return 0;
+        }
+
+      case ETH_DRV_SET_MC_ALL:
+      case ETH_DRV_SET_MC_LIST:
+        {
+            WRITE32(GAUR(eth), 0xFFFFFFFF);
+            WRITE32(GALR(eth),  0xFFFFFFFF);
+            return 0;
+        }
+#if defined(CYGFUN_DEVS_ETH_MCFxxxx_STATISTICS) && defined(ETH_DRV_GET_IF_STATS_UD)
+      case ETH_DRV_GET_IF_STATS_UD:
+      case ETH_DRV_GET_IF_STATS:
+        {
+            struct ether_drv_stats* stats   = (struct ether_drv_stats*) data;
+            cyg_uint32              tcr;
+            
+            strcpy(stats->description, "Freescale MCFxxxx ethernet device");
+            stats->snmp_chipset[0]      = '\0';
+            READ32(TCR(eth), tcr);
+            if (0 == (tcr & HAL_MCFxxxx_ETHx_TCR_FDEN)) {
+                stats->duplex               = 2;                // simplex
+            } else {
+                stats->duplex               = 3;                // full duplex
+            }
+            stats->operational          = eth->started ? 3 : 2;
+            stats->speed                = 10 * 1000000;     // assume 10MHz. This would require querying the phy
+            stats->supports_dot3        = 1;
+            stats->tx_queue_len         = 1;                // fixed by the driver design
+            stats->tx_dropped           = 0;                // can_send() is robust
+            
+            stats->interrupts           = eth->interrupts;
+
+            stats->tx_count             = eth->tx_count;
+            stats->tx_good              = eth->tx_good;
+            stats->tx_max_collisions    = eth->tx_max_collisions;
+            stats->tx_late_collisions   = eth->tx_late_collisions;
+            stats->tx_underrun          = eth->tx_underrun;
+            stats->tx_carrier_loss      = eth->tx_carrier_loss;
+            stats->tx_deferred          = eth->tx_deferred;
+            stats->tx_single_collisions = eth->tx_single_collisions;
+            stats->tx_mult_collisions   = eth->tx_mult_collisions;
+            stats->tx_total_collisions  = eth->tx_total_collisions;
+            
+            stats->rx_count             = eth->rx_count;
+            stats->rx_good              = eth->rx_good;
+            stats->rx_crc_errors        = eth->rx_crc_errors;
+            stats->rx_align_errors      = eth->rx_align_errors;
+            stats->rx_overrun_errors    = eth->rx_overrun_errors;
+            stats->rx_short_frames      = eth->rx_short_frames;
+            stats->rx_too_long_frames   = eth->rx_too_long_frames;
+            
+            return 0;
+        }
+#endif
+        
+      default:
+        return 1;
+    }
+}
+
+// ----------------------------------------------------------------------------
+
+// Starting involves setting up initial empty buffer descriptors, then
+// enabling the ethernet device. Optionally promiscuous mode needs to be
+// set as well.
+
+#if 0
+static int
+mcfxxxx_eth_read_phy(int addr, int reg)
+{
+    cyg_uint32  events;
+    int         ints_state;
+    int         i;
+    int         result  = -1;
+    
+    HAL_DISABLE_INTERRUPTS(ints_state);
+    WRITE32(MMFR(eth),
+                     HAL_MCFxxxx_ETHx_MMFR_ST_VALUE |
+                     HAL_MCFxxxx_ETHx_MMFR_OP_READ |
+                     (addr << HAL_MCFxxxx_ETHx_MMFR_PA_SHIFT) |
+                     (reg << HAL_MCFxxxx_ETHx_MMFR_RA_SHIFT) |
+                     HAL_MCFxxxx_ETHx_MMFR_TA_VALUE |
+                     (0x00 << HAL_MCFxxxx_ETHx_MMFR_DATA_SHIFT));
+
+    // When the phy has responded the MII interrupt bit should be set.
+    for (i = 0; i < 10; i++) {
+        // 16 bits * both ways @ 2.5MHz, should just take a few microseconds.
+        // This happens in a loop, just in case.
+        HAL_DELAY_US(10);
+        READ32(EIR(eth), events);
+        if (events & HAL_MCFxxxx_ETHx_EIR_MII) {
+            WRITE32(EIR(eth), HAL_MCFxxxx_ETHx_EIR_MII);
+            READ32(MMFR(eth), result);
+            result  &= 0x0000FFFF;
+            break;
+        }
+    }
+    
+    HAL_RESTORE_INTERRUPTS(ints_state);
+    return result;
+}
+#endif
+
+static void
+mcfxxxx_eth_start(struct eth_drv_sc* sc, unsigned char* enaddr, int flags)
+{
+    mcfxxxx_eth*    eth = (mcfxxxx_eth*) sc->driver_private;
+    cyg_uint32      rcr;
+    int             i;
+
+    if (eth->started) {
+        return;
+    }
+
+    DIAG(1, "entry");
+    
+    eth->tx_can_send    = 1;
+    eth->tx_done        = 0;
+    eth->rx_rdy         = 0;
+
+    // Clear all tx buffer descriptors, setting the WRAP bit on the last one.
+    for (i = 0; i < TX_BUFFER_DESCRIPTOR_COUNT; i++) {
+        eth->txbds[i].ethbd_flags     = 0;
+    }
+    eth->txbds[TX_BUFFER_DESCRIPTOR_COUNT - 1].ethbd_flags    = HAL_MCFxxxx_ETHx_TXBD_W;
+    eth->tx_index = 0;
+
+    // Set all rx buffer descriptors to empty.
+    for (i = 0; i < CYGNUM_DEVS_ETH_MCFxxxx_RXBUFFERS; i++) {
+        eth->rxbds[i].ethbd_flags   = HAL_MCFxxxx_ETHx_RXBD_E;
+        eth->rxbds[i].ethbd_length  = 1518;
+    }
+    eth->rxbds[CYGNUM_DEVS_ETH_MCFxxxx_RXBUFFERS - 1].ethbd_flags = HAL_MCFxxxx_ETHx_RXBD_E | HAL_MCFxxxx_ETHx_RXBD_W;
+
+    // None of the rx buffers should be in the cache so no need to invalidate.
+    // The rxbds must be written to memory. The txbds will be taken care of
+    // by the transmit code when there are packets to transmit.
+#ifdef HAL_DCACHE_STORE
+    HAL_DCACHE_STORE(&(eth->rxbds[0]), CYGNUM_DEVS_ETH_MCFxxxx_RXBUFFERS * sizeof(hal_mcfxxxx_eth_buffer_descriptor));
+#endif
+    
+#if 0
+    // Interact with the phy to see if the RCR DRT and X_CTRNL FDEN
+    // bits should be set. Default to half-duplex.
+    // NOTE: this should be conditional on the presence of a phy, and the
+    // platform should be able to override the default phy settings. It
+    // is also necessary to search the phy address space.
+    {
+        int     addr    = -1;
+        int     i;
+        int     reg;
+
+        // Find the phy's address. If the phy is not present the id register
+        // will read as 0x00FFFF.
+        for (i = 0; i < 32; i++) {
+            reg = mcfxxxx_eth_read_phy(i, 2);
+            if ((-1 != reg) && (0x0000FFFF != reg)) {
+                addr = i;
+                break;
+            }
+        }
+        
+        if (-1 != addr) {
+            // Read the status register, especially the link bit
+            reg = mcfxxxx_eth_read_phy(addr, 1);
+            if (reg & 0x04) {
+                // The link is up. What was negotiated with the partner?
+                reg = mcfxxxx_eth_read_phy(addr, 5);
+                if (reg & 0x0140) {
+                    // Full duplex, either 100 or 10. Clear the RCR DRT bit, set
+                    // the TCR FDEN bit
+                    cyg_uint32  rcr;
+                    READ32(RCR(eth), rcr);
+                    WRITE32(RCR(eth), rcr & ~ HAL_MCFxxxx_ETHx_RCR_DRT);
+                    WRITE32(TCR(eth), HAL_MCFxxxx_ETHx_TCR_FDEN);
+                }
+            }
+        }
+    }
+#endif
+
+    // The RCR register can have additional bits set on some processors,
+    // e.g. on the mcf5282 it contains the maximum frame length. Change it only
+    // by read/modify/write.
+    READ32(RCR(eth), rcr);
+#ifdef CYGPKG_NET    
+    if (flags & IFF_PROMISC) {
+        rcr |= HAL_MCFxxxx_ETHx_RCR_PROM;
+    } else {
+        rcr &= ~HAL_MCFxxxx_ETHx_RCR_PROM;
+    }
+#else
+    rcr &= ~HAL_MCFxxxx_ETHx_RCR_PROM;
+#endif
+    WRITE32(RCR(eth), rcr);
+
+    // The hardware is now ready
+    // The documentation says that setting R_DES_ACTIVE could also be done before asserting
+    // ETHER_EN, but that appears incorrect: you'll never get any packets.
+    WRITE32(ECR(eth),  HAL_MCFxxxx_ETHx_ECR_ETHER_EN);
+    WRITE32(RDAR(eth), HAL_MCFxxxx_ETHx_RDAR_R_DES_ACTIVE);
+    WRITE32(TDAR(eth), HAL_MCFxxxx_ETHx_TDAR_X_DES_ACTIVE);
+    
+    eth->started = 1;
+
+    DIAG(1, "done");
+}
+
+// Stopping can be done simply by disabling the device, which causes
+// all transmits and receives to be aborted. There should be no further
+// interrupts.
+static void
+mcfxxxx_eth_stop(struct eth_drv_sc* sc)
+{
+    mcfxxxx_eth*    eth = (mcfxxxx_eth*) sc->driver_private;
+
+    DIAG(1, "entry");
+    
+    eth->started    = 0;
+    WRITE32(ECR(eth), 0);
+    eth->tx_done    = 0;
+    eth->rx_rdy     = 0;
+    if (! eth->tx_can_send) {
+        (*sc->funs->eth_drv->tx_done)(sc, eth->tx_key, 1);
+        eth->tx_can_send    = 1;
+    }
+
+    DIAG(1, "done");
+}


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]