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)
This commit is contained in:
Till Straumann
2005-11-03 01:54:59 +00:00
parent 2628ebba04
commit 912ab10e29
4 changed files with 575 additions and 104 deletions

View File

@@ -1,3 +1,11 @@
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)
2005-11-02 straumanatslacdotstanford.edu
* configure.ac, mpc6xx/exceptions/raw_exception.c,

View File

@@ -19,52 +19,469 @@
*
* $Id$
*/
#include <rtems.h>
#include <libcpu/bat.h>
#include <libcpu/spr.h>
#include <rtems/bspIo.h>
typedef union { /* BAT register values to be loaded */
BAT bat;
unsigned int word[2];
}ubat;
#include <libcpu/cpuIdent.h>
typedef struct batrange { /* stores address ranges mapped by BATs */
unsigned long start;
unsigned long limit;
unsigned long phys;
}batrange;
typedef union
{ /* BAT register values to be loaded */
BAT bat;
struct
{
unsigned int u, l;
} words;
} ubat;
batrange bat_addrs[4];
typedef struct batrange
{ /* stores address ranges mapped by BATs */
unsigned long start;
unsigned long limit;
unsigned long phys;
} batrange;
void asm_setdbat0(unsigned int, unsigned int);
void setdbat(int bat_index, unsigned long virt, unsigned long phys,
unsigned int size, int flags)
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;
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);
}
}
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
}
}
/* 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);
}
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;
}

View File

@@ -25,16 +25,53 @@
#include <libcpu/mmu.h>
#include <libcpu/pgtable.h>
#include <rtems/bspIo.h>
#define IO_PAGE (_PAGE_NO_CACHE | _PAGE_GUARDED | _PAGE_RW)
#ifndef ASM
/* Take no risks -- the essential parts of this routine run with
* interrupts disabled!
*
* The routine does basic parameter checks:
* - Index must be 0..3 (0..7 on 7455, 7457).
* If an index > 3 is requested the 745x is
* programmed to enable the higher BATs.
* - Size must be a power of two and <= 1<<28
* (<=1<<31 on 7455, 7457. Also, on these processors
* the special value 0xffffffff is allowed which stands
* for 1<<32).
* If a size > 1<<28 is requested, the 745x is
* programmed to enable the larger block sizes.
* - Bat ranges must not overlap.
* - Physical & virtual addresses must be aligned
* to the size.
*/
extern void setdbat(int bat_index, unsigned long virt, unsigned long phys,
unsigned int size, int flags);
/* read DBAT # 'idx' into *pu/*pl. NULL pointers may be passed.
* If pu and pl are NULL, the bat contents are dumped to the console (printk).
*
* RETURNS: upper BAT contents or (-1) if index is invalid
*/
extern int getdbat(int bat_index, unsigned long *pu, unsigned long *pl);
extern void asm_setdbat0(unsigned int uperPart, unsigned int lowerPart);
extern void asm_setdbat1(unsigned int uperPart, unsigned int lowerPart);
extern void asm_setdbat2(unsigned int uperPart, unsigned int lowerPart);
extern void asm_setdbat3(unsigned int uperPart, unsigned int lowerPart);
extern void asm_setdbat4(unsigned int uperPart, unsigned int lowerPart);
#else
/* Initialize all bats (upper and lower) to zero. This routine should *only*
* be called during early BSP initialization when no C-ABI is available
* yet.
* This routine clobbers r3 and r4.
* NOTE: on 7450 CPUs all 8 dbat/ibat units are cleared. On 601 CPUs only
* 4 ibats.
*/
.globl CPU_clear_bats_early
.type CPU_clear_bats_early,@function
#endif
#endif /* _LIBCPU_BAT_H */

View File

