[rtems commit] powerpc/haleakala: Add network driver

Joel Sherril joel at rtems.org
Fri Nov 21 19:47:56 UTC 2014


Module:    rtems
Branch:    master
Commit:    502609c80d5f8c5ec852845c4632c905811b3ac3
Changeset: http://git.rtems.org/rtems/commit/?id=502609c80d5f8c5ec852845c4632c905811b3ac3

Author:    Nigel Spon <nigel at adi.co.nz>
Date:      Fri Nov 21 11:15:22 2014 -0600

powerpc/haleakala: Add network driver

close 1405

---

 c/src/lib/libbsp/powerpc/haleakala/Makefile.am     |   15 +-
 c/src/lib/libbsp/powerpc/haleakala/configure.ac    |    1 -
 .../lib/libbsp/powerpc/haleakala/include/mmu_405.h |   77 ++
 c/src/lib/libbsp/powerpc/haleakala/mmu/mmu_405.c   |  281 +++++
 .../lib/libbsp/powerpc/haleakala/mmu/mmu_405asm.S  |   83 ++
 .../lib/libbsp/powerpc/haleakala/network/network.c | 1243 ++++++++++++++++++++
 c/src/lib/libbsp/powerpc/haleakala/preinstall.am   |    4 +
 c/src/lib/libcpu/powerpc/ppc403/include/ppc405ex.h |  108 +-
 c/src/lib/libcpu/powerpc/ppc403/include/ppc405gp.h |   75 +-
 9 files changed, 1845 insertions(+), 42 deletions(-)

diff --git a/c/src/lib/libbsp/powerpc/haleakala/Makefile.am b/c/src/lib/libbsp/powerpc/haleakala/Makefile.am
index c56a228..1c91d05 100644
--- a/c/src/lib/libbsp/powerpc/haleakala/Makefile.am
+++ b/c/src/lib/libbsp/powerpc/haleakala/Makefile.am
@@ -7,6 +7,7 @@ include_bspdir = $(includedir)/bsp
 dist_project_lib_DATA = bsp_specs
 
 include_HEADERS = include/bsp.h
+include_HEADERS += include/mmu_405.h
 include_HEADERS += ../../shared/include/tm27.h
 
 nodist_include_HEADERS = include/bspopts.h
@@ -30,7 +31,7 @@ libbsp_a_SOURCES += ../../shared/bspclean.c ../../shared/bsplibc.c \
     startup/bspstart.c ../../shared/bootcard.c \
     ../../shared/bsppredriverhook.c ../../shared/bspgetworkarea.c \
     ../../shared/bsppretaskinghook.c ../../shared/sbrk.c \
-    ../../shared/gnatinstallhandler.c
+    ../../shared/gnatinstallhandler.c mmu/mmu_405.c mmu/mmu_405asm.S 
 
 # dlentry
 libbsp_a_SOURCES += dlentry/dlentry.S
@@ -47,6 +48,14 @@ include_bsp_HEADERS += irq/irq.h \
 # irq
 libbsp_a_SOURCES += irq/irq_init.c irq/irq.c
 
+if HAS_NETWORKING
+network_CPPFLAGS = -D__INSIDE_RTEMS_BSD_TCPIP_STACK__
+noinst_PROGRAMS = network.rel
+network_rel_SOURCES = network/network.c 
+network_rel_CPPFLAGS = $(AM_CPPFLAGS) $(network_CPPFLAGS)
+network_rel_LDFLAGS = $(RTEMS_RELLDFLAGS)
+endif
+
 libbsp_a_LIBADD = ../../../libcpu/@RTEMS_CPU@/@exceptions@/rtems-cpu.rel \
     ../../../libcpu/@RTEMS_CPU@/@exceptions@/exc_bspsupport.rel \
     ../../../libcpu/@RTEMS_CPU@/@exceptions@/irq_bspsupport.rel \
@@ -55,5 +64,9 @@ libbsp_a_LIBADD = ../../../libcpu/@RTEMS_CPU@/@exceptions@/rtems-cpu.rel \
     ../../../libcpu/@RTEMS_CPU@/ppc403/clock.rel \
     ../../../libcpu/@RTEMS_CPU@/ppc403/timer.rel
 
+if HAS_NETWORKING
+libbsp_a_LIBADD += network.rel
+endif
+
 include $(srcdir)/preinstall.am
 include $(top_srcdir)/../../../../automake/local.am
diff --git a/c/src/lib/libbsp/powerpc/haleakala/configure.ac b/c/src/lib/libbsp/powerpc/haleakala/configure.ac
index 81c418f..bc66c3e 100644
--- a/c/src/lib/libbsp/powerpc/haleakala/configure.ac
+++ b/c/src/lib/libbsp/powerpc/haleakala/configure.ac
@@ -14,7 +14,6 @@ RTEMS_CANONICALIZE_TOOLS
 RTEMS_PROG_CCAS
 
 RTEMS_CHECK_NETWORKING
-
 AM_CONDITIONAL(HAS_NETWORKING,test "$HAS_NETWORKING" = "yes")
 
 RTEMS_BSPOPTS_SET([PPC_USE_SPRG],[*],[1])
