summaryrefslogtreecommitdiffstats
path: root/c/src/lib/libbsp/powerpc/haleakala/mmu
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 /c/src/lib/libbsp/powerpc/haleakala/mmu
parentobjectsetname.c: Fix always true condition (Coverity ID 1063874) (diff)
downloadrtems-502609c80d5f8c5ec852845c4632c905811b3ac3.tar.bz2
powerpc/haleakala: Add network driver
close 1405
Diffstat (limited to 'c/src/lib/libbsp/powerpc/haleakala/mmu')
-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
2 files changed, 364 insertions, 0 deletions
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
+
+
+