@@ -10,7 +10,7 @@
*
* The license and distribution terms for this file may be
* found in found in the file LICENSE in this distribution or at
* http://www.rtems.com/license/LICENSE.
* http://www.OARcorp.com/rtems/license.html.
*
* T. Straumann - 11/2001: added support for 7400 (no AltiVec yet)
* S.K. Feng - 10/2003: added support for 7455 (no AltiVec yet)
@@ -20,6 +20,7 @@
#include <rtems/asm.h>
#include <rtems/score/cpu.h>
#include <libcpu/io.h>
#include <libcpu/bat.h>
/* Unfortunately, the CPU types defined in cpu.h are
* an 'enum' type and hence not available :-(
@@ -34,6 +35,7 @@
#define PPC_604r 0xA
#define PPC_7400 0xC
#define PPC_7455 0x8001
#define PPC_7457 0x8002
#define PPC_620 0x16
#define PPC_860 0x50
#define PPC_821 PPC_860
@@ -53,76 +55,8 @@
#define DL1HWF (1<<(31-8))
#define L2HWF (1<<(31-20))
/*
* Each setdbat routine start by invalidating the DBAT as some
* proc (604e) request the valid bit set to 0 before accepting
* to write in BAT
*/
#FIXME Should really move this to C code
.globl asm_setdbat0
.type asm_setdbat0,@function
asm_setdbat0:
li r0,0
sync
isync
mtspr DBAT0U,r0
mtspr DBAT0L,r0
sync
isync
mtspr DBAT0L, r4
mtspr DBAT0U, r3
sync
isync
blr
.globl asm_setdbat1
.type asm_setdbat1,@function
asm_setdbat1:
li r0,0
sync
isync
mtspr DBAT1U,r0
mtspr DBAT1L,r0
sync
isync
mtspr DBAT1L, r4
mtspr DBAT1U, r3
sync
isync
blr
.globl asm_setdbat2
.type asm_setdbat2,@function
asm_setdbat2:
li r0,0
sync
isync
mtspr DBAT2U,r0
mtspr DBAT2L,r0
sync
isync
mtspr DBAT2L, r4
mtspr DBAT2U, r3
sync
isync
blr
.globl asm_setdbat3
.type asm_setdbat3,@function
asm_setdbat3:
li r0,0
sync
isync
mtspr DBAT3U,r0
mtspr DBAT3L,r0
sync
isync
mtspr DBAT3L, r4
mtspr DBAT3U, r3
sync
isync
blr
.globl L1_caches_enables
.type L1_caches_enables, @function
@@ -157,7 +91,10 @@ L1_caches_enables:
cmpi 2,r9,PPC_7400 /* or 7400 */
cror 6,6,10
cmpli 0,r9,PPC_7455 /* or 7455 */
bne 2f
beq 1f
cmpli 0,r9,PPC_7457 /* or 7457 */
bne 2f
1:
/* 7455:link register stack,branch folding &
* TBEN : enable the time base and decrementer.
* EMCP bit is defined in HID1. However, it's not used
@@ -168,7 +105,7 @@ L1_caches_enables:
ori r11,r11,(HID0_LRSTK|HID0_FOLD|HID0_TBEN)@l
2: cror 2,2,10
bne 3f
ori r11,r11,HID0_BTIC /* enable branch tgt cache on 7400 & 7455 */
ori r11,r11,HID0_BTIC /* enable branch tgt cache on 7400 , 7455 , 7457 */
3: cror 2,2,6
bne 4f
/* on 7400 SIED is actually SGE (store gathering enable) */
@@ -199,6 +136,8 @@ get_L2CR:
beq 1f
cmplwi r3,PPC_7455 /* it's a 7455 */
beq 1f
cmplwi r3,PPC_7457 /* it's a 7457 */
beq 1f
li r3,-1
blr
@@ -248,6 +187,8 @@ set_L2CR:
beq thisIs750
cmplwi r0,PPC_7455
beq thisIs750
cmplwi r0,PPC_7457
beq thisIs750
li r3,-1
blr
@@ -293,9 +234,12 @@ disableCache:
mtmsr r4
isync /* make sure memory accesses have completed */
cmplwi r0,PPC_7455 /* 7455 ? */
beq 1f
cmplwi r0,PPC_7457 /* 7457 ? */
bne not745x
/* 7455:L1 Load/Flush, L2, L3 : hardware flush */
/* If not using AltiVec data streaming instructions,DSSALL not necessary */
1:
/* 745x:L1 Load/Flush, L2, L3 : hardware flush */
DSSALL
sync
mfspr r4, MSSCR0
rlwinm r4,r4,0,29,0 /* Turn off the L2PFE bits */
@@ -406,6 +350,8 @@ get_L3CR:
rlwinm r3,r3,16,16,31
cmplwi r3,PPC_7455 /* it's a 7455 */
beq 1f
cmplwi r3,PPC_7457 /* it's a 7457 */
beq 1f
li r3,-1
blr
@@ -434,6 +380,8 @@ set_L3CR:
rlwinm r0,r0,16,16,31
cmplwi r0,PPC_7455
beq thisIs7455
cmplwi r0,PPC_7457
beq thisIs7455
li r3,-1
blr
@@ -515,3 +463,64 @@ enableL3Cache:
mtspr L3CR,r3
sync
blr
/*
* An undocumented "feature" of 604e requires that the v bit
* be cleared before changing BAT values.
*
* Also, newer IBM firmware does not clear bat3 and 4 so
* this makes sure it's done.
* -- Cort
*/
.globl CPU_clear_bats_early
.type CPU_clear_bats_early,@function
CPU_clear_bats_early:
li r3,0
mfspr r4,PVR
rlwinm r4,r4,16,16,31 /* r4 = 1 for 601, 4 for 604 */
cmpwi r4, 1
sync
isync
beq 1f
cmplwi r4,0x8001 /* 7445, 7455 (0x8001), 7447, 7457 (0x8002) */
blt 2f /* 7447a (0x8003) and 7448 (0x8004) have 16 bats */
cmplwi r4,0x8004
bgt 2f
mtspr DBAT4U,r3
mtspr DBAT4L,r3
mtspr DBAT5U,r3
mtspr DBAT5L,r3
mtspr DBAT6U,r3
mtspr DBAT6L,r3
mtspr DBAT7U,r3
mtspr DBAT7L,r3
mtspr IBAT4U,r3
mtspr IBAT4L,r3
mtspr IBAT5U,r3
mtspr IBAT5L,r3
mtspr IBAT6U,r3
mtspr IBAT6L,r3
mtspr IBAT7U,r3
mtspr IBAT7L,r3
2:
mtspr DBAT0U,r3
mtspr DBAT0L,r3
mtspr DBAT1U,r3
mtspr DBAT1L,r3
mtspr DBAT2U,r3
mtspr DBAT2L,r3
mtspr DBAT3U,r3
mtspr DBAT3L,r3
1:
mtspr IBAT0U,r3
mtspr IBAT0L,r3
mtspr IBAT1U,r3
mtspr IBAT1L,r3
mtspr IBAT2U,r3
mtspr IBAT2L,r3
mtspr IBAT3U,r3
mtspr IBAT3L,r3
sync
isync
blr