diff --git a/c/src/lib/libbsp/powerpc/haleakala/include/mmu_405.h b/c/src/lib/libbsp/powerpc/haleakala/include/mmu_405.h
new file mode 100644
index 0000000..e11cfa7
--- /dev/null
+++ b/c/src/lib/libbsp/powerpc/haleakala/include/mmu_405.h
@@ -0,0 +1,77 @@
+#ifndef _mmu_405_h
+#define _mmu_405_h
+
+/* 
+   Simple interface to the PowerPC 405 MMU
+   
+   The intention here is just to allow the MMU to be used to define cacheability and
+   read/write/execute permissions in a simple enough way to fit entirely into the
+   64-entry TLB cache.
+   
+   This code does not do address relocation and does not generate any MMU-related interrupts.
+   
+   The process ID support is there for a possible future extension where RTEMS supports
+   setting the process ID on task switches, which allows per-process stack protection
+    
+   This code will call fatal_error() if your add_space() calls overrun the 64 entries
+
+   Michael Hamel ADInstruments 2008
+
+*/
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "stdint.h"
+
+enum {
+	kAllProcessIDs = 0
+};
+
+typedef enum MMUAccessType {
+	executable,
+	readOnlyData,
+	readOnlyNoCache,
+	readWriteData,
+	readWriteNoCache,
+	readWriteExecutable
+} MMUAccessType;
+
+/* Initialise and clear the MMU */
+void		mmu_initialise();
+
+/* Turn on/off data access translation */
+bool		mmu_enable_data(bool enable);
+
+/* Turn on instruction translation */
+bool		mmu_enable_code(bool enable);
+
+/* Define properties for an area of memory (must be 1K-aligned) */
+void		mmu_add_space(uint32_t startAddr, uint32_t endAddr, MMUAccessType permissions, uint8_t processID);
+
+/* Delete a memory property definition */
+void		mmu_remove_space(uint32_t startAddr, uint32_t endAddr);
+
+/* Return number of TLB entries out of total in use */
+int			mmu_get_tlb_count();
+
+/* Allocate a new process ID and return it */
+uint8_t		mmu_new_processID();
+
+/* Free a process ID that has been in use */
+void		mmu_free_processID(uint8_t freeThis);
+
+/* Return the current process ID */
+uint8_t		mmu_current_processID();
+
+/* Change the process ID to ID and return the old value */
+uint8_t		mmu_set_processID(uint8_t toID);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif //_mmu_405.h
\ No newline at end of file
diff --git a/c/src/lib/libbsp/powerpc/haleakala/mmu/mmu_405.c b/c/src/lib/libbsp/powerpc/haleakala/mmu/mmu_405.c
new file mode 100644
index 0000000..a5f802e
--- /dev/null
+++ b/c/src/lib/libbsp/powerpc/haleakala/mmu/mmu_405.c
@@ -0,0 +1,281 @@
+/* 
+ *  Simple interface to the PowerPC 405 MMU
+ *
+ *   Michael Hamel ADInstruments 2008
+ *
+  */
+
+
+#include <bsp.h>
+#include <libcpu/powerpc-utility.h>
+#include "mmu_405.h"
+
+/* #define qLogTLB */
+/* #define qLogTLBDetails */
+
+
+/*--------------------------------- TLB handling ------------------------------------- */
+/* The following are in assembler in mmu_405asm.S  */
+extern  void MMU_GetTLBEntry(uint8_t index, uint32_t* tagword, uint32_t* dataword, uint8_t* pid);
+extern  void MMU_SetTLBEntry(uint8_t index, uint32_t hiword, uint32_t loword, uint8_t pid);
+extern  void MMU_ClearTLBs();
+extern  int16_t MMU_FindTLBEntry(uint32_t address);
+
+
+enum { kNTLBs = 64 };		/* for 403GCX and 405 */
+
+static bool		sFreeTLBs[kNTLBs];
+static uint8_t	sLastIndex = 0;
+static int		sNInUse = 0;
+
+static void MMUFault(const char* what)
+/* Used for all setup faults; these can't really be ignored */
+{
+	printk("\n>>>MMU fatal error %s\n",what);
+	rtems_fatal_error_occurred(RTEMS_INTERNAL_ERROR);
+}
+
+static uint8_t AllocTLB()
+{
+	uint8_t index;
+	
+	index = sLastIndex;
+	do {
+		index++;
+		if (index == kNTLBs)
+			index = 0;
+		if (index == sLastIndex)
+			MMUFault("TLB table full");
+	} while (! sFreeTLBs[index]);
+	sFreeTLBs[index] = false;
+	sLastIndex = index;
+	sNInUse++;
+	return index;
+}
+
+static void FreeTLB(uint8_t index)
+{
+	MMU_SetTLBEntry(index,0,0,0);
+	sFreeTLBs[index] = true;
+	sLastIndex = index-1;
+	sNInUse--;
+}
+
+
+/*---------------------------- MMU operations ---------------------------------- */
+
+int DataMissException(BSP_Exception_frame *f, unsigned int vector);
+int InstructionMissException(BSP_Exception_frame *f, unsigned int vector);
+int InstructionFetchException(BSP_Exception_frame *f, unsigned int vector);
+
+void
+mmu_initialise()
+/* Clear the TLBs and set up exception handlers for the MMU miss handlers */
+{
+	int i;
+	
+	MMU_ClearTLBs();
+	for (i=0; i<kNTLBs; i++) {
+		sFreeTLBs[i] = true;
+		MMU_SetTLBEntry(i,0,0,0xFF);
+	}
+	ppc_exc_set_handler(ASM_ISI_VECTOR ,InstructionFetchException);
+	ppc_exc_set_handler(ASM_BOOKE_ITLBMISS_VECTOR ,DataMissException);
+	ppc_exc_set_handler(ASM_BOOKE_DTLBMISS_VECTOR ,InstructionMissException);
+}
+
+static void
+MakeTLBEntries(uint32_t startAt, uint32_t nBytes, bool EX, bool WR, bool I, uint8_t PID)
+{
+	uint32_t mask, options, tagWord, dataWord;
+	uint8_t	 index, sizeCode, pid;
+	
+	if ((startAt & 0x3FF) != 0)
+		MMUFault("TLB entry not on 1K boundary");
+	if ((nBytes & 0x3FF) != 0)
+		MMUFault("TLB size not on 1K boundary");
+		
+	options = 0;
+	if (EX) options += 0x200;
+	if (WR) options += 0x100;
+	if (I) options += 5;
+	
+	#ifdef qLogTLB
+		printk("TLB: make entries for $%X bytes from $%X..$%X PID %d",nBytes, startAt, startAt+nBytes-1, PID);
+		if (EX) printk(" EX");
+		if (WR) printk(" WR");
+		if (I) printk(" I");
+		printk("\n");
+	#endif
+	
+	while (nBytes > 0) {
+		/* Find the largest block we can base on this address */
+		mask = 0x3FF;
+		sizeCode = 0;
+		while (mask < nBytes && ((startAt & mask)==0) && sizeCode < 8) {
+			mask = (mask<<2) + 3;
+			sizeCode++;
+		}
+		mask >>= 2;
+		sizeCode--;
+		
+		/* Make a TLB entry describing this, ZSEL=0 */
+		tagWord = startAt | (sizeCode<<7) | 0x40;
+		dataWord = startAt | options;
+		index = AllocTLB();
+		MMU_SetTLBEntry( index , tagWord, dataWord, PID);
+		
+		{
+			/* Paranoia: check that we can read that back... */
+			uint8_t tdex, oldpid;
+			
+			oldpid = mmu_current_processID();
+			mmu_set_processID(PID);
+			tdex = MMU_FindTLBEntry(startAt);
+			mmu_set_processID(oldpid);
+			
+			if (tdex != index) {
+				printk(" Add TLB %d: At %X for $%X sizecode %d tagWord $%X  ",index, startAt, mask+1,sizeCode,tagWord);
+				printk(" -- find failed, %d/%d!\n",tdex,index);
+				MMU_GetTLBEntry(index, &tagWord, &dataWord, &pid);
+				printk(" -- reads back $%X : $%X, PID %d\n",tagWord,dataWord,pid);
+			} else {
+				#ifdef qLogTLBDetails
+				printk(" Add TLB %d: At %X for $%X sizecode %d tagWord $%X\n",index, startAt, mask+1,sizeCode,tagWord);
+				#endif
+			}
+		}
+	
+		/* Subtract block from startAddr and nBytes */
+		mask++;		/* Convert to a byte count */
+		startAt += mask;
+		nBytes -= mask;
+	}
+	#ifdef qLogTLB
+		printk(" %d in use\n",sNInUse);
+	#endif
+}
+
+void
+mmu_remove_space(uint32_t startAt, uint32_t endAt)
+{
+	int16_t index;
+	int32_t size;
+	uint32_t tagword, dataword, nBytes;
+	uint8_t  pid, sCode;
+	
+	nBytes = endAt - startAt;
+	
+	#ifdef qLogTLB
+	printk("TLB: delete entries for $%X bytes from $%X\n",nBytes,startAt);
+	#endif
+	
+	while (nBytes > 0) {
+		index = MMU_FindTLBEntry( (uint32_t)startAt );
+		size = 1024;
+		if (index >= 0) {
+			MMU_GetTLBEntry(index, &tagword, &dataword, &pid);
+			if ((tagword & 0x40) == 0)
+				MMUFault("Undefine failed: redundant entries?");
+			if ((tagword & 0xFFFFFC00) != (uint32_t)startAt)
+				MMUFault("Undefine not on TLB boundary");
+			FreeTLB(index);
+			sCode = (tagword >> 7) & 7;
+			while (sCode > 0) {
+				size <<= 2;
+				sCode--;
+			}
+			#ifdef qLogTLBDetails
+			printk(" Free TLB %d: At %X for $%X\n",index, startAt, size);
+			#endif
+		}
+		startAt += size;
+		nBytes -= size;
+	}
+}
+
+void
+mmu_add_space(uint32_t startAddr, uint32_t endAddr, MMUAccessType permissions, uint8_t processID)
+/* Convert accesstype to write-enable, executable, and cache-inhibit bits */
+{
+	bool EX, WR, I;
+	
+	EX = false;
+	WR = false;
+	I = false;
+	switch (permissions) {
+		case executable 		: EX = true;  break;
+		case readOnlyData 		: break;
+		case readOnlyNoCache 	: I = true; break;
+		case readWriteData 		: WR = true; break;
+		case readWriteNoCache   : WR = true; I= true; break;
+		case readWriteExecutable: WR = true; EX = true; break;
+	}
+	MakeTLBEntries( (uint32_t)startAddr, (uint32_t)(endAddr-startAddr+1), EX, WR, I, processID);
+}
+
+int
+mmu_get_tlb_count()
+{
+	return sNInUse;
+}
+
+/*---------------------------- CPU process ID handling ----------------------------------
+ * Really dumb system where we just hand out sequential numbers and eventually fail
+ * As long as we only use 8-9 processes this isn't a problem */
+
+static uint8_t sNextPID = 1;
+
+#define SPR_PID		0x3B1
+
+uint8_t	mmu_new_processID()
+{
+	return sNextPID++;
+}
+
+void mmu_free_processID(uint8_t freeThis)
+{
+}
+
+uint8_t mmu_current_processID()
+{
+	return PPC_SPECIAL_PURPOSE_REGISTER(SPR_PID);
+}
+
+uint8_t mmu_set_processID(uint8_t newID)
+{
+	uint8_t prev = mmu_current_processID();
+	PPC_SET_SPECIAL_PURPOSE_REGISTER(SPR_PID,newID);
+	return prev;
+}
+
+
+/* ------------------ Fault handlers ------------------ */
+
+#define SPR_ESR		0x3D4
+#define SPR_DEAR	0x3D5
+
+enum { kESR_DST = 0x00800000 };
+
+int DataMissException(BSP_Exception_frame *f, unsigned int vector)
+{
+	uint32_t addr, excSyn;
+	
+	addr = PPC_SPECIAL_PURPOSE_REGISTER(SPR_DEAR);
+	excSyn  = PPC_SPECIAL_PURPOSE_REGISTER(SPR_ESR);
+	if (excSyn & kESR_DST) printk("\n---Data write to $%X attempted at $%X\n",addr,f->EXC_SRR0);
+					  else printk("\n---Data read from $%X attempted at $%X\n",addr,f->EXC_SRR0);
+	return -1;
+}
+
+int InstructionMissException(BSP_Exception_frame *f, unsigned int vector)
+{
+	printk("\n---Instruction fetch attempted from $%X, no TLB exists\n",f->EXC_SRR0);
+	return -1;
+}
+
+int InstructionFetchException(BSP_Exception_frame *f, unsigned int vector)
+{
+	printk("\n---Instruction fetch attempted from $%X, TLB is no-execute\n",f->EXC_SRR0);
+	return -1;
+}
diff --git a/c/src/lib/libbsp/powerpc/haleakala/mmu/mmu_405asm.S b/c/src/lib/libbsp/powerpc/haleakala/mmu/mmu_405asm.S
new file mode 100644
index 0000000..5fef5fb
--- /dev/null
+++ b/c/src/lib/libbsp/powerpc/haleakala/mmu/mmu_405asm.S
@@ -0,0 +1,83 @@
+/*
+
+Low-level interface to the PPC405 MMU
+
+M.Hamel ADInstruments 2008
+
+*/
+
+#include <rtems/asm.h>
+
+/* Useful MMU SPR values */
+
+#define SPR_ZPR		0x3B0
+#define SPR_PID		0x3B1
+
+			.text
+
+/* void MMU_ClearTLBs(); */
+			PUBLIC_VAR(MMU_ClearTLBs)
+SYM (MMU_ClearTLBs):
+			tlbia
+			isync
+			lis		r3,0x5555		// *** Gratuitous fiddle of ZPR to 0101010101 to take it out of 
+			mtspr	SPR_ZPR,r3		// the picture
+			blr
+			
+/* void MMU_SetTLBEntry(UInt8 index, UInt32 tagword, UInt32 dataword, UInt8 SPR_PID) */
+			PUBLIC_VAR(MMU_SetTLBEntry)
+SYM (MMU_SetTLBEntry):
+			mfspr	r7,SPR_PID		// Save the current SPR_PID
+			mtspr	SPR_PID,r6		// Write to SPR_PID
+			tlbwehi	r4,r3			// Write hiword
+			mtspr	SPR_PID,r7		// Restore the SPR_PID
+			tlbwelo	r5,r3			// Write loword
+			isync
+			blr
+
+/* void MMU_GetTLBEntry(UInt8 index, UInt32& tagword, UInt32& dataword, UInt8& SPR_PID) */
+			PUBLIC_VAR(MMU_GetTLBEntry)
+SYM (MMU_GetTLBEntry):		
+			mfspr	r7,SPR_PID		// Save the current SPR_PID
+			tlbrehi	r8,r3			// Read hiword & SPR_PID
+			mfspr	r9,SPR_PID		// Copy the SPR_PID
+			mtspr	SPR_PID,r7		// Restore original SPR_PID so we can proceed
+			stw		r8,0(r4)		// Write to r4 pointer
+			stb		r9,0(r6)		// Write to r6 pointer
+			tlbrelo	r8,r3			// Read loword
+			stw		r8,0(r5)		// Write to r5 pointer
+			blr
+			
+/* SInt16 MMU_FindTLBEntry(UInt32 address) */
+/* Returns index of covering TLB entry (0..63), or -1 if there isn't one */
+			PUBLIC_VAR(MMU_FindTLBEntry)
+SYM (MMU_FindTLBEntry):		
+			tlbsx.	r3,0,r3
+			beqlr
+			li		r3,0xFFFFFFFF
+			blr
+
+/* bool		mmu_enable_code(bool enable); */
+			PUBLIC_VAR(mmu_enable_code)
+SYM (mmu_enable_code):	
+			li		r5,0x20	// IR bit
+			b		msrbits
+			
+/* bool		mmu_enable_data(bool enable); */
+			PUBLIC_VAR(mmu_enable_data)
+SYM (mmu_enable_data):	
+			li		r5,0x10		// DR bit
+msrbits:	cmpwi	r3,0			// Common code: parameter 0?
+			mfmsr	r4				// r4 = MSR state
+			beq		clrBit
+			or		r6,r4,r5		// If 1, r6 = MSR with bit set
+			b		setmsr
+clrBit:		andc	r6,r4,r5		// If 0 r6 = MSR with bit clear
+setmsr:		mtmsr	r6				// Write new MSR
+			and.	r3,r4,r5		// Result = old MSR bit
+			beqlr						// If zero return zero
+			li		r3,0xFF		// If nonzero return byte -1
+			blr
+	
+			
+			
diff --git a/c/src/lib/libbsp/powerpc/haleakala/network/network.c b/c/src/lib/libbsp/powerpc/haleakala/network/network.c
new file mode 100644
index 0000000..03cce2a
--- /dev/null
+++ b/c/src/lib/libbsp/powerpc/haleakala/network/network.c
@@ -0,0 +1,1243 @@
+/*
+ *  network.c
+ *  RTEMS_490
+ *
+ *  Created by Michael Hamel on 18/11/08.
+ *
+ * This code is for the PPC405EX, 405EXr on the Haleakala board with an 
+ * 88E1111 PHY. 
+ * Its has some adaptations for the 405GP, and 405GPr (untested).
+ *
+ * It should handle dual interfaces correctly, but has not been tested.
+ *
+ */
+
+#include <bsp.h>
+#include <stdio.h>
+#include <errno.h>
+#include <rtems/error.h>
+#include <rtems/rtems_bsdnet.h>
+#include <rtems/rtems_mii_ioctl.h>
+
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+
+#include <net/if.h>
+
+#include <netinet/in.h>
+
+#include <netinet/if_ether.h>
+#include <bsp/irq.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include <ppc4xx/ppc405gp.h>
+#include <ppc4xx/ppc405ex.h>
+
+#define qDebug 		/* General printf debugging */
+/* #define qMultiDebug */	/* Debugging for the multicast hardware filter */
+
+/*---------------------------- Hardware definitions -------------------------- */
+
+/* PHY addresses for Kilauea & Haleakala; defined by hardware */
+enum {
+	kPHY0 = 1,
+	kPHY1 = 2,
+	kMaxEMACs = 2
+};
+
+enum {
+	kMaxRxBuffers = 256,
+	kNXmtDescriptors = 256,	/* May as well use all of them */
+	kNoXmtBusy = 666		/* Arbitrary flag value outside 0..kNXmtDescriptors */
+};
+
+
+/*----------------------- Local variables for the driver -------------------------- */
+
+typedef struct MALDescriptor {
+	uint16_t ctrlBits;
+	uint16_t adrDataSize;	/* 4 bits of high address, 12 bits of length */
+	uint8_t* ptr;
+} MALDescriptor;
+
+typedef struct EMACLocals {
+    struct arpcom  arpcom;
+
+	/* Pointer to memory-mapped hardware */
+	volatile EthernetRegisters_GP*  EMAC;
+	
+    /* Transmit and receive task references */
+    rtems_id		rxDaemonTid;
+	rtems_id		txDaemonTid;
+	int				nRxBuffers;
+	int				xmtFreeIndex;
+	int				xmtBusyIndex;
+	MALDescriptor*	xmtDescTable;
+	MALDescriptor*  rcvDescTable;
+	
+	struct mbuf* rxMBufs[kMaxRxBuffers];
+	struct mbuf* txMBufs[kNXmtDescriptors];
+	
+	int			phyAddr,	/* PHY address */
+				phyState,	/* Last link state */
+				phyOUI,		/* Cached PHY type info */
+				phyModel,
+				phyRev;
+
+	/* Statistics */
+	uint32_t   rxInterrupts;
+	uint32_t   rxOverrun;
+	uint32_t   rxRunt;
+	uint32_t   rxBadCRC;
+	uint32_t   rxNonOctet;
+	uint32_t   rxGiant;
+	
+	uint32_t	txInterrupts;
+	
+	uint32_t	txLostCarrier;
+	uint32_t	txDeferred;
+	uint32_t	txOneCollision;
+	uint32_t	txMultiCollision;
+	uint32_t	txTooManyCollision;
+	uint32_t	txLateCollision;
+	uint32_t	txUnderrun;
+	uint32_t	txPoorSignal;
+} EMACLocals;
+
+
+EMACLocals gEmacs[kMaxEMACs];
+
+/*----------------------------------- Globals --------------------------------------*/
+
+/* 
+   Pointers to the buffer descriptor tables used by the MAL. Tricky because they are both
+   read and written to by the MAL, which is unaware of the CPU data cache. As four 8-byte 
+   descriptors fit into a single cache line this makes managing them in cached memory difficult. 
+   Best solution is to label them as uncached using the MMU. This code assumes an appropriate 
+   sized block stating at _enet_bdesc_base has been reserved by linkcmds and has been set up
+   with uncached MMU attrributes in bspstart.c */
+
+LINKER_SYMBOL(_enet_bdesc_start);		/* start of buffer descriptor space, from linkcmds */
+LINKER_SYMBOL(_enet_bdesc_end);			/* top limit, from linkcmds */
+
+static MALDescriptor* gTx0Descs = NULL;
+static MALDescriptor* gRx0Descs = NULL;
+static MALDescriptor* gTx1Descs = NULL;	
+static MALDescriptor* gRx1Descs = NULL;
+
+/*------------------------------------------------------------*/
+
+
+/*
+ * RTEMS event used by interrupt handler to signal driver tasks.
+ * This must not be any of the events used by the network task synchronization.
+ */
+#define INTERRUPT_EVENT			RTEMS_EVENT_1
+
+/*
+ * RTEMS event used to start transmit daemon.
+ * This must not be the same as INTERRUPT_EVENT.
+ */
+#define START_TRANSMIT_EVENT	RTEMS_EVENT_2
+
+#define _sync    __asm__ volatile ("sync\n"::)
+
+#define	kCacheLineMask	(PPC_CACHE_ALIGNMENT - 1)
+
+
+/*----------------------- IRQ handler glue -----------------------*/
+
+static void InstallIRQHandler(rtems_irq_number id,
+							  rtems_irq_hdl handler,
+							  rtems_irq_enable turnOn,
+							  rtems_irq_disable turnOff)
+{
+	rtems_irq_connect_data params;
+	
+	params.name = id;
+	params.hdl = handler;
+	params.on = turnOn;
+	params.off = turnOff;
+	params.isOn = NULL;
+	params.handle = NULL;
+	if (! BSP_install_rtems_irq_handler(&params))
+		rtems_panic ("Can't install interrupt handler");
+}
+
+static void
+NoAction(const rtems_irq_connect_data* unused)
+{
+	/* printf("NoAction %d\n",unused->name); */
+}
+
+
+/*------------------------ PHY interface -------------------------- */
+/* This code recognises and works with the 88E1111 only. Other PHYs 
+   will require appropriate adaptations to this code */
+   
+enum {
+	kPHYControl = 0,
+	 kPHYReset = 0x8000,
+	kPHYStatus = 1,
+	 kPHYLinkStatus = 0x0004,
+	kPHYID1 = 2,
+	kPHYID2 = 3,
+	kPHYAutoNegExp = 6,
+	kPHY1000BaseTCtl = 9,
+	kPHYExtStatus = 15,
+	
+	/* 88E1111 identification */
+	kMarvellOUI = 0x5043,
+	k88E1111Part = 0x0C,
+
+	/* 88E1111 register addresses */
+	k8PHYSpecStatus = 17,
+		k8PHYSpeedShift = 14,
+		k8PHYDuplex    = 0x2000,
+		k8PHYResolved  = 0x0800,
+		k8PHYLinkUp    = 0x0400,
+	k8IntStatus = 19,
+	k8IntEnable = 18,
+	 k8AutoNegComplete = 0x0800,
+	 k8LinkStateChanged = 0x0400,
+	k8ExtCtlReg = 20,
+		k8RcvTimingDelay = 0x0080,
+		k8XmtTimingDelay = 0x0002,
+		k8XmtEnable      = 0x0001,
+	k8LEDCtlReg = 24,
+	k8ExtStatusReg = 27,
+};
+
+
+static uint16_t ReadPHY(EMACLocals* ep, uint8_t reg)
+{
+	int n = 0;
+	uint32_t t;
+	
+	reg &= 0x1F;
+	
+	/* 405EX-specific! */
+	while ((ep->EMAC->STAcontrol & keSTARun) != 0)
+		{ ; }
+	ep->EMAC->STAcontrol = keSTADirectRd + (ep->phyAddr<<5) + reg;
+	ep->EMAC->STAcontrol |= keSTARun;
+	/* Wait for the read to complete, should take ~25usec */
+	do {
+		t = ep->EMAC->STAcontrol;
+		if (++n > 200000) 
+			rtems_panic("PHY read timed out");
+	} while ((t & keSTARun) != 0);
+	
+	if (t & kSTAErr) 
+		rtems_panic("PHY read failed");
+	return t >> 16;
+}
+
+static void WritePHY(EMACLocals* ep, uint8_t reg, uint16_t value)
+{
+
+	reg &= 0x1F;
+	
+	/* 405EX-specific */
+	while ((ep->EMAC->STAcontrol & keSTARun) != 0)
+		{ ; }
+	ep->EMAC->STAcontrol = (value<<16) | keSTADirectWr | (ep->phyAddr<<5) | reg;
+	ep->EMAC->STAcontrol |= keSTARun;
+}
+
+static void ResetPHY(EMACLocals* ep)
+{
+	int n;
+	
+	n = ReadPHY(ep, kPHYControl);
+	n |= kPHYReset;
+	WritePHY(ep, kPHYControl, n);
+	do {
+		rtems_task_wake_after( (rtems_bsdnet_ticks_per_second/20) + 1 );
+		n = ReadPHY(ep, kPHYControl);
+	} while ((n & kPHYReset)!=0);
+}
+
+enum {
+	kELinkUp = 0x80,
+	kELinkFullDuplex = 0x40,
+	kELinkSpeed10 = 0,
+	kELinkSpeed100 = 1,
+	kELinkSpeed1000 = 2,
+	kELinkSpeedMask = 3
+};
+
+static int GetPHYLinkState(EMACLocals* ep)
+/* Return link state (up/speed/duplex) as a set of flags */
+{
+	int state, result;
+	
+	/* if (ep->phyOUI==kMarvellOUI) */
+	result = 0;
+	state = ReadPHY(ep,k8PHYSpecStatus);
+	if ((state & k8PHYLinkUp) && (state & k8PHYResolved)) {
+		result |= kELinkUp;
+		if (state & k8PHYDuplex) result |= kELinkFullDuplex;
+		result |= ((state >> k8PHYSpeedShift) & 3);
+	}
+	return result;
+}
+
+/*---------------------- PHY setup ------------------------*/
+
+
+static void InitPHY(EMACLocals* ep)
+{
+	int id,id2,n;
+	
+	id = ReadPHY(ep,kPHYID1);
+	id2 = ReadPHY(ep,kPHYID2);
+	ep->phyOUI = (id<<6) + (id2>>10);
+	ep->phyModel = (id2>>4) & 0x1F;
+	ep->phyRev = id2 & 0xF;
+	
+	#ifdef qDebug
+		printf("PHY %d maker $%X model %d revision %d\n",ep->phyAddr,ep->phyOUI,ep->phyModel,ep->phyRev); 
+	#endif
+	
+	/* Test for PHYs that we understand; insert new PHY types initialisation here */
+	if (ep->phyOUI == kMarvellOUI || ep->phyModel == k88E1111Part) {
+		/* 88E111-specific: Enable RxTx timing control, enable transmitter */
+		n = ReadPHY(ep, k8ExtCtlReg);
+		n |= k8RcvTimingDelay + k8XmtTimingDelay + k8XmtEnable;		
+		WritePHY(ep, k8ExtCtlReg, n);
+		
+		/* Set LED mode; Haleakala has LINK10 and TX LEDs only. Set up to do 100/1000 and link up/active*/
+		WritePHY(ep, k8LEDCtlReg, 0x4109);
+		
+		/* Need to do a reset after fiddling with registers*/
+		ResetPHY(ep);
+	} else
+		rtems_panic("Unknown PHY type");
+}
+
+
+/*--------------------- Interrupt handlers for the MAL ----------------------------- */
+
+static void
+MALTXDone_handler(rtems_irq_hdl_param param)
+{
+	int n;
+	
+	n = PPC_DEVICE_CONTROL_REGISTER(MAL0_TXEOBISR);
+	if (n & kMALChannel0) {
+		gEmacs[0].txInterrupts++;
+		rtems_event_send (gEmacs[0].txDaemonTid, INTERRUPT_EVENT);
+	}
+	if (n & kMALChannel1) {
+		gEmacs[1].txInterrupts++;
+		rtems_event_send (gEmacs[1].txDaemonTid, INTERRUPT_EVENT);
+	}
+	PPC_SET_DEVICE_CONTROL_REGISTER(MAL0_TXEOBISR,n);
+}
+
+static void
+MALRXDone_handler (rtems_irq_hdl_param param)
+{
+	int n;
+	
+	n = PPC_DEVICE_CONTROL_REGISTER(MAL0_RXEOBISR);
+	if (n & kMALChannel0) {
+		gEmacs[0].rxInterrupts++;
+		rtems_event_send (gEmacs[0].rxDaemonTid, INTERRUPT_EVENT);
+	}
+	if (n & kMALChannel1) {
+		gEmacs[1].rxInterrupts++;
+		rtems_event_send (gEmacs[1].rxDaemonTid, INTERRUPT_EVENT);
+	}
+	PPC_SET_DEVICE_CONTROL_REGISTER(MAL0_RXEOBISR,n);	/* Write back to clear interrupt */
+}
+
+/* These handlers are useful for debugging, but we don't actually need to process these interrupts */
+
+static void
+MALErr_handler (rtems_irq_hdl_param param)
+{
+	uint32_t errCause;
+	
+	errCause = PPC_DEVICE_CONTROL_REGISTER(MAL0_ESR);
+	/* Clear the error */
+	PPC_SET_DEVICE_CONTROL_REGISTER(MAL0_ESR,errCause);
+}
+	
+static void
+EMAC0Err_handler (rtems_irq_hdl_param param)
+{
+	uint32_t errCause;
+	
+	errCause = gEmacs[0].EMAC->intStatus;
+	/* Clear error by writing back */
+	gEmacs[0].EMAC->intStatus = errCause;
+}
+
+static void
+EMAC1Err_handler (rtems_irq_hdl_param param)
+{
+	uint32_t errCause;
+	
+	errCause = gEmacs[1].EMAC->intStatus;
+	/* Clear error by writing back */
+	gEmacs[1].EMAC->intStatus = errCause;
+}
+
+
+/*--------------------- Low-level hardware initialisation ----------------------------- */
+
+
+
+static void
+mal_initialise(void)
+{	
+	uint32_t bdescbase;
+	int nBytes, ntables;
+	
+	/*------------------- Initialise the MAL for both channels ---------------------- */
+			
+	PPC_SET_DEVICE_CONTROL_REGISTER(MAL0_CFG,kMALReset);
+	PPC_SET_DEVICE_CONTROL_REGISTER(MAL0_TXCARR, kMALChannel0 | kMALChannel1);
+	PPC_SET_DEVICE_CONTROL_REGISTER(MAL0_RXCARR, kMALChannel0 | kMALChannel1);
+
+	/* Acquire MAL interrupts */
+	InstallIRQHandler(BSP_UIC_MALTXEOB, MALTXDone_handler, NoAction, NoAction);
+	InstallIRQHandler(BSP_UIC_MALRXEOB, MALRXDone_handler, NoAction, NoAction);
+	InstallIRQHandler(BSP_UIC_MALSERR, MALErr_handler, NoAction, NoAction);
+		
+	/* Set up the buffer descriptor tables */
+	bdescbase = (uint32_t)(_enet_bdesc_start);
+	nBytes = sizeof(MALDescriptor) * 256;
+	ntables = 4;
+	if (get_ppc_cpu_type() != PPC_405EX) {
+		/* The 405GP/GPr requires table bases to be 4K-aligned and can use two tx channels on one EMAC */
+		nBytes = (nBytes + 0x0FFF) & ~0x0FFF;
+		bdescbase = (bdescbase + 0x0FFF) & ~0x0FFF;
+		ntables = 3;
+	}
+	
+	/* printf("Buffer descriptors at $%X..$%X, code from $%X\n",bdescbase, bdescbase + nBytes*ntables - 1,(uint32_t)&_text_start); */
+	
+	/* Check that we have been given enough space and the buffers don't run past the enet_bdesc_end address */
+	if (bdescbase + nBytes*ntables > (uint32_t)_enet_bdesc_end)
+		rtems_panic("Ethernet descriptor space insufficient!");
+		
+	gTx0Descs = (MALDescriptor*)bdescbase;
+	gTx1Descs = (MALDescriptor*)(bdescbase + nBytes);
+	gRx0Descs = (MALDescriptor*)(bdescbase + nBytes*2);
+	/* Clear the buffer descriptor tables */
+	memset(gTx0Descs, 0, sizeof(MALDescriptor)*256);
+	memset(gTx1Descs, 0, sizeof(MALDescriptor)*256);
+	memset(gRx0Descs, 0, sizeof(MALDescriptor)*256);
+	if (get_ppc_cpu_type() == PPC_405EX) {
+		gRx1Descs = (MALDescriptor*)(bdescbase + nBytes*3);
+		memset(gRx1Descs, 0, sizeof(MALDescriptor)*256);
+	}
+	
+	/* Set up the MAL registers */
+	PPC_SET_DEVICE_CONTROL_REGISTER(MAL0_TXCTP0R,gTx0Descs);
+	PPC_SET_DEVICE_CONTROL_REGISTER(MAL0_TXCTP1R,gTx1Descs);
+	PPC_SET_DEVICE_CONTROL_REGISTER(MAL0_RXCTP0R,gRx0Descs);
+	PPC_SET_DEVICE_CONTROL_REGISTER(MAL0_RCBS0, (MCLBYTES-16)>>4);		/* The hardware writes directly to the mbuf clusters, so it can write MCLBYTES */
+	if (get_ppc_cpu_type() == PPC_405EX) {
+		PPC_SET_DEVICE_CONTROL_REGISTER(MAL0_CFG,kMALMedHiPriority + keMALRdMaxBurst32 + keMALWrMedHiPriority + keMALWrMaxBurst32 +
+											 kMALLocksOPB + kMALLocksErrs + kMALCanBurst);
+		PPC_SET_DEVICE_CONTROL_REGISTER(MAL0_RXCTP1R,gRx1Descs);
+		PPC_SET_DEVICE_CONTROL_REGISTER(MAL0_RCBS1, (MCLBYTES-16)>>4);
+		PPC_SET_DEVICE_CONTROL_REGISTER(MAL0_IER,0xF7);		/* Enable all MAL interrupts */
+	} else {
+		PPC_SET_DEVICE_CONTROL_REGISTER(MAL0_CFG,kMALMedHiPriority + kMALLocksOPB + kMALLocksErrs + kMALCanBurst + kMALLatency8);
+		PPC_SET_DEVICE_CONTROL_REGISTER(MAL0_IER,0x1F);		/* Enable all MAL interrupts */
+	}
+}
+
+
+
+#ifdef qDebug
+static void printaddr(uint8_t* enetaddr)
+{
+	printf("%02X.%02X.%02X.%02X.%02X.%02X",enetaddr[0],enetaddr[1],enetaddr[2],enetaddr[3],enetaddr[4],enetaddr[5]);
+}
+#endif
+
+static bool gMALInited = FALSE;
+	
+static void
+ppc405_emac_initialize_hardware(EMACLocals* ep)
+{
+
+	int  n,mfr;
+	int  unitnum = ep->arpcom.ac_if.if_unit;
+	
+	if (get_ppc_cpu_type() == PPC_405EX) {
+		/* PPC405EX: configure the RGMII bridge and clocks */
+		RGMIIRegisters* rgmp = (RGMIIRegisters*)RGMIIAddress;
+		rgmp->FER = 0x00080055;	/* Both EMACs RGMII */
+		rgmp->SSR = 0x00000044;	/* Both EMACs 1000Mbps */
+		/* Configure the TX clock to be external */
+		mfsdr(SDR0_MFR,mfr);
+		mfr &= ~0x0C000000;		/* Switches both PHYs */
+		mtsdr(SDR0_MFR,mfr);
+	}
+	
+	/* Reset the EMAC */
+	n = 0;
+	ep->EMAC->mode0 = kEMACSoftRst;							
+	while ((ep->EMAC->mode0 & kEMACSoftRst) != 0)
+		n++;		/* Wait for it to complete */
+	
+	/* Set up so we can talk to the PHY */
+	ep->EMAC->mode1 = keEMACIPHYAddr4 |  keEMACOPB100MHz;
+	
+	/* Initialise the PHY  */
+	InitPHY(ep);
+	
+	/* Initialise the MAL (once only) */
+	if ( ! gMALInited) {
+		mal_initialise();
+		gMALInited = TRUE;
+	}
+
+	/* Set up IRQ handlers and enable the MAL channels for this port */
+	if (unitnum==0) {
+		ep->xmtDescTable = gTx0Descs;
+		ep->rcvDescTable = gRx0Descs;
+		InstallIRQHandler(BSP_UIC_EMAC0, EMAC0Err_handler, NoAction, NoAction);
+		PPC_SET_DEVICE_CONTROL_REGISTER(MAL0_TXCASR,kMALChannel0);
+		PPC_SET_DEVICE_CONTROL_REGISTER(MAL0_RXCASR,kMALChannel0);
+	} else {
+		ep->xmtDescTable = gTx1Descs;
+		ep->rcvDescTable = gRx1Descs;
+		InstallIRQHandler(BSP_UIC_EMAC1, EMAC1Err_handler, NoAction, NoAction);
+		PPC_SET_DEVICE_CONTROL_REGISTER(MAL0_TXCASR,kMALChannel1);
+		PPC_SET_DEVICE_CONTROL_REGISTER(MAL0_RXCASR,kMALChannel1);
+	}
+	
+	/* The rest of the EMAC initialisation is done in emac_phy_adapt
+	   when we know what the characteristics of the link are */
+}
+
+
+/* EMAC second stage initialisation; talks to the PHY to find out how to do it.
+   Resets the EMAC if the PHY parameters need to be changed */
+  
+int ppc405_emac_phy_adapt(EMACLocals* ep)
+{
+	int linkState = GetPHYLinkState(ep);
+	int spd;
+	
+	if ((linkState & kELinkUp) && (linkState != ep->phyState)) {
+		/* Reset the EMAC and set registers according to PHY state */
+		int i,n = 0;
+		uint32_t mode, rmode;
+		
+		ep->EMAC->mode0 = kEMACSoftRst;							
+		while ((ep->EMAC->mode0 & kEMACSoftRst) != 0)
+			n++;		/* Wait for it to complete */
+		spd = linkState & kELinkSpeedMask;
+		mode = (spd<<22) | kgEMACTx0Multi;
+		if (get_ppc_cpu_type() == PPC_405EX) 
+			mode |= (keEMAC16KRxFIFO | keEMAC16KTxFIFO | keEMACIPHYAddr4 | keEMACOPB100MHz );
+		else
+			mode |= (kgEMAC4KRxFIFO | kgEMAC2KTxFIFO);
+		if (linkState & kELinkFullDuplex)
+			mode |= kEMACFullDuplex + kEMACDoFlowControl;
+		if ( (linkState & kELinkFullDuplex) || (spd > kELinkSpeed10) )
+			mode |= kEMACIgnoreSQE;
+	
+		
+		if (spd==kELinkSpeed1000) {
+			/* Gigabit, so we support jumbo frames. Take appropriate measures: adjust the if_mtu */
+			/* Note that we do this here because changing it later doesn't work very well : see
+			   the SIOCSIFMTU discussion below */
+			struct ifnet* ifp = &ep->arpcom.ac_if;
+			ifp->if_mtu = ETHERMTU_JUMBO;
+			mode |= keEMACJumbo;
+		}
+		
+		
+		ep->phyState = linkState;
+		ep->EMAC->mode1 = mode;
+		
+		/* Install 48-bit hardware address that we have been given */
+		ep->EMAC->addrHi = (ep->arpcom.ac_enaddr[0]<<8) + ep->arpcom.ac_enaddr[1];
+		ep->EMAC->addrLo = (ep->arpcom.ac_enaddr[2]<<24) + (ep->arpcom.ac_enaddr[3]<<16)
+						+ (ep->arpcom.ac_enaddr[4]<<8) + (ep->arpcom.ac_enaddr[5] );
+		
+		/* Set receive mode appropriately */
+		rmode = kEMACStripPadding + kEMACStripFCS + kEMACBrcastRcv;
+		
+		if (ep->arpcom.ac_if.if_flags & IFF_PROMISC) rmode |= kEMACPromiscRcv;
+												else rmode |= kEMACIndivRcv;
+		if (get_ppc_cpu_type() == PPC_405EX)
+			rmode |= keEMACRxFIFOAFMax;	
+		if ((ep->arpcom.ac_if.if_flags & IFF_ALLMULTI) != 0) 
+			rmode |= kEMACPromMultRcv;
+		else if ((ep->arpcom.ac_if.if_flags & IFF_MULTICAST) != 0)
+			rmode |= kEMACMultcastRcv;
+		
+		ep->EMAC->rcvMode = rmode;
+
+		if (get_ppc_cpu_type() == PPC_405EX) 
+			for (i=0; i<8; i++)
+				ep->EMAC->e_groupHash[i] = 0;
+		else 
+			for (i=0; i<4; i++)
+				ep->EMAC->g_groupHash[i] = 0;
+
+		if (get_ppc_cpu_type() == PPC_405EX) {
+			/* Rcv low watermark, must be < mode1 Rcv FIFO size and > MAL burst length (default 64x4 bytes), 16-byte units
+			   High watermark must be > low and < RcvFIFO size */
+			ep->EMAC->rcvWatermarks = (16<<22) + (768<<6);
+			/* Xmt low request must be >= 17 FIFO entries, Xmt urgent must be > low */
+			ep->EMAC->xmtMode1 = (17<<27) + (68<<14);			/* TLR = 17, TUR = 68 */
+			/* Xmt partial packet request threshold */
+			ep->EMAC->xmtReqThreshold = ((1000>>2)-1) << 24;	/* TRTR[TRT] = 1000 FIFO entries */
+		} else {
+			ep->EMAC->rcvWatermarks = (15<<24) + (32<<8);
+			ep->EMAC->xmtReqThreshold = ((1448>>6)-1) << 26;	/* TRT = 1024b */
+			ep->EMAC->xmtMode1 = 0x40200000;					/* TLR = 8w=32b, TUR=32w=128b */
+		}
+			
+		ep->EMAC->IPGap = 8;
+		
+		/* Want EMAC interrupts for error logging & statistics */
+		ep->EMAC->intEnable = kEMACIOverrun + kEMACIPause + kEMACIBadPkt + kEMACIRuntPkt + kEMACIShortEvt
+							+ kEMACIAlignErr + kEMACIBadFCS + kEMACIOverSize + kEMACILLCRange + kEMACISQEErr
+							+ kEMACITxErr;
+						
+		/* Start it running */
+		ep->EMAC->mode0 = kEMACRxEnable + kEMACTxEnable;		
+		return 0;
+	} else
+		return -1;
+}
+
+
+static void
+ppc405_emac_disable(EMACLocals* ep)
+/* Disable the EMAC channels so we stop running and processing interrupts */
+{
+	ep->EMAC->mode0 = 0;
+}
+
+static void
+ppc405_emac_startxmt(EMACLocals* ep)
+/* Start the transmitter: set TMR0[GNP] */
+{
+	ep->EMAC->xmtMode0 = kEMACNewPacket0 + 7;		/* *** TFAE value for EX */
+}
+
+static void ppc405_emac_watchdog(struct ifnet* ifp)
+/* Called if a transmit stalls or when the interface is down. Check the PHY
+   until we get a valid link */
+{
+	EMACLocals* ep = ifp->if_softc;
+	
+	if (ppc405_emac_phy_adapt(ep)==0) {
+		ep->arpcom.ac_if.if_flags |= IFF_RUNNING;
+		ifp->if_timer = 0;		/* No longer needed */
+	} else
+		ifp->if_timer = 1;		/* reschedule, once a second */
+}
+
+
+
+/*----------------------- The transmit daemon/task -------------------------- */
+
+
+static void
+FreeTxDescriptors(EMACLocals* ep)
+/* Make descriptors and mbufs at xmtBusyIndex available for re-use if the packet that they */
+/* point at has all gone */
+{
+	uint16_t scan, status;
+	
+	if (ep->xmtBusyIndex != kNoXmtBusy) {
+		scan = ep->xmtBusyIndex;
+		while (TRUE) {
+			/* Scan forward through the descriptors */
+			status = ep->xmtDescTable[scan].ctrlBits;
+			if (++scan >= kNXmtDescriptors)
+				scan = 0;
+			/* If we find a ready (i.e not-yet-sent) descriptor, stop */
+			if ((status & kMALTxReady) != 0)
+				break; 
+			/* If we find a last descriptor, we can free all the buffers up to and including it */
+			if ((status & kMALLast) != 0) {
+				/* End of packet and it has been sent or abandoned; advance xmtBusyIndex to  */
+				/* the next buffer and free buffers. */
+				if ((status & kEMACErrMask) != 0) {
+					/* Transmit error of some kind */
+					
+					if ((status & kEMACDeferred) != 0)
+						ep->txDeferred++;
+					if ((status & kEMACLostCarrier) != 0)
+						ep->txLostCarrier++;	/* *** Perhaps more serious reaction needed... */
+					
+					if ((status & kEMACLateColl) != 0)
+						ep->txLateCollision++;
+					if ((status & kEMACOneColl) != 0)
+						ep->txOneCollision++;
+					if ((status & kEMACMultColl) != 0)
+						ep->txMultiCollision++;
+					if ((status & kEMACCollFail) != 0)
+						ep->txTooManyCollision++;
+					
+					if ((status & kEMACSQEFail) != 0)
+						ep->txPoorSignal++;
+					if ((status & kEMACUnderrun) != 0)
+						ep->txUnderrun++;
+				}
+				while (ep->xmtBusyIndex != scan) {
+					m_free(ep->txMBufs[ep->xmtBusyIndex]);
+					if (++ep->xmtBusyIndex >= kNXmtDescriptors) ep->xmtBusyIndex = 0;
+				}
+				if (ep->xmtBusyIndex == ep->xmtFreeIndex) {
+					/* Nothing is busy */
+					ep->xmtBusyIndex = kNoXmtBusy;
+					break;
+				}
+			}
+		}
+	}
+}
+
+
+static void
+SendPacket (EMACLocals* ep, struct ifnet *ifp, struct mbuf *m)
+/* Given a chain of mbufs, set up a transmit description and fire it off */
+{
+	int nAdded, index, lastidx, totalbytes;
+	uint16_t status;
+	struct mbuf* lastAdded;
+	
+	nAdded = 0;
+	totalbytes = 0;
+	lastAdded = NULL;
+	index = ep->xmtFreeIndex;
+	
+	/* Go through the chain of mbufs setting up descriptors for each */
+	while (m != NULL) {
+					
+		if (m->m_len == 0) {
+			/* Can be empty: dispose and unlink from chain */
+			m = m_free(m);
+			if (lastAdded!=NULL) lastAdded->m_next = m;
+		} else {
+			/* Make sure the mbuf has been written to memory */
+			rtems_cache_flush_multiple_data_lines(mtod (m, void *), m->m_len);
+			/* If there are no descriptors available wait until there are */
+			while (index == ep->xmtBusyIndex) {
+				rtems_event_set events;
+				ifp->if_timer = 2;
+				/* Then check for free descriptors, followed by: */
+				rtems_bsdnet_event_receive (INTERRUPT_EVENT, RTEMS_WAIT | RTEMS_EVENT_ANY, RTEMS_NO_TIMEOUT, &events);
+				FreeTxDescriptors(ep);
+			}
+				
+			/* Fill in a descriptor for this mbuf and record it */
+			ep->txMBufs[index] = m;
+			ep->xmtDescTable[index].ptr = mtod (m, void *);
+			ep->xmtDescTable[index].adrDataSize = m->m_len;
+			/* Fill in ctrlBits as we go but don't mark the first one as ready yet */
+			status = kEMACGenFCS + kEMACGenPad + kEMACRepSrcAddr;
+			if (nAdded > 0)
+				status |= kMALTxReady;
+			if (index==kNXmtDescriptors-1)
+				status |= kMALWrap;
+			ep->xmtDescTable[index].ctrlBits = status;
+			lastidx = index;
+					
+			totalbytes += m->m_len;
+			lastAdded = m;
+			m = m->m_next;
+			nAdded++;
+			
+			index += 1;
+			if (index==kNXmtDescriptors)
+				index = 0;
+			
+			if (nAdded==kNXmtDescriptors) 
+				rtems_fatal_error_occurred(RTEMS_INTERNAL_ERROR);		/* This is impossible, of course... */
+		}
+	}
+	
+	if (nAdded > 0) {
+		/* Done and we added some buffers */
+		/* Label the last buffer and ask for an interrupt */
+		ep->xmtDescTable[lastidx].ctrlBits |= kMALLast + kMALInterrupt;
+		/* Finally set the ready bit on the first buffer */
+		ep->xmtDescTable[ep->xmtFreeIndex].ctrlBits |= kMALTxReady;
+		/* Make sure this has been written */
+		_sync;
+		if (ep->xmtBusyIndex == kNoXmtBusy)
+			ep->xmtBusyIndex = ep->xmtFreeIndex;
+		ep->xmtFreeIndex = index;
+		/* Poke the EMAC to get it started (which may not be needed if its already running */
+		ppc405_emac_startxmt(ep);
+		ifp->if_timer = 2;
+	}
+}
+
+static void
+ppc405_emac_txDaemon (void* param)
+{
+	EMACLocals* ep = param;
+	struct ifnet *ifp = &ep->arpcom.ac_if;
+	struct mbuf *m;
+	rtems_event_set events;
+
+	ep->xmtFreeIndex = 0;
+	ep->xmtBusyIndex = kNoXmtBusy;
+	while (TRUE) {
+		/* Wait for someone wanting to transmit */
+		rtems_bsdnet_event_receive (START_TRANSMIT_EVENT | INTERRUPT_EVENT,
+									RTEMS_EVENT_ANY | RTEMS_WAIT, 
+									RTEMS_NO_TIMEOUT,
+									&events);
+		if (events & INTERRUPT_EVENT) 
+			ifp->if_timer = 0;
+		/* Grab packets and send until empty */
+		/* Note that this doesn't (at the time of writing, RTEMS 4.9.1), ever get asked to send more than
+		   one header mbuf and one data mbuf cluster, regardless of the MTU. This is because sosend() in the FreeBSD
+		   stack only passes one mbuf at a time across to tcp_send, which immediately sends it */
+		while (TRUE) {
+			FreeTxDescriptors(ep);
+			IF_DEQUEUE(&ifp->if_snd, m);
+			if (m == NULL) 
+				break;
+			SendPacket (ep, ifp, m);
+		}
+		ifp->if_flags &= ~IFF_OACTIVE;
+	}
+}
+
+/*----------------------- The receive daemon/task -------------------------- */
+
+static void
+MakeRxBuffer(EMACLocals* ep, int index)
+{
+	struct mbuf* 	m;
+	
+	/* Allocate an mbuf, wait if necessary, label as dynamic data, start of record */
+	MGETHDR (m, M_WAIT, MT_DATA);
+	/* Allocate a cluster buffer to this mbuf, waiting if necessary */
+	MCLGET (m, M_WAIT);
+	/* Set up reference to the interface the packet will be received on */
+	m->m_pkthdr.rcvif = &ep->arpcom.ac_if;
+	ep->rxMBufs[index] = m;
+	ep->rcvDescTable[index].ptr = mtod (m, uint8_t*);
+	ep->rcvDescTable[index].adrDataSize = 0x0EEE;		/* Precaution */
+	if (index==ep->nRxBuffers-1)
+		ep->rcvDescTable[index].ctrlBits = kMALRxEmpty + kMALInterrupt + kMALWrap;
+	else
+		ep->rcvDescTable[index].ctrlBits = kMALRxEmpty + kMALInterrupt;
+}
+
+
+
+static void
+ppc405_emac_rxDaemon (void* param)
+{
+	EMACLocals* ep = param;
+	int 		index,n,mdex;
+	struct mbuf* 	m;
+	struct mbuf*    mstart = NULL;
+	struct mbuf*    mlast = NULL;
+	struct ifnet*	ifp;
+	struct ether_header* eh = NULL;
+	rtems_event_set events;
+	
+	/* Startup : allocate a bunch of receive buffers and point the descriptor table entries at them */
+	ifp = &ep->arpcom.ac_if;
+	index = 0;
+	while (index < ep->nRxBuffers) {
+		MakeRxBuffer(ep,index);
+		index += 1;
+	}
+	index = 0;
+	mdex = 0;
+	
+	/* Loop waiting for frames to arrive */
+	while (TRUE) {
+		rtems_bsdnet_event_receive (INTERRUPT_EVENT,
+											RTEMS_WAIT | RTEMS_EVENT_ANY,
+											RTEMS_NO_TIMEOUT,
+											&events);
+		while ((ep->rcvDescTable[index].ctrlBits & kMALRxEmpty) == 0) {
+			/* Got a frame */
+			uint16_t flags = ep->rcvDescTable[index].ctrlBits;
+			if ((flags & kEMACErrMask) != 0) {
+				/* It has errors. Update statistics */
+				if ((flags & kEMACOverrun) != 0)
+					ep->rxOverrun++;
+				if ((flags & kEMACRuntPkt) != 0)
+					ep->rxRunt++;
+				if ((flags & kEMACBadFCS) != 0)
+					ep->rxBadCRC++;
+				if ((flags & kEMACAlignErr) != 0)
+					ep->rxNonOctet++;
+				if ((flags & kEMACPktLong) != 0)
+					ep->rxGiant++;
+				/* and reset descriptor to empty */
+				
+				/* No need to get new mbufs, just reset */
+				ep->rcvDescTable[index].adrDataSize = 0x0EEE;
+				if (index==ep->nRxBuffers-1)
+					ep->rcvDescTable[index].ctrlBits = kMALRxEmpty + kMALInterrupt + kMALWrap;
+				else
+					ep->rcvDescTable[index].ctrlBits = kMALRxEmpty + kMALInterrupt;
+				
+			} else {
+				/* Seems to be OK. Invalidate cache over the size we received */
+				n = ep->rcvDescTable[index].adrDataSize & 0x0FFF;
+				m = ep->rxMBufs[index];
+				rtems_cache_invalidate_multiple_data_lines(m->m_data, (n + kCacheLineMask) & ~kCacheLineMask);
+				
+				/* Consider copying small packets out of the cluster into m_pktdat to save clusters? */
+				m->m_len = n;
+					
+				/* Jumbo packets will span multiple mbufs; chain them together and submit when we get the last one */
+				if (flags & kMALRxFirst) {
+					/* First mbuf in the packet */
+					if (mstart!=NULL)
+						rtems_panic("first, no last");
+					
+					/* Adjust the mbuf pointers to skip the header and set eh to point to it */
+					m->m_len -= sizeof(struct ether_header);
+					m->m_pkthdr.len = m->m_len;
+					eh = mtod (m, struct ether_header *);
+					m->m_data += sizeof(struct ether_header);
+					mstart = m;
+					mlast = m;
+					mdex = index;
+				} else {
+					/* Chain onto mstart: add length to pkthdr.len */
+					if (mstart == NULL)
+						rtems_panic("last, no first");
+					
+					mstart->m_pkthdr.len += n;
+					m->m_flags &= ~M_PKTHDR;
+					mlast->m_next = m;
+					mlast = m;
+				}
+				
+				if (flags & kMALLast) {
+					/* Last mbuf in the packet: pass base of the chain to a higher level */
+					ether_input (ifp, eh, mstart);
+					
+					/* ether_input took the chain, set up new mbufs in the slots we used */
+					mdex -= 1;
+					do {
+						if (++mdex==ep->nRxBuffers) mdex = 0;
+						MakeRxBuffer(ep,mdex);
+					} while (mdex != index);
+					mstart = NULL;
+					mlast = NULL;
+					eh = NULL;
+				}
+			}
+			index += 1;
+			if (index == ep->nRxBuffers) index = 0;
+		}
+	}
+}
+
+/*----------- Vectored routines called through the driver struct ------------------ */
+
+static void ppc405_emac_init (void* p)
+/* Initialise the hardware, create and start the transmit and receive tasks */
+{
+	char txName[] = "ETx0";
+	char rxName[] = "ERx0";
+	
+	EMACLocals* ep = (EMACLocals*)p;
+	if (ep->txDaemonTid == 0) {
+		ppc405_emac_initialize_hardware(ep);
+		rxName[3] += ep->phyAddr;
+		ep->rxDaemonTid = rtems_bsdnet_newproc (rxName, 4096, ppc405_emac_rxDaemon, ep);
+		txName[3] += ep->phyAddr;
+		ep->txDaemonTid = rtems_bsdnet_newproc (txName, 4096, ppc405_emac_txDaemon, ep);
+	}
+	/* Only set IFF_RUNNING if the PHY is ready. If not set the watchdog timer running so we check it */
+	if ( GetPHYLinkState(ep) & kELinkUp ) 
+		ep->arpcom.ac_if.if_flags |= IFF_RUNNING;
+	else
+		ep->arpcom.ac_if.if_timer = 1;
+}
+
+static void ppc405_emac_start(struct ifnet *ifp)
+/* Send a packet: send an event to the transmit task, waking it up */
+{
+	EMACLocals* ep = ifp->if_softc;
+	rtems_event_send (ep->txDaemonTid, START_TRANSMIT_EVENT);
+	ifp->if_flags |= IFF_OACTIVE;
+}
+
+static void ppc405_emac_stop (EMACLocals* ep)
+{
+	uint32_t mask;
+	
+	mask = 0x80000000 >> ep->arpcom.ac_if.if_unit;
+	PPC_SET_DEVICE_CONTROL_REGISTER(MAL0_TXCARR,mask);
+	PPC_SET_DEVICE_CONTROL_REGISTER(MAL0_RXCARR,mask);
+	ppc405_emac_disable(ep);
+	/* *** delete daemons, or do they exit themselves? */
+	ep->arpcom.ac_if.if_flags &= ~IFF_RUNNING;
+}
+
+#ifdef qDebug
+static void ppc405_emac_stats (EMACLocals* ep)
+{
+
+	printf ("  Rx Interrupts:%-8lu", ep->rxInterrupts);
+	printf ("          Giant:%-8lu", ep->rxGiant);
+	printf ("           Runt:%-8lu\n", ep->rxRunt);
+	printf ("      Non-octet:%-8lu", ep->rxNonOctet);
+	printf ("        Bad CRC:%-8lu", ep->rxBadCRC);
+	printf ("        Overrun:%-8lu\n", ep->rxOverrun);
+
+	printf ("    Tx Interrupts:%-8lu", ep->txInterrupts);
+	printf ("    Long deferral:%-8lu", ep->txDeferred);
+	printf ("       No Carrier:%-8lu\n", ep->txLostCarrier);
+	printf ("   Late collision:%-8lu", ep->txLateCollision);
+	printf ("    One collision:%-8lu", ep->txOneCollision);
+	printf ("  Many collisions:%-8lu\n", ep->txMultiCollision);
+	printf ("Excess collisions:%-8lu", ep->txTooManyCollision);
+	printf ("         Underrun:%-8lu", ep->txUnderrun);
+	printf ("      Poor signal:%-8lu\n", ep->txPoorSignal);
+}
+#endif
+
+static int UpdateMulticast(EMACLocals* ep)
+{
+	/* Traverse list of multicast addresses and update hardware hash filter. This is just a work-reduction */
+	/* step; the filter uses a hash of the hardware address and therefore doesn't catch all unwanted packets */
+	/* We have to do other checks in software. */
+	/* 405GP/GPr has 4x16-bit hash registers, 405EX/EXr has 8x32-bit */
+	
+	struct ether_multi* enm;
+	struct ether_multistep step;
+	uint32_t hash;
+	
+	#ifdef qMultiDebug
+		printf("\nMulticast etheraddrs:\n");
+	#endif
+	
+	ETHER_FIRST_MULTI(step, &ep->arpcom, enm);
+	while (enm != NULL) {
+		
+		/* *** Doesn't implement ranges */
+				
+		hash = ether_crc32_be( (uint8_t*)&enm->enm_addrlo, sizeof(enm->enm_addrlo) );
+		if (get_ppc_cpu_type() == PPC_405EX) {
+			hash >>= 24;	/* Upper 8 bits, split 3/5 */
+			/* This has been experimentally verified against the hardware */
+			ep->EMAC->e_groupHash[7-(hash>>5)] |= (1 << (hash & 0x1F));
+		} else {
+			hash >>= 26;	/* Upper 6 bits, split 2/4 */
+			/* This has not been checked */
+			ep->EMAC->g_groupHash[3-(hash>>6)] |= (1 << (hash & 0xF));
+		}
+		
+		#ifdef qMultiDebug
+			printf("  ");
+			printaddr(enm->enm_addrlo);
+			printf(" = bit %d",hash);
+			if (memcmp(&enm->enm_addrlo, &enm->enm_addrhi, 6) != 0) {
+				printf(" - ");
+				printaddr(enm->enm_addrhi);
+				printf(" [not supported]");
+			}
+			printf("\n");
+		#endif
+		
+		ETHER_NEXT_MULTI(step, enm);
+	}
+	#ifdef qMultiDebug
+	{
+		int i;
+		printf(" Grouphash is ");
+		for (i=0; i<8; i++)
+			printf("%08X:",(int)ep->EMAC->e_groupHash[i]);
+		printf("\n");
+	}
+	#endif
+	return 0;
+}
+
+
+static int ppc405_emac_ioctl (struct ifnet *ifp, ioctl_command_t command, caddr_t data)
+{
+	int error = 0;
+	EMACLocals* ep = ifp->if_softc;
+	struct ifreq* reqP = (struct ifreq *) data;
+		
+	switch (command) {
+	
+		case SIOCSIFFLAGS:
+			switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) {
+				case IFF_RUNNING:
+					ppc405_emac_stop(ep);
+					break;
+		
+				case IFF_UP:
+					ppc405_emac_init(ep);
+					break;
+		
+				case IFF_UP | IFF_RUNNING:
+					ppc405_emac_stop(ep);
+					ppc405_emac_init(ep);
+					break;
+		
+				default:
+					break;
+			}
+			break;
+	
+		case SIOCADDMULTI: {
+				error = ether_addmulti( reqP, &ep->arpcom);
+				if (error==ENETRESET)
+					error = UpdateMulticast(ep);
+			} break;
+			
+		case SIOCDELMULTI:
+			error = ether_delmulti( (struct ifreq *) data, &ep->arpcom);
+			if (error==ENETRESET)
+				error = UpdateMulticast(ep);
+			break;
+			
+		case SIOCSIFMTU: {
+				/* Note that this may not do what you want; setting the interface MTU doesn't touch the route MTUs,
+				   and new routes are sometimes made by cloning old ones. So this won't change the MTU to known hosts
+				   and may not change the MTU to new ones either... */
+				int max;
+				if ( get_ppc_cpu_type() == PPC_405EX && (ep->EMAC->mode1 & keEMACJumbo) != 0 )
+					max = ETHER_MAX_LEN_JUMBO;
+				else
+					max = ETHER_MAX_LEN;
+				if (reqP->ifr_mtu > max - ETHER_HDR_LEN - ETHER_CRC_LEN)
+					error = EINVAL;
+				else 
+					ifp->if_mtu = reqP->ifr_mtu;
+			} break;
+			
+		case SIO_RTEMS_SHOW_STATS:
+			#ifdef qDebug
+				ppc405_emac_stats(ep);
+			#endif
+			break;
+			
+		default:
+			/* Not handled here, pass to generic */
+			error = ether_ioctl(ifp,command,data);
+			break;
+	}
+	
+	#ifdef qDebug
+	if (error != 0)
+		printf("--- Ethernet ioctl %d failed %d\n",(int)command,error);
+	#endif
+	
+	return error;
+}
+
+	
+/*----------------------- External attach function -------------------------- 
+ *  
+ * This is the one function we are required to define in here: declared in bsp.h 
+ * as RTEMS_BSP_NETWORK_DRIVER_ATTACH and called from rtems_bsdnet_attach 
+ *
+*/
+
+int
+rtems_emac_driver_attach(struct rtems_bsdnet_ifconfig* config, int attaching)
+{
+	int 	unitNumber, nUnits;
+	char*	unitName;
+	struct	ifnet* ifp;
+	EMACLocals* ep;
+	
+	if (attaching==0) {
+		printk ("EMAC: driver cannot be detached.\n");
+		return 0;
+	}
+	
+	nUnits = 1;
+	if (get_ppc_cpu_type()==PPC_405EX && get_ppc_cpu_revision() > 0x1474)
+		nUnits = 2;	/* PPC405EX has two interfaces, EXr has one */
+	
+	unitNumber = rtems_bsdnet_parse_driver_name (config, &unitName);
+    if (unitNumber < 0 || unitNumber > nUnits-1) {
+		printk ("EMAC: bad unit number %d.\n",unitNumber);
+		return 0;
+	}
+	
+	ep = &gEmacs[unitNumber];
+	
+	if (get_ppc_cpu_type()==PPC_405EX) {
+		if (unitNumber==0) ep->EMAC = (EthernetRegisters_EX*)EMAC0EXAddress;
+					  else ep->EMAC = (EthernetRegisters_GP*)EMAC1EXAddress;
+	} else 
+		ep->EMAC = (EthernetRegisters_GP*)EMAC0GPAddress;
+		
+	ifp = &ep->arpcom.ac_if;
+	if (ifp->if_softc != NULL) {
+		printk ("EMAC: driver already in use.\n");
+		return 0;
+	}
+	ifp->if_softc = ep;
+	
+	if (config->hardware_address == 0)
+		rtems_panic("No Ethernet MAC address specified!");
+	memcpy (ep->arpcom.ac_enaddr, config->hardware_address, ETHER_ADDR_LEN);
+	
+	ifp->if_name = unitName;
+	ifp->if_unit = unitNumber;
+	
+	if (config->mtu != 0)
+		ifp->if_mtu = config->mtu;
+	else 
+		ifp->if_mtu = ETHERMTU;		/* May be adjusted later by ppc405_emac_phy_adapt() */
+		
+	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+	if (ifp->if_snd.ifq_maxlen == 0)
+		ifp->if_snd.ifq_maxlen = ifqmaxlen;
+	ifp->if_init = &ppc405_emac_init;
+	ifp->if_ioctl = ppc405_emac_ioctl;
+	ifp->if_start = ppc405_emac_start;
+	ifp->if_output = ether_output;
+	ifp->if_watchdog = ppc405_emac_watchdog;
+	ifp->if_timer	 = 0;
+	
+	if (config->rbuf_count != 0) {
+		if (config->rbuf_count > 256) ep->nRxBuffers = 256;
+								 else ep->nRxBuffers = config->rbuf_count;
+	} else
+		ep->nRxBuffers = nmbclusters/2;
+		
+	ep->phyAddr = unitNumber+1;
+	ep->phyState = 0;
+	
+	#ifdef qDebug
+		printf("\n  Setting up EMAC %d of %d\n",unitNumber+1,nUnits);
+		printf("  MAC address is ");
+		printaddr(ep->arpcom.ac_enaddr);
+		printf("  MHLEN = %d, MINCLSIZE = %d MCLBYTES = %d\n",MHLEN,MINCLSIZE,MCLBYTES); 
+		printf("  ticks/sec = %d, usec/tick = %d\n", rtems_bsdnet_ticks_per_second, rtems_bsdnet_microseconds_per_tick);
+ 	#endif
+	
+ 	if_attach (ifp);
+	ether_ifattach (ifp);
+
+	return 1;
+}
+
diff --git a/c/src/lib/libbsp/powerpc/haleakala/preinstall.am b/c/src/lib/libbsp/powerpc/haleakala/preinstall.am
index 0e65b26..7483a4f 100644
--- a/c/src/lib/libbsp/powerpc/haleakala/preinstall.am
+++ b/c/src/lib/libbsp/powerpc/haleakala/preinstall.am
@@ -41,6 +41,10 @@ $(PROJECT_INCLUDE)/bsp.h: include/bsp.h $(PROJECT_INCLUDE)/$(dirstamp)
 	$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp.h
 PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp.h
 
