summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNigel Spon <nigel@adi.co.nz>2014-11-21 11:15:22 -0600
committerJoel Sherrill <joel.sherrill@oarcorp.com>2014-11-21 13:47:42 -0600
commit502609c80d5f8c5ec852845c4632c905811b3ac3 (patch)
tree3ca172b6f6017f03df58aa81f4e3c7567bf95f82
parentobjectsetname.c: Fix always true condition (Coverity ID 1063874) (diff)
downloadrtems-502609c80d5f8c5ec852845c4632c905811b3ac3.tar.bz2
powerpc/haleakala: Add network driver
close 1405
-rw-r--r--c/src/lib/libbsp/powerpc/haleakala/Makefile.am15
-rw-r--r--c/src/lib/libbsp/powerpc/haleakala/configure.ac1
-rw-r--r--c/src/lib/libbsp/powerpc/haleakala/include/mmu_405.h77
-rw-r--r--c/src/lib/libbsp/powerpc/haleakala/mmu/mmu_405.c281
-rw-r--r--c/src/lib/libbsp/powerpc/haleakala/mmu/mmu_405asm.S83
-rw-r--r--c/src/lib/libbsp/powerpc/haleakala/network/network.c1243
-rw-r--r--c/src/lib/libbsp/powerpc/haleakala/preinstall.am4
-rw-r--r--c/src/lib/libcpu/powerpc/ppc403/include/ppc405ex.h108
-rw-r--r--c/src/lib/libcpu/powerpc/ppc403/include/ppc405gp.h75
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 c56a22803a..1c91d054e7 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 81c418f118..bc66c3e54e 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 0000000000..e11cfa7738
--- /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 0000000000..a5f802e210
--- /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 0000000000..5fef5fb11f
--- /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 0000000000..03cce2a97f
--- /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 0e65b26a9f..7483a4f329 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 5a4a653dd1..98ebbe28a2 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 8c7c9f1d83..9cf46a780a 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,