diff options
author | Nigel Spon <nigel@adi.co.nz> | 2014-11-21 11:15:22 -0600 |
---|---|---|
committer | Joel Sherrill <joel.sherrill@oarcorp.com> | 2014-11-21 13:47:42 -0600 |
commit | 502609c80d5f8c5ec852845c4632c905811b3ac3 (patch) | |
tree | 3ca172b6f6017f03df58aa81f4e3c7567bf95f82 /c/src/lib/libbsp/powerpc/haleakala/mmu | |
parent | objectsetname.c: Fix always true condition (Coverity ID 1063874) (diff) | |
download | rtems-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.c | 281 | ||||
-rw-r--r-- | c/src/lib/libbsp/powerpc/haleakala/mmu/mmu_405asm.S | 83 |
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 + + + |