diff options
author | Till Straumann <strauman@slac.stanford.edu> | 2005-11-03 01:54:59 +0000 |
---|---|---|
committer | Till Straumann <strauman@slac.stanford.edu> | 2005-11-03 01:54:59 +0000 |
commit | 912ab10e29e318802da485772252d8d9962b5363 (patch) | |
tree | 8cc907627297e181ce249b5ab6f454ec03059de2 /c/src/lib/libcpu/powerpc/mpc6xx/mmu/bat.c | |
parent | 2005-11-02 straumanatslacdotstanford.edu (diff) | |
download | rtems-912ab10e29e318802da485772252d8d9962b5363.tar.bz2 |
2005-11-02 straumanatslacdotstanford.edu
* mpc6xx/mmu/bat.c, mpc6xx/mmu/bat.h, mpc6xx/mmu/mmuAsm.S: moved
assembly code to C; setdbat now supports high bats on 7450 CPUs;
added argument checking to setdbat; added getdbat; moved early
initialization code (clear_bats) from BSP to libcpu
(CPU_clear_bats_early)
Diffstat (limited to 'c/src/lib/libcpu/powerpc/mpc6xx/mmu/bat.c')
-rw-r--r-- | c/src/lib/libcpu/powerpc/mpc6xx/mmu/bat.c | 473 |
1 files changed, 445 insertions, 28 deletions
diff --git a/c/src/lib/libcpu/powerpc/mpc6xx/mmu/bat.c b/c/src/lib/libcpu/powerpc/mpc6xx/mmu/bat.c index e706256535..42071a7642 100644 --- a/c/src/lib/libcpu/powerpc/mpc6xx/mmu/bat.c +++ b/c/src/lib/libcpu/powerpc/mpc6xx/mmu/bat.c @@ -19,52 +19,469 @@ * * $Id$ */ - +#include <rtems.h> #include <libcpu/bat.h> +#include <libcpu/spr.h> +#include <rtems/bspIo.h> + +#include <libcpu/cpuIdent.h> + +typedef union +{ /* BAT register values to be loaded */ + BAT bat; + struct + { + unsigned int u, l; + } words; +} ubat; + +typedef struct batrange +{ /* stores address ranges mapped by BATs */ + unsigned long start; + unsigned long limit; + unsigned long phys; +} batrange; + +batrange bat_addrs[8] = { {0,} }; + +/* could encode this in bat_addrs but I don't touch that one for bwds compat. reasons */ +/* bitmask of used bats */ +static unsigned bat_in_use = 0; + +/* define a few macros */ + +#define CLRBAT_ASM(batu,r) \ + " sync \n" \ + " isync \n" \ + " li "#r", 0 \n" \ + " mtspr "#batu", "#r"\n" \ + " sync \n" \ + " isync \n" + +#define SETBAT_ASM(batu, batl, u, l)\ + " mtspr "#batl", "#l" \n" \ + " sync \n" \ + " isync \n" \ + " mtspr "#batu", "#u" \n" \ + " sync \n" \ + " isync \n" + +#define CLRBAT(bat) \ + asm volatile( \ + CLRBAT_ASM(%0, 0) \ + : \ + :"i"(bat##U) \ + :"0") + +#define GETBAT(bat,u,l) \ + asm volatile( \ + " mfspr %0, %2 \n" \ + " mfspr %1, %3 \n" \ + :"=r"(u),"=r"(l) \ + :"i"(bat##U),"i"(bat##L) \ + ) + +#define DECL_SETBAT(lcbat,bat) \ +void \ +asm_set##lcbat(unsigned int upper, unsigned int lower) \ +{ \ +asm volatile( \ + CLRBAT_ASM(%0,0) \ + SETBAT_ASM(%0,%1,%2,%3) \ + : \ + :"i"(bat##U), \ + "i"(bat##L), \ + "r"(upper),"r"(lower) \ + :"0"); \ +} + +/* export the 'asm' versions for historic reasons */ +DECL_SETBAT (dbat0, DBAT0) +DECL_SETBAT (dbat1, DBAT1) +DECL_SETBAT (dbat2, DBAT2) +DECL_SETBAT (dbat3, DBAT3) + +static DECL_SETBAT (dbat4, DBAT4) +static DECL_SETBAT (dbat5, DBAT5) +static DECL_SETBAT (dbat6, DBAT6) +static DECL_SETBAT (dbat7, DBAT7) + +SPR_RO (HID0); + +static void +set_hid0_sync (unsigned long val) +{ + asm volatile ( + " sync \n" + " isync \n" + " mtspr %0, %1 \n" + " sync \n" + " isync \n" + : + :"i" (HID0), "r" (val) + ); +} + +static void +bat_addrs_put (ubat * bat, int idx) +{ + unsigned long bl; + if (bat->bat.batu.vp || bat->bat.batu.vs) { + bat_addrs[idx].start = bat->bat.batu.bepi << 17; + bat_addrs[idx].phys = bat->bat.batl.brpn << 17; + + /* extended BL cannot be extracted using BAT union + * - let's just hope the upper bits read 0 on pre 745x + * CPUs. + */ + bl = (bat->words.u << 15) | ((1 << 17) - 1); + bat_addrs[idx].limit = bat_addrs[idx].start + bl; -typedef union { /* BAT register values to be loaded */ - BAT bat; - unsigned int word[2]; -}ubat; + bat_in_use |= (1 << idx); + } +} + +/* We don't know how the board was initialized. Therefore, + * when 'setdbat' is first used we must initialize our + * cache. + */ +static void +bat_addrs_init () +{ + ppc_cpu_id_t cpu = get_ppc_cpu_type (); + + ubat bat; + + GETBAT (DBAT0, bat.words.u, bat.words.l); + bat_addrs_put (&bat, 0); + GETBAT (DBAT1, bat.words.u, bat.words.l); + bat_addrs_put (&bat, 1); + GETBAT (DBAT2, bat.words.u, bat.words.l); + bat_addrs_put (&bat, 2); + GETBAT (DBAT3, bat.words.u, bat.words.l); + bat_addrs_put (&bat, 3); + + if ((cpu == PPC_7455 || cpu == PPC_7457) + && (HID0_7455_HIGH_BAT_EN & _read_HID0 ())) { + GETBAT (DBAT4, bat.words.u, bat.words.l); + bat_addrs_put (&bat, 4); + GETBAT (DBAT5, bat.words.u, bat.words.l); + bat_addrs_put (&bat, 5); + GETBAT (DBAT6, bat.words.u, bat.words.l); + bat_addrs_put (&bat, 6); + GETBAT (DBAT7, bat.words.u, bat.words.l); + bat_addrs_put (&bat, 7); + } +} -typedef struct batrange { /* stores address ranges mapped by BATs */ - unsigned long start; - unsigned long limit; - unsigned long phys; -}batrange; +static void +do_dssall () +{ + /* Before changing BATs, 'dssall' must be issued. + * We check MSR for MSR_VE and issue a 'dssall' if + * MSR_VE is set hoping that + * a) on non-altivec CPUs MSR_VE reads as zero + * b) all altivec CPUs use the same bit + */ + if (_read_MSR () & MSR_VE) { + /* this construct is needed because we don't know + * if this file is compiled with -maltivec. + * (I plan to add altivec support outside of + * RTEMS core and hence I'd rather not + * rely on consistent compiler flags). + */ +#define DSSALL 0x7e00066c /* dssall opcode */ + asm volatile (" .long %0"::"i" (DSSALL)); +#undef DSSALL + } +} -batrange bat_addrs[4]; +/* Clear I/D bats 4..7 ONLY ON 7455 etc. */ +static void +clear_hi_bats () +{ + do_dssall (); + CLRBAT (DBAT4); + CLRBAT (DBAT5); + CLRBAT (DBAT6); + CLRBAT (DBAT7); + CLRBAT (IBAT4); + CLRBAT (IBAT5); + CLRBAT (IBAT6); + CLRBAT (IBAT7); +} -void asm_setdbat0(unsigned int, unsigned int); -void setdbat(int bat_index, unsigned long virt, unsigned long phys, - unsigned int size, int flags) +static int +check_bat_index (int i) +{ + unsigned long hid0; + + if (i >= 0 && i < 4) + return 0; + if (i >= 4 && i < 8) { + /* don't use current_ppc_cpu because we don't know if it has been set already */ + ppc_cpu_id_t cpu = get_ppc_cpu_type (); + if (cpu != PPC_7455 && cpu != PPC_7457) + return -1; + /* OK, we're on the right hardware; + * check if we are already enabled + */ + hid0 = _read_HID0 (); + if (HID0_7455_HIGH_BAT_EN & hid0) + return 0; + /* No; enable now */ + clear_hi_bats (); + set_hid0_sync (hid0 | HID0_7455_HIGH_BAT_EN); + return 0; + } + return -1; +} + +/* size argument check: + * - must be a power of two or zero + * - must be <= 1<<28 ( non 745x cpu ) + * - can be 1<<29..1<31 or 0xffffffff on 745x + * - size < 1<<17 means 0 + * computes and returns the block mask + * RETURNS: + * block mask on success or -1 on error + */ +static int +check_bat_size (unsigned long size) { + unsigned long bit; + unsigned long hid0; + + /* First of all, it must be a power of two */ + if (0 == size) + return 0; + + if (0xffffffff == size) { + bit = 32; + } else { + asm volatile (" cntlzw %0, %1":"=r" (bit):"r" (size)); + bit = 31 - bit; + if (1 << bit != size) + return -1; + } + /* bit < 17 is not really legal but we aliased it to 0 in the past */ + if (bit > (11 + 17)) { + /* don't use current_ppc_cpu because we don't know if it has been set already */ + ppc_cpu_id_t cpu = get_ppc_cpu_type (); + if (cpu != PPC_7455 && cpu != PPC_7457) + return -1; + + hid0 = _read_HID0 (); + /* Let's enable the larger block size if necessary */ + if (!(HID0_7455_XBSEN & hid0)) + set_hid0_sync (hid0 | HID0_7455_XBSEN); + } + + return (1 << (bit - 17)) - 1; +} + +static int +check_overlap (unsigned long start, unsigned long size) +{ + int i; + unsigned long limit = start + size - 1; + for (i = 0; i < sizeof (bat_addrs) / sizeof (bat_addrs[0]); i++) { + if (!((1 << i) & bat_in_use)) + continue; /* unused bat */ + /* safe is 'limit < bat_addrs[i].start || start > bat_addrs[i].limit */ + if (limit >= bat_addrs[i].start && start <= bat_addrs[i].limit) + return i; + } + return -1; +} + + +/* Take no risks -- the essential parts of this routine run with + * interrupts disabled! + */ + +void +setdbat (int bat_index, unsigned long virt, unsigned long phys, + unsigned int size, int flags) +{ + unsigned long level; unsigned int bl; + int err; int wimgxpp; ubat bat; - bl = (size >= (1<<17)) ? (size >> 17) - 1 : 0; + if (check_bat_index (bat_index)) { + printk ("Invalid BAT index\n", bat_index); + return; + } + + if ((int) (bl = check_bat_size (size)) < 0) { + printk ("Invalid BAT size\n", size); + return; + } + + if (virt & (size - 1)) { + printk ("BAT effective address 0x%08x misaligned (size is 0x%08x)\n", + virt, size); + return; + } + + if (phys & (size - 1)) { + printk ("BAT physical address 0x%08x misaligned (size is 0x%08x)\n", phys, + size); + return; + } + + if (virt + size - 1 < virt) { + printk ("BAT range invalid: wraps around zero 0x%08x..0x%08x\n", virt, + virt + size - 1); + return; + } + +/* must protect the bat_addrs table -- since this routine is only used for board setup + * or similar special purposes we don't bother about interrupt latency too much. + */ + rtems_interrupt_disable (level); + + { /* might have to initialize our cached data */ + static char init_done = 0; + if (!init_done) { + bat_addrs_init (); + init_done = 1; + } + } + + if (size >= (1 << 17) && (err = check_overlap (virt, size)) >= 0) { + rtems_interrupt_enable (level); + printk ("BATs must not overlap; area 0x%08x..0x%08x hits BAT %i\n", + virt, virt + size, err); + return; + } + /* 603, 604, etc. */ wimgxpp = flags & (_PAGE_WRITETHRU | _PAGE_NO_CACHE - | _PAGE_COHERENT | _PAGE_GUARDED); - wimgxpp |= (flags & _PAGE_RW)? BPP_RW: BPP_RX; - bat.word[0] = virt | (bl << 2) | 2; /* Vs=1, Vp=0 */ - bat.word[1] = phys | wimgxpp; + | _PAGE_COHERENT | _PAGE_GUARDED); + wimgxpp |= (flags & _PAGE_RW) ? BPP_RW : BPP_RX; + bat.words.u = virt | (bl << 2) | 2; /* Vs=1, Vp=0 */ + bat.words.l = phys | wimgxpp; if (flags & _PAGE_USER) bat.bat.batu.vp = 1; bat_addrs[bat_index].start = virt; - bat_addrs[bat_index].limit = virt + (bl ? ((bl + 1) << 17) - 1 : 0); + bat_addrs[bat_index].limit = virt + ((bl + 1) << 17) - 1; bat_addrs[bat_index].phys = phys; - if ( 0 == bl ) { + bat_in_use |= 1 << bat_index; + if (size < (1 << 17)) { /* size of 0 tells us to switch it off */ - bat.bat.batu.vp = 0; - bat.bat.batu.vs = 0; + bat.bat.batu.vp = 0; + bat.bat.batu.vs = 0; + bat_in_use &= ~(1 << bat_index); + /* mimic old behavior when bl was 0 (bs==0 is actually legal; it doesnt + * indicate a size of zero. We now accept bl==0 and look at the size. + */ + bat_addrs[bat_index].limit = virt; } + do_dssall (); switch (bat_index) { - case 0 : asm_setdbat0(bat.word[0], bat.word[1]); break; - case 1 : asm_setdbat1(bat.word[0], bat.word[1]); break; - case 2 : asm_setdbat2(bat.word[0], bat.word[1]); break; - case 3 : asm_setdbat3(bat.word[0], bat.word[1]); break; - default: printk("bat.c : invalid BAT bat_index\n"); + case 0: + asm_setdbat0 (bat.words.u, bat.words.l); + break; + case 1: + asm_setdbat1 (bat.words.u, bat.words.l); + break; + case 2: + asm_setdbat2 (bat.words.u, bat.words.l); + break; + case 3: + asm_setdbat3 (bat.words.u, bat.words.l); + break; + /* cpu check already done in check_index */ + case 4: + asm_setdbat4 (bat.words.u, bat.words.l); + break; + case 5: + asm_setdbat5 (bat.words.u, bat.words.l); + break; + case 6: + asm_setdbat6 (bat.words.u, bat.words.l); + break; + case 7: + asm_setdbat7 (bat.words.u, bat.words.l); + break; + default: /* should never get here anyways */ + break; + } + rtems_interrupt_enable (level); +} + +int +getdbat (int idx, unsigned long *pu, unsigned long *pl) +{ + unsigned long u, l; + + if (check_bat_index (idx)) { + printk ("Invalid BAT #%i\n", idx); + return -1; + } + switch (idx) { + case 0: + GETBAT (DBAT0, u, l); + break; + case 1: + GETBAT (DBAT1, u, l); + break; + case 2: + GETBAT (DBAT2, u, l); + break; + case 3: + GETBAT (DBAT3, u, l); + break; + /* cpu check already done in check_index */ + case 4: + GETBAT (DBAT4, u, l); + break; + case 5: + GETBAT (DBAT5, u, l); + break; + case 6: + GETBAT (DBAT6, u, l); + break; + case 7: + GETBAT (DBAT7, u, l); + break; + default: /* should never get here anyways */ + return -1; + } + if (pu) { + *pu = u; + } + if (pl) { + *pl = l; + } + + if (!pu && !pl) { + /* dump */ + ubat b; + b.words.u = u; + b.words.l = l; + printk ("Raw DBAT %i contents; UPPER: (0x%08x)", idx, u); + printk (" BEPI: 0x%08x", b.bat.batu.bepi); + printk (" BL: 0x%08x", (u >> 2) & ((1 << 15) - 1)); + printk (" VS: 0b%i", b.bat.batu.vs); + printk (" VP: 0b%i", b.bat.batu.vp); + printk ("\n"); + printk (" LOWER: (0x%08x)", l); + printk (" RPN: 0x%08x", b.bat.batl.brpn); + printk (" wimg: 0b%1i%1i%1i%1i", b.bat.batl.w, b.bat.batl.i, + b.bat.batl.m, b.bat.batl.g); + printk (" PP: 0x%1x", b.bat.batl.pp); + printk ("\n"); + printk ("Covering EA Range: "); + if (bat_in_use & (1 << idx)) + printk ("0x%08x .. 0x%08x\n", bat_addrs[idx].start, + bat_addrs[idx].limit); + else + printk ("<none> (BAT off)\n"); + } + return u; } |