+$(PROJECT_INCLUDE)/mmu_405.h: include/mmu_405.h $(PROJECT_INCLUDE)/$(dirstamp)
+	$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/mmu_405.h
+PREINSTALL_FILES += $(PROJECT_INCLUDE)/mmu_405.h
+
 $(PROJECT_INCLUDE)/tm27.h: ../../shared/include/tm27.h $(PROJECT_INCLUDE)/$(dirstamp)
 	$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/tm27.h
 PREINSTALL_FILES += $(PROJECT_INCLUDE)/tm27.h
diff --git a/c/src/lib/libcpu/powerpc/ppc403/include/ppc405ex.h b/c/src/lib/libcpu/powerpc/ppc403/include/ppc405ex.h
index 5a4a653..98ebbe2 100644
--- a/c/src/lib/libcpu/powerpc/ppc403/include/ppc405ex.h
+++ b/c/src/lib/libcpu/powerpc/ppc403/include/ppc405ex.h
@@ -78,60 +78,87 @@ enum {
 };
 
 enum {
+	SDR0_PINSTP = 0x40,
 	SDR0_UART0	= 0x120,
 	SDR0_UART1  = 0x121,
 	SDR0_C405	= 0x180,
+	SDR0_SRST0  = 0x200,
 	SDR0_MALTBL = 0x280,
 	SDR0_MALRBL = 0x2A0,
 	SDR0_MALTBS = 0x2C0,
-	SDR0_MALRBS = 0x2E0
+	SDR0_MALRBS = 0x2E0,
+	SDR0_PFC2   = 0x4102,
+	SDR0_MFR    = 0x4300,
+	SDR0_EMAC0RXST = 0x4301,
+	SDR0_HSF    = 0x4400
 };
 
