forked from Imagelibrary/rtems
546 lines
15 KiB
C
546 lines
15 KiB
C
/*
|
|
* bat.c
|
|
*
|
|
* This file contains the implementation of C function to
|
|
* Instantiate 60x/7xx ppc Block Address Translation (BAT) registers.
|
|
* More detailed information can be found on motorola
|
|
* site and more precisely in the following book :
|
|
*
|
|
* MPC750
|
|
* Risc Microporcessor User's Manual
|
|
* Mtorola REF : MPC750UM/AD 8/97
|
|
*
|
|
* Copyright (C) 1999 Eric Valette (eric.valette@free.fr)
|
|
* Canon Centre Recherche France.
|
|
*
|
|
* The license and distribution terms for this file may be
|
|
* found in the file LICENSE in this distribution or at
|
|
* http://www.rtems.org/license/LICENSE.
|
|
*/
|
|
#include <rtems.h>
|
|
#include <libcpu/bat.h>
|
|
#include <libcpu/spr.h>
|
|
#include <rtems/bspIo.h>
|
|
|
|
#include <libcpu/cpuIdent.h>
|
|
|
|
#define TYP_I 1
|
|
#define TYP_D 0
|
|
|
|
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[2][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[2] = { 0, 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)
|
|
|
|
static DECL_SETBAT (ibat0, IBAT0)
|
|
static DECL_SETBAT (ibat1, IBAT1)
|
|
static DECL_SETBAT (ibat2, IBAT2)
|
|
static DECL_SETBAT (ibat3, IBAT3)
|
|
static DECL_SETBAT (ibat4, IBAT4)
|
|
static DECL_SETBAT (ibat5, IBAT5)
|
|
static DECL_SETBAT (ibat6, IBAT6)
|
|
static DECL_SETBAT (ibat7, IBAT7)
|
|
|
|
|
|
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)
|
|
:"memory" /* paranoia */
|
|
);
|
|
}
|
|
|
|
static void
|
|
bat_addrs_put (ubat * bat, int typ, int idx)
|
|
{
|
|
unsigned long bl;
|
|
if (bat->bat.batu.vp || bat->bat.batu.vs) {
|
|
bat_addrs[typ][idx].start = bat->bat.batu.bepi << 17;
|
|
bat_addrs[typ][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[typ][idx].limit = bat_addrs[typ][idx].start + bl;
|
|
|
|
bat_in_use[typ] |= (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 (void)
|
|
{
|
|
ubat bat;
|
|
|
|
GETBAT (DBAT0, bat.words.u, bat.words.l);
|
|
bat_addrs_put (&bat, TYP_D, 0);
|
|
GETBAT (DBAT1, bat.words.u, bat.words.l);
|
|
bat_addrs_put (&bat, TYP_D, 1);
|
|
GETBAT (DBAT2, bat.words.u, bat.words.l);
|
|
bat_addrs_put (&bat, TYP_D, 2);
|
|
GETBAT (DBAT3, bat.words.u, bat.words.l);
|
|
bat_addrs_put (&bat, TYP_D, 3);
|
|
|
|
GETBAT (IBAT0, bat.words.u, bat.words.l);
|
|
bat_addrs_put (&bat, TYP_I, 0);
|
|
GETBAT (IBAT1, bat.words.u, bat.words.l);
|
|
bat_addrs_put (&bat, TYP_I, 1);
|
|
GETBAT (IBAT2, bat.words.u, bat.words.l);
|
|
bat_addrs_put (&bat, TYP_I, 2);
|
|
GETBAT (IBAT3, bat.words.u, bat.words.l);
|
|
bat_addrs_put (&bat, TYP_I, 3);
|
|
|
|
|
|
if ( ppc_cpu_has_8_bats() && (HID0_7455_HIGH_BAT_EN & _read_HID0 ())) {
|
|
GETBAT (DBAT4, bat.words.u, bat.words.l);
|
|
bat_addrs_put (&bat, TYP_D, 4);
|
|
GETBAT (DBAT5, bat.words.u, bat.words.l);
|
|
bat_addrs_put (&bat, TYP_D, 5);
|
|
GETBAT (DBAT6, bat.words.u, bat.words.l);
|
|
bat_addrs_put (&bat, TYP_D, 6);
|
|
GETBAT (DBAT7, bat.words.u, bat.words.l);
|
|
bat_addrs_put (&bat, TYP_D, 7);
|
|
GETBAT (IBAT4, bat.words.u, bat.words.l);
|
|
bat_addrs_put (&bat, TYP_I, 4);
|
|
GETBAT (IBAT5, bat.words.u, bat.words.l);
|
|
bat_addrs_put (&bat, TYP_I, 5);
|
|
GETBAT (IBAT6, bat.words.u, bat.words.l);
|
|
bat_addrs_put (&bat, TYP_I, 6);
|
|
GETBAT (IBAT7, bat.words.u, bat.words.l);
|
|
bat_addrs_put (&bat, TYP_I, 7);
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_dssall (void)
|
|
{
|
|
/* 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
|
|
* NOTE: psim doesn't implement dssall so we skip if we run on psim
|
|
*/
|
|
if ( (_read_MSR () & MSR_VE) && PPC_PSIM != get_ppc_cpu_type() ) {
|
|
/* 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 (void)
|
|
{
|
|
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) {
|
|
if ( ! ppc_cpu_has_8_bats() )
|
|
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)) {
|
|
if ( ! ppc_cpu_has_8_bats() )
|
|
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 (int typ, unsigned long start, unsigned long size)
|
|
{
|
|
int i;
|
|
unsigned long limit = start + size - 1;
|
|
for (i = 0; i < sizeof (bat_addrs[typ]) / sizeof (bat_addrs[typ][0]); i++) {
|
|
if (!((1 << i) & bat_in_use[typ]))
|
|
continue; /* unused bat */
|
|
/* safe is 'limit < bat_addrs[t][i].start || start > bat_addrs[t][i].limit */
|
|
if (limit >= bat_addrs[typ][i].start && start <= bat_addrs[typ][i].limit)
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
/* Take no risks -- the essential parts of this routine run with
|
|
* interrupts disabled!
|
|
*/
|
|
|
|
static int
|
|
setbat (int typ, 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;
|
|
|
|
if (check_bat_index (bat_index)) {
|
|
printk ("Invalid BAT index %d\n", bat_index);
|
|
return -1;
|
|
}
|
|
|
|
if ((int) (bl = check_bat_size (size)) < 0) {
|
|
printk ("Invalid BAT size %u\n", size);
|
|
return -1;
|
|
}
|
|
|
|
if (virt & (size - 1)) {
|
|
printk ("BAT effective address 0x%08lx misaligned (size is 0x%08x)\n",
|
|
virt, size);
|
|
return -1;
|
|
}
|
|
|
|
if (phys & (size - 1)) {
|
|
printk ("BAT physical address 0x%08lx misaligned (size is 0x%08x)\n", phys,
|
|
size);
|
|
return -1;
|
|
}
|
|
|
|
if (virt + size - 1 < virt) {
|
|
printk ("BAT range invalid: wraps around zero 0x%08lx..0x%08lx\n", virt,
|
|
virt + size - 1);
|
|
return -1;
|
|
}
|
|
|
|
if ( TYP_I == typ && ( ( _PAGE_GUARDED | _PAGE_WRITETHRU ) & flags ) ) {
|
|
printk("IBAT must not have 'guarded' or 'writethrough' attribute\n");
|
|
return -1;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
}
|
|
|
|
err = check_overlap (typ, virt, size);
|
|
if ((size >= (1 << 17)) && (err >= 0) && (err != bat_index)) {
|
|
rtems_interrupt_enable (level);
|
|
printk ("BATs must not overlap; area 0x%08lx..0x%08lx hits %cBAT %i\n",
|
|
virt, virt + size, (TYP_I == typ ? 'I' : 'D'), err);
|
|
return -1;
|
|
}
|
|
|
|
/* 603, 604, etc. */
|
|
wimgxpp = flags & (_PAGE_WRITETHRU | _PAGE_NO_CACHE
|
|
| _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[typ][bat_index].start = virt;
|
|
bat_addrs[typ][bat_index].limit = virt + ((bl + 1) << 17) - 1;
|
|
bat_addrs[typ][bat_index].phys = phys;
|
|
bat_in_use[typ] |= 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_in_use[typ] &= ~(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[typ][bat_index].limit = virt;
|
|
}
|
|
do_dssall ();
|
|
if ( TYP_I == typ ) {
|
|
switch (bat_index) {
|
|
case 0: asm_setibat0 (bat.words.u, bat.words.l); break;
|
|
case 1: asm_setibat1 (bat.words.u, bat.words.l); break;
|
|
case 2: asm_setibat2 (bat.words.u, bat.words.l); break;
|
|
case 3: asm_setibat3 (bat.words.u, bat.words.l); break;
|
|
/* cpu check already done in check_index */
|
|
case 4: asm_setibat4 (bat.words.u, bat.words.l); break;
|
|
case 5: asm_setibat5 (bat.words.u, bat.words.l); break;
|
|
case 6: asm_setibat6 (bat.words.u, bat.words.l); break;
|
|
case 7: asm_setibat7 (bat.words.u, bat.words.l); break;
|
|
default: /* should never get here anyways */
|
|
break;
|
|
}
|
|
} else {
|
|
switch (bat_index) {
|
|
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);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
getbat (int typ, 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;
|
|
}
|
|
if ( TYP_I == typ ) {
|
|
switch (idx) {
|
|
case 0: GETBAT (IBAT0, u, l); break;
|
|
case 1: GETBAT (IBAT1, u, l); break;
|
|
case 2: GETBAT (IBAT2, u, l); break;
|
|
case 3: GETBAT (IBAT3, u, l); break;
|
|
/* cpu check already done in check_index */
|
|
case 4: GETBAT (IBAT4, u, l); break;
|
|
case 5: GETBAT (IBAT5, u, l); break;
|
|
case 6: GETBAT (IBAT6, u, l); break;
|
|
case 7: GETBAT (IBAT7, u, l); break;
|
|
default: /* should never get here anyways */
|
|
return -1;
|
|
}
|
|
} else {
|
|
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 %cBAT %i contents; UPPER: (0x%08lx)", (TYP_I == typ ? 'I' : 'D'), idx, u);
|
|
printk (" BEPI: 0x%08x", b.bat.batu.bepi);
|
|
printk (" BL: 0x%08lx", (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%08lx)", 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[typ] & (1 << idx))
|
|
printk ("0x%08lx .. 0x%08lx\n", bat_addrs[typ][idx].start,
|
|
bat_addrs[typ][idx].limit);
|
|
else
|
|
printk ("<none> (BAT off)\n");
|
|
|
|
}
|
|
return u;
|
|
}
|
|
|
|
int
|
|
setdbat (int bat_index, unsigned long virt, unsigned long phys,
|
|
unsigned int size, int flags)
|
|
{
|
|
return setbat(TYP_D, bat_index, virt, phys, size, flags);
|
|
}
|
|
|
|
int
|
|
setibat (int bat_index, unsigned long virt, unsigned long phys,
|
|
unsigned int size, int flags)
|
|
{
|
|
return setbat(TYP_I, bat_index, virt, phys, size, flags);
|
|
}
|
|
|
|
int
|
|
getdbat (int idx, unsigned long *pu, unsigned long *pl)
|
|
{
|
|
return getbat (TYP_D, idx, pu, pl);
|
|
}
|
|
|
|
int
|
|
getibat (int idx, unsigned long *pu, unsigned long *pl)
|
|
{
|
|
return getbat (TYP_I, idx, pu, pl);
|
|
}
|