+enum {
+	CPR0_CLKUPD = 0x20,
+	CPR0_PLLC = 0x40,
+	CPR0_PLLD = 0x60,
+	CPR0_CPUD = 0x80,
+	CPR0_PLBD = 0xA0,
+	CPR0_OPBD = 0xC0,
+	CPR0_PERD = 0xE0,
+	CPR0_AHBD = 0x100,
+	CPR0_ICFG = 0x140
+};
 
 /* Memory-mapped registers */
 
 
 /*======================= Ethernet =================== */
 
-
-typedef struct EthernetRegisters_EX {
-	uint32_t mode0;
-	uint32_t mode1;
-	uint32_t xmtMode0;
-	uint32_t xmtMode1;
-	uint32_t rcvMode;
-	uint32_t intStatus;
-	uint32_t intEnable;
-	uint32_t addrHi;
-	uint32_t addrLo;
-	uint32_t VLANTPID;
-	uint32_t VLANTCI;
-	uint32_t pauseTimer;
-	uint32_t multicastAddr[2];
-	uint32_t multicastMask[2];
-	uint32_t unused[4];
-	uint32_t lastSrcLo;
-	uint32_t lastSrcHi;
-	uint32_t IPGap;
-	uint32_t STAcontrol;
-	uint32_t xmtReqThreshold;
-	uint32_t rcvWatermark;
-	uint32_t bytesXmtd;
-	uint32_t bytesRcvd;
-	uint32_t unused2;
-	uint32_t revID;
-	uint32_t unused3[2];
-	uint32_t indivHash[8];
-	uint32_t groupHash[8];
-	uint32_t xmtPause;
-} EthernetRegisters_EX;
-
 enum {
-	EMAC0Address = 0xEF600900,
-	EMAC1Address = 0xEF600A00
+	EMAC0EXAddress = 0xEF600900,
+	EMAC1EXAddress = 0xEF600A00,
+	
+	/* 405EX-specific bits in EMAC_MR1 */
+	keEMAC1000Mbps = 0x00800000,
+	keEMAC16KRxFIFO = 0x00280000,
+	keEMAC8KRxFIFO  = 0x00200000,
+	keEMAC4KRxFIFO  = 0x00180000,
+	keEMAC2KRxFIFO  = 0x00100000,
+	keEMAC1KRxFIFO  = 0x00080000,
+	keEMAC16KTxFIFO = 0x00050000,
+	keEMAC8KTxFIFO  = 0x00040000,
+	keEMAC4KTxFIFO  = 0x00030000,
+	keEMAC2KTxFIFO  = 0x00020000,
+	keEMAC1KTxFIFO  = 0x00010000,
+	keEMACJumbo	    = 0x00000800,
+	keEMACIPHYAddr4 = 0x180,
+	keEMACOPB50MHz  = 0x00,
+	keEMACOPB66MHz  = 0x08,
+	keEMACOPB83MHz  = 0x10,
+	keEMACOPB100MHz = 0x18,
+	keEMACOPBGt100  = 0x20,
+	
+	/* 405EX-specific bits in MAL0_CFG */
+	keMALRdMaxBurst4  = 0,
+	keMALRdMaxBurst8  = 0x00100000,
+	keMALRdMaxBurst16 = 0x00200000,
+	keMALRdMaxBurst32 = 0x00300000,
+	
+	keMALWrLowPriority    = 0,
+	keMALWrMedLowPriority = 0x00040000,
+	keMALWrMedHiPriority  = 0x00080000,
+	keMALWrHighPriority   = 0x000C0000,
+
+	keMALWrMaxBurst4  = 0,
+	keMALWrMaxBurst8  = 0x00010000,
+	keMALWrMaxBurst16 = 0x00020000,
+	keMALWrMaxBurst32 = 0x00030000,
+	
+	/* 405EX-specific STA bits */
+	keSTARun	  = 0x8000,
+	keSTADirectRd = 0x1000,
+	keSTADirectWr = 0x0800,
+	keSTAIndirAddr = 0x2000,
+	keSTAIndirRd  = 0x3000,
+	keSTAIndirWr  = 0x2800
 };
 
-
 typedef struct GPIORegisters {
 	uint32_t OR;
 	uint32_t GPIO_TCR;		/* Note that TCR is defined as a DCR name */
@@ -155,3 +182,10 @@ typedef struct GPIORegisters {
 
 enum { GPIOAddress = 0xEF600800 };
 
+typedef struct RGMIIRegisters {
+	uint32_t FER;
+	uint32_t SSR;
+} RGMIIRegisters;
+
+enum { RGMIIAddress = 0xEF600B00 };
+
diff --git a/c/src/lib/libcpu/powerpc/ppc403/include/ppc405gp.h b/c/src/lib/libcpu/powerpc/ppc403/include/ppc405gp.h
index 8c7c9f1..9cf46a7 100644
--- a/c/src/lib/libcpu/powerpc/ppc403/include/ppc405gp.h
+++ b/c/src/lib/libcpu/powerpc/ppc403/include/ppc405gp.h
@@ -36,6 +36,25 @@ enum {
 	EBC0_CFG	=	0x23
 };
 
+/* MAL DCRs, have to be #defines */
+#define	MAL0_CFG		0x180
+#define	MAL0_ESR		0x181
+#define	MAL0_IER		0x182
+#define	MAL0_TXCASR		0x184
+#define	MAL0_TXCARR		0x185
+#define	MAL0_TXEOBISR	0x186
+#define	MAL0_TXDEIR		0x187
+#define	MAL0_RXCASR		0x190
+#define	MAL0_RXCARR		0x191
+#define	MAL0_RXEOBISR	0x192
+#define	MAL0_RXDEIR		0x193
+#define	MAL0_TXCTP0R	0x1A0
+#define	MAL0_TXCTP1R	0x1A1
+#define	MAL0_RXCTP0R	0x1C0
+#define	MAL0_RXCTP1R	0x1C1
+#define	MAL0_RCBS0		0x1E0
+#define	MAL0_RCBS1		0x1E1
+
 /* Memory-mapped registers */
 
 typedef struct EthernetRegisters_GP {
@@ -51,19 +70,28 @@ typedef struct EthernetRegisters_GP {
 	uint32_t VLANTPID;
 	uint32_t VLANTCI;
 	uint32_t pauseTimer;
-	uint32_t indivHash[4];
-	uint32_t groupHash[4];
+	uint32_t g_indivHash[4];  /* EX non-IP multicast addr/mask */
+	uint32_t g_groupHash[4];
 	uint32_t lastSrcLo;
 	uint32_t lastSrcHi;
 	uint32_t IPGap;
 	uint32_t STAcontrol;
 	uint32_t xmtReqThreshold;
-	uint32_t rcvWatermark;
+	uint32_t rcvWatermarks;
 	uint32_t bytesXmtd;
 	uint32_t bytesRcvd;
+	uint32_t e_unused2;
+	uint32_t e_revID;
+	uint32_t e_unused3[2];
+	uint32_t e_indivHash[8];
+	uint32_t e_groupHash[8];
+	uint32_t e_xmtPause;
 } EthernetRegisters_GP;
 
+typedef struct EthernetRegisters_GP EthernetRegisters_EX;
+
 enum { EMACAddress = 0xEF600800 };
+enum { EMAC0GPAddress = 0xEF600800 };
 
 enum {
 	// Mode 0 bits
@@ -75,12 +103,19 @@ enum {
 
 	// Mode 1 bits
 	kEMACFullDuplex = 0x80000000,
+	kEMACDoFlowControl = 0x10000000,
 	kEMACIgnoreSQE  = 0x01000000,
 	kEMAC100MBbps	 = 0x00400000,
 	kEMAC4KRxFIFO   = 0x00300000,
 	kEMAC2KTxFIFO	 = 0x00080000,
 	kEMACTx0Multi	 = 0x00008000,
 	kEMACTxDependent= 0x00014000,
+	kEMAC100Mbps	 = 0x00400000,
+	kgEMAC4KRxFIFO   = 0x00300000,
+	kgEMAC2KTxFIFO	 = 0x00080000,
+	kgEMACTx0Multi	 = 0x00008000,
+	kgEMACTxDependent= 0x00014000,
+
 
 	// Tx mode bits
 	kEMACNewPacket0	= 0x80000000,
@@ -98,6 +133,25 @@ enum {
 	kEMACHashRcv		= 0x00200000,
 	kEMACBrcastRcv		= 0x00100000,
 	kEMACMultcastRcv	= 0x00080000,
+	keEMACNonIPMultcast = 0x00040000,
+	keEMACRxFIFOAFMax   = 7,
+	
+	// EMAC_STACR bits
+	kgSTAComplete   = 0x8000,
+	kSTAErr		= 0x4000,
+	
+	// Interrupt status bits
+	kEMACIOverrun = 0x02000000,
+	kEMACIPause   = 0x01000000,
+	kEMACIBadPkt  = 0x00800000,
+	kEMACIRuntPkt = 0x00400000,
+	kEMACIShortEvt= 0x00200000,
+	kEMACIAlignErr= 0x00100000,
+	kEMACIBadFCS  = 0x00080000,
+	kEMACIOverSize= 0x00040000,
+	kEMACILLCRange= 0x00020000,
+	kEMACISQEErr  = 0x00000080,
+	kEMACITxErr   = 0x00000040,
 
 	// Buffer descriptor control bits
 	kMALTxReady			= 0x8000,
@@ -108,6 +162,21 @@ enum {
 	kMALRxFirst			= 0x0800,
 	kMALInterrupt		= 0x0400,
 
+	kMALReset			= 0x80000000,
+	kMALLowPriority    = 0,
+	kMALMedLowPriority = 0x00400000,
+	kMALMedHiPriority  = 0x00800000,
+	kMALHighPriority   = 0x00C00000,
+	kMALLatency8       = 0x00040000,
+	kMALLockErr	    = 0x8000,
+	kMALCanBurst	= 0x4000,
+	kMALLocksOPB    = 0x80,
+	kMALLocksErrs	= 0x2,
+	
+	// MAL channel masks
+	kMALChannel0 = 0x80000000,
+	kMALChannel1 = 0x40000000,
+
 	// EMAC Tx descriptor bits sent
 	kEMACGenFCS			= 0x200,
 	kEMACGenPad			= 0x100,




More information about the vc mailing list