Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:23:58

0001 /*
0002  * bat.c
0003  *
0004  *      This file contains the implementation of C function to
0005  *          Instantiate 60x/7xx ppc Block Address Translation (BAT) registers.
0006  *      More detailed information can be found on motorola
0007  *      site and more precisely in the following book :
0008  *
0009  *      MPC750
0010  *      Risc Microporcessor User's Manual
0011  *      Mtorola REF : MPC750UM/AD 8/97
0012  *
0013  * Copyright (C) 1999  Eric Valette (valette@crf.canon.fr)
0014  *                     Canon Centre Recherche France.
0015  *
0016  *  The license and distribution terms for this file may be
0017  *  found in the file LICENSE in this distribution or at
0018  *  http://www.rtems.org/license/LICENSE.
0019  */
0020 #include <rtems.h>
0021 #include <libcpu/bat.h>
0022 #include <libcpu/spr.h>
0023 #include <rtems/bspIo.h>
0024 
0025 #include <libcpu/cpuIdent.h>
0026 
0027 #define TYP_I   1
0028 #define TYP_D   0
0029 
0030 typedef union
0031 {                               /* BAT register values to be loaded */
0032   BAT bat;
0033   struct
0034   {
0035     unsigned int u, l;
0036   } words;
0037 } ubat;
0038 
0039 typedef struct batrange
0040 {                               /* stores address ranges mapped by BATs */
0041   unsigned long start;
0042   unsigned long limit;
0043   unsigned long phys;
0044 } batrange;
0045 
0046 batrange bat_addrs[2][8] = { { {0,} } };
0047 
0048 /* could encode this in bat_addrs but I don't touch that one for bwds compat. reasons */
0049 /* bitmask of used bats */
0050 static unsigned bat_in_use[2] = { 0, 0 };
0051 
0052 /* define a few macros */
0053 
0054 #define CLRBAT_ASM(batu,r)          \
0055     "   sync                   \n"  \
0056     "   isync                  \n"  \
0057     "   li      "#r ",    0    \n"  \
0058     "   mtspr   "#batu ", "#r "\n"  \
0059     "   sync                   \n"  \
0060     "   isync                  \n"
0061 
0062 #define SETBAT_ASM(batu, batl, u, l)\
0063     "   mtspr   "#batl ", "#l " \n" \
0064     "   sync                    \n" \
0065     "   isync                   \n" \
0066     "   mtspr   "#batu ", "#u " \n" \
0067     "   sync                    \n" \
0068     "   isync                   \n"
0069 
0070 #define CLRBAT(bat)                 \
0071     asm volatile(                   \
0072         CLRBAT_ASM(%0, 0)           \
0073         :                           \
0074         :"i"(bat##U)                \
0075         :"0")
0076 
0077 #define GETBAT(bat,u,l)             \
0078     asm volatile(                   \
0079         "   mfspr %0, %2     \n"    \
0080         "   mfspr %1, %3     \n"    \
0081         :"=r"(u),"=r"(l)            \
0082         :"i"(bat##U),"i"(bat##L)    \
0083         )
0084 
0085 #define DECL_SETBAT(lcbat,bat)      \
0086 void                                \
0087 asm_set##lcbat(unsigned int upper, unsigned int lower)  \
0088 {                                   \
0089 asm volatile(                       \
0090     CLRBAT_ASM(%0,0)                \
0091     SETBAT_ASM(%0,%1,%2,%3)         \
0092     :                               \
0093     :"i"(bat##U),                   \
0094      "i"(bat##L),                   \
0095      "r"(upper),"r"(lower)          \
0096     :"0");                          \
0097 }
0098 
0099 /* export the 'asm' versions for historic reasons */
0100 DECL_SETBAT (dbat0, DBAT0)
0101 DECL_SETBAT (dbat1, DBAT1)
0102 DECL_SETBAT (dbat2, DBAT2)
0103 DECL_SETBAT (dbat3, DBAT3)
0104 
0105 static DECL_SETBAT (dbat4, DBAT4)
0106 static DECL_SETBAT (dbat5, DBAT5)
0107 static DECL_SETBAT (dbat6, DBAT6)
0108 static DECL_SETBAT (dbat7, DBAT7)
0109 
0110 static DECL_SETBAT (ibat0, IBAT0)
0111 static DECL_SETBAT (ibat1, IBAT1)
0112 static DECL_SETBAT (ibat2, IBAT2)
0113 static DECL_SETBAT (ibat3, IBAT3)
0114 static DECL_SETBAT (ibat4, IBAT4)
0115 static DECL_SETBAT (ibat5, IBAT5)
0116 static DECL_SETBAT (ibat6, IBAT6)
0117 static DECL_SETBAT (ibat7, IBAT7)
0118 
0119 
0120 SPR_RO (HID0);
0121 
0122 static void
0123 set_hid0_sync (unsigned long val)
0124 {
0125   __asm__ volatile (
0126     "   sync            \n"
0127     "   isync           \n"
0128     "   mtspr   %0, %1  \n"
0129     "   sync            \n"
0130     "   isync           \n"
0131     :
0132     :"i" (HID0), "r" (val)
0133     :"memory" /* paranoia */
0134   );
0135 }
0136 
0137 static void
0138 bat_addrs_put (ubat * bat, int typ, int idx)
0139 {
0140   unsigned long bl;
0141   if (bat->bat.batu.vp || bat->bat.batu.vs) {
0142     bat_addrs[typ][idx].start = bat->bat.batu.bepi << 17;
0143     bat_addrs[typ][idx].phys = bat->bat.batl.brpn << 17;
0144 
0145     /* extended BL cannot be extracted using BAT union
0146      * - let's just hope the upper bits read 0 on pre 745x
0147      * CPUs.
0148      */
0149     bl = (bat->words.u << 15) | ((1 << 17) - 1);
0150     bat_addrs[typ][idx].limit = bat_addrs[typ][idx].start + bl;
0151 
0152     bat_in_use[typ] |= (1 << idx);
0153   }
0154 }
0155 
0156 /* We don't know how the board was initialized. Therefore,
0157  * when 'setdbat' is first used we must initialize our
0158  * cache.
0159  */
0160 static void
0161 bat_addrs_init (void)
0162 {
0163   ubat bat;
0164 
0165   GETBAT (DBAT0, bat.words.u, bat.words.l);
0166   bat_addrs_put (&bat, TYP_D, 0);
0167   GETBAT (DBAT1, bat.words.u, bat.words.l);
0168   bat_addrs_put (&bat, TYP_D, 1);
0169   GETBAT (DBAT2, bat.words.u, bat.words.l);
0170   bat_addrs_put (&bat, TYP_D, 2);
0171   GETBAT (DBAT3, bat.words.u, bat.words.l);
0172   bat_addrs_put (&bat, TYP_D, 3);
0173 
0174   GETBAT (IBAT0, bat.words.u, bat.words.l);
0175   bat_addrs_put (&bat, TYP_I, 0);
0176   GETBAT (IBAT1, bat.words.u, bat.words.l);
0177   bat_addrs_put (&bat, TYP_I, 1);
0178   GETBAT (IBAT2, bat.words.u, bat.words.l);
0179   bat_addrs_put (&bat, TYP_I, 2);
0180   GETBAT (IBAT3, bat.words.u, bat.words.l);
0181   bat_addrs_put (&bat, TYP_I, 3);
0182 
0183 
0184   if ( ppc_cpu_has_8_bats() && (HID0_7455_HIGH_BAT_EN & _read_HID0 ())) {
0185     GETBAT (DBAT4, bat.words.u, bat.words.l);
0186     bat_addrs_put (&bat, TYP_D, 4);
0187     GETBAT (DBAT5, bat.words.u, bat.words.l);
0188     bat_addrs_put (&bat, TYP_D, 5);
0189     GETBAT (DBAT6, bat.words.u, bat.words.l);
0190     bat_addrs_put (&bat, TYP_D, 6);
0191     GETBAT (DBAT7, bat.words.u, bat.words.l);
0192     bat_addrs_put (&bat, TYP_D, 7);
0193     GETBAT (IBAT4, bat.words.u, bat.words.l);
0194     bat_addrs_put (&bat, TYP_I, 4);
0195     GETBAT (IBAT5, bat.words.u, bat.words.l);
0196     bat_addrs_put (&bat, TYP_I, 5);
0197     GETBAT (IBAT6, bat.words.u, bat.words.l);
0198     bat_addrs_put (&bat, TYP_I, 6);
0199     GETBAT (IBAT7, bat.words.u, bat.words.l);
0200     bat_addrs_put (&bat, TYP_I, 7);
0201   }
0202 }
0203 
0204 static void
0205 do_dssall (void)
0206 {
0207   /* Before changing BATs, 'dssall' must be issued.
0208    * We check MSR for MSR_VE and issue a 'dssall' if
0209    * MSR_VE is set hoping that
0210    *  a) on non-altivec CPUs MSR_VE reads as zero
0211    *  b) all altivec CPUs use the same bit
0212    * NOTE: psim doesn't implement dssall so we skip if we run on psim
0213    */
0214   if ( (_read_MSR () & MSR_VE) && PPC_PSIM != get_ppc_cpu_type() ) {
0215     /* this construct is needed because we don't know
0216      * if this file is compiled with -maltivec.
0217      * (I plan to add altivec support outside of
0218      * RTEMS core and hence I'd rather not
0219      * rely on consistent compiler flags).
0220      */
0221 #define DSSALL  0x7e00066c      /* dssall opcode */
0222     __asm__ volatile (" .long %0"::"i" (DSSALL));
0223 #undef  DSSALL
0224   }
0225 }
0226 
0227 /* Clear I/D bats 4..7 ONLY ON 7455 etc.  */
0228 static void
0229 clear_hi_bats (void)
0230 {
0231   do_dssall ();
0232   CLRBAT (DBAT4);
0233   CLRBAT (DBAT5);
0234   CLRBAT (DBAT6);
0235   CLRBAT (DBAT7);
0236   CLRBAT (IBAT4);
0237   CLRBAT (IBAT5);
0238   CLRBAT (IBAT6);
0239   CLRBAT (IBAT7);
0240 }
0241 
0242 static int
0243 check_bat_index (int i)
0244 {
0245   unsigned long hid0;
0246 
0247   if (i >= 0 && i < 4)
0248     return 0;
0249   if (i >= 4 && i < 8) {
0250     if ( ! ppc_cpu_has_8_bats() )
0251       return -1;
0252     /* OK, we're on the right hardware;
0253      * check if we are already enabled
0254      */
0255     hid0 = _read_HID0 ();
0256     if (HID0_7455_HIGH_BAT_EN & hid0)
0257       return 0;
0258     /* No; enable now */
0259     clear_hi_bats ();
0260     set_hid0_sync (hid0 | HID0_7455_HIGH_BAT_EN);
0261     return 0;
0262   }
0263   return -1;
0264 }
0265 
0266 /* size argument check:
0267  *  - must be a power of two or zero
0268  *  - must be <= 1<<28 ( non 745x cpu )
0269  *  - can be 1<<29..1<31 or 0xffffffff on 745x
0270  *  - size < 1<<17 means 0
0271  * computes and returns the block mask
0272  * RETURNS:
0273  *  block mask on success or -1 on error
0274  */
0275 static int
0276 check_bat_size (unsigned long size)
0277 {
0278   unsigned long bit;
0279   unsigned long hid0;
0280 
0281   /* First of all, it must be a power of two */
0282   if (0 == size)
0283     return 0;
0284 
0285   if (0xffffffff == size) {
0286     bit = 32;
0287   } else {
0288     __asm__ volatile (" cntlzw %0, %1":"=r" (bit):"r" (size));
0289     bit = 31 - bit;
0290     if (1 << bit != size)
0291       return -1;
0292   }
0293   /* bit < 17 is not really legal but we aliased it to 0 in the past */
0294   if (bit > (11 + 17)) {
0295     if ( ! ppc_cpu_has_8_bats() )
0296       return -1;
0297 
0298     hid0 = _read_HID0 ();
0299     /* Let's enable the larger block size if necessary */
0300     if (!(HID0_7455_XBSEN & hid0))
0301       set_hid0_sync (hid0 | HID0_7455_XBSEN);
0302   }
0303 
0304   return (1 << (bit - 17)) - 1;
0305 }
0306 
0307 static int
0308 check_overlap (int typ, unsigned long start, unsigned long size)
0309 {
0310   int i;
0311   unsigned long limit = start + size - 1;
0312   for (i = 0; i < sizeof (bat_addrs[typ]) / sizeof (bat_addrs[typ][0]); i++) {
0313     if (!((1 << i) & bat_in_use[typ]))
0314       continue;                 /* unused bat */
0315     /* safe is 'limit < bat_addrs[t][i].start || start > bat_addrs[t][i].limit */
0316     if (limit >= bat_addrs[typ][i].start && start <= bat_addrs[typ][i].limit)
0317       return i;
0318   }
0319   return -1;
0320 }
0321 
0322 
0323 /* Take no risks -- the essential parts of this routine run with
0324  * interrupts disabled!
0325  */
0326 
0327 static int
0328 setbat (int typ, int bat_index, unsigned long virt, unsigned long phys,
0329          unsigned int size, int flags)
0330 {
0331   unsigned long level;
0332   unsigned int bl;
0333   int err;
0334   int wimgxpp;
0335   ubat bat;
0336 
0337   if (check_bat_index (bat_index)) {
0338     printk ("Invalid BAT index %d\n", bat_index);
0339     return -1;
0340   }
0341 
0342   if ((int) (bl = check_bat_size (size)) < 0) {
0343     printk ("Invalid BAT size %u\n", size);
0344     return -1;
0345   }
0346 
0347   if (virt & (size - 1)) {
0348     printk ("BAT effective address 0x%08lx misaligned (size is 0x%08x)\n",
0349             virt, size);
0350     return -1;
0351   }
0352 
0353   if (phys & (size - 1)) {
0354     printk ("BAT physical address 0x%08lx misaligned (size is 0x%08x)\n", phys,
0355             size);
0356     return -1;
0357   }
0358 
0359   if (virt + size - 1 < virt) {
0360     printk ("BAT range invalid: wraps around zero 0x%08lx..0x%08lx\n", virt,
0361             virt + size - 1);
0362     return -1;
0363   }
0364 
0365   if ( TYP_I == typ && ( ( _PAGE_GUARDED | _PAGE_WRITETHRU ) & flags ) ) {
0366     printk("IBAT must not have 'guarded' or 'writethrough' attribute\n");
0367     return -1;
0368   }
0369 
0370 /* must protect the bat_addrs table -- since this routine is only used for board setup
0371  * or similar special purposes we don't bother about interrupt latency too much.
0372  */
0373   rtems_interrupt_disable (level);
0374 
0375   {                             /* might have to initialize our cached data */
0376     static char init_done = 0;
0377     if (!init_done) {
0378       bat_addrs_init ();
0379       init_done = 1;
0380     }
0381   }
0382 
0383   err = check_overlap (typ, virt, size);
0384   if ((size >= (1 << 17)) && (err >= 0) && (err != bat_index)) {
0385     rtems_interrupt_enable (level);
0386     printk ("BATs must not overlap; area 0x%08lx..0x%08lx hits %cBAT %i\n",
0387             virt, virt + size, (TYP_I == typ ? 'I' : 'D'), err);
0388     return -1;
0389   }
0390 
0391   /* 603, 604, etc. */
0392   wimgxpp = flags & (_PAGE_WRITETHRU | _PAGE_NO_CACHE
0393                      | _PAGE_COHERENT | _PAGE_GUARDED);
0394   wimgxpp |= (flags & _PAGE_RW) ? BPP_RW : BPP_RX;
0395   bat.words.u = virt | (bl << 2) | 2;   /* Vs=1, Vp=0 */
0396   bat.words.l = phys | wimgxpp;
0397   if (flags & _PAGE_USER)
0398     bat.bat.batu.vp = 1;
0399   bat_addrs[typ][bat_index].start = virt;
0400   bat_addrs[typ][bat_index].limit = virt + ((bl + 1) << 17) - 1;
0401   bat_addrs[typ][bat_index].phys = phys;
0402   bat_in_use[typ] |= 1 << bat_index;
0403   if (size < (1 << 17)) {
0404     /* size of 0 tells us to switch it off */
0405     bat.bat.batu.vp = 0;
0406     bat.bat.batu.vs = 0;
0407     bat_in_use[typ] &= ~(1 << bat_index);
0408     /* mimic old behavior when bl was 0 (bs==0 is actually legal; it doesnt
0409      * indicate a size of zero. We now accept bl==0 and look at the size.
0410      */
0411     bat_addrs[typ][bat_index].limit = virt;
0412   }
0413   do_dssall ();
0414   if ( TYP_I == typ ) {
0415       switch (bat_index) {
0416           case 0: asm_setibat0 (bat.words.u, bat.words.l); break;
0417           case 1: asm_setibat1 (bat.words.u, bat.words.l); break;
0418           case 2: asm_setibat2 (bat.words.u, bat.words.l); break;
0419           case 3: asm_setibat3 (bat.words.u, bat.words.l); break;
0420               /* cpu check already done in check_index */
0421           case 4: asm_setibat4 (bat.words.u, bat.words.l); break;
0422           case 5: asm_setibat5 (bat.words.u, bat.words.l); break;
0423           case 6: asm_setibat6 (bat.words.u, bat.words.l); break;
0424           case 7: asm_setibat7 (bat.words.u, bat.words.l); break;
0425           default:                     /* should never get here anyways */
0426               break;
0427       }
0428   } else {
0429       switch (bat_index) {
0430           case 0: asm_setdbat0 (bat.words.u, bat.words.l); break;
0431           case 1: asm_setdbat1 (bat.words.u, bat.words.l); break;
0432           case 2: asm_setdbat2 (bat.words.u, bat.words.l); break;
0433           case 3: asm_setdbat3 (bat.words.u, bat.words.l); break;
0434               /* cpu check already done in check_index */
0435           case 4: asm_setdbat4 (bat.words.u, bat.words.l); break;
0436           case 5: asm_setdbat5 (bat.words.u, bat.words.l); break;
0437           case 6: asm_setdbat6 (bat.words.u, bat.words.l); break;
0438           case 7: asm_setdbat7 (bat.words.u, bat.words.l); break;
0439           default:                     /* should never get here anyways */
0440               break;
0441       }
0442   }
0443   rtems_interrupt_enable (level);
0444 
0445   return 0;
0446 }
0447 
0448 static int
0449 getbat (int typ, int idx, unsigned long *pu, unsigned long *pl)
0450 {
0451   unsigned long u, l;
0452 
0453   if (check_bat_index (idx)) {
0454     printk ("Invalid BAT #%i\n", idx);
0455     return -1;
0456   }
0457   if ( TYP_I == typ ) {
0458       switch (idx) {
0459           case 0: GETBAT (IBAT0, u, l); break;
0460           case 1: GETBAT (IBAT1, u, l); break;
0461           case 2: GETBAT (IBAT2, u, l); break;
0462           case 3: GETBAT (IBAT3, u, l); break;
0463                   /* cpu check already done in check_index */
0464           case 4: GETBAT (IBAT4, u, l); break;
0465           case 5: GETBAT (IBAT5, u, l); break;
0466           case 6: GETBAT (IBAT6, u, l); break;
0467           case 7: GETBAT (IBAT7, u, l); break;
0468           default:                     /* should never get here anyways */
0469                   return -1;
0470       }
0471   } else {
0472       switch (idx) {
0473           case 0: GETBAT (DBAT0, u, l); break;
0474           case 1: GETBAT (DBAT1, u, l); break;
0475           case 2: GETBAT (DBAT2, u, l); break;
0476           case 3: GETBAT (DBAT3, u, l); break;
0477                   /* cpu check already done in check_index */
0478           case 4: GETBAT (DBAT4, u, l); break;
0479           case 5: GETBAT (DBAT5, u, l); break;
0480           case 6: GETBAT (DBAT6, u, l); break;
0481           case 7: GETBAT (DBAT7, u, l); break;
0482           default:                     /* should never get here anyways */
0483                   return -1;
0484       }
0485   }
0486   if (pu) {
0487     *pu = u;
0488   }
0489   if (pl) {
0490     *pl = l;
0491   }
0492 
0493   if (!pu && !pl) {
0494     /* dump */
0495     ubat b;
0496     b.words.u = u;
0497     b.words.l = l;
0498     printk ("Raw %cBAT %i contents; UPPER: (0x%08lx)", (TYP_I == typ ? 'I' : 'D'), idx, u);
0499     printk (" BEPI: 0x%08x", b.bat.batu.bepi);
0500     printk (" BL: 0x%08lx", (u >> 2) & ((1 << 15) - 1));
0501     printk (" VS: 0b%i", b.bat.batu.vs);
0502     printk (" VP: 0b%i", b.bat.batu.vp);
0503     printk ("\n");
0504     printk ("                     LOWER: (0x%08lx)", l);
0505     printk ("  RPN: 0x%08x", b.bat.batl.brpn);
0506     printk (" wimg:   0b%1i%1i%1i%1i", b.bat.batl.w, b.bat.batl.i,
0507             b.bat.batl.m, b.bat.batl.g);
0508     printk (" PP: 0x%1x", b.bat.batl.pp);
0509     printk ("\n");
0510     printk ("Covering EA Range: ");
0511     if (bat_in_use[typ] & (1 << idx))
0512       printk ("0x%08lx .. 0x%08lx\n", bat_addrs[typ][idx].start,
0513               bat_addrs[typ][idx].limit);
0514     else
0515       printk ("<none> (BAT off)\n");
0516 
0517   }
0518   return u;
0519 }
0520 
0521 int
0522 setdbat (int bat_index, unsigned long virt, unsigned long phys,
0523          unsigned int size, int flags)
0524 {
0525     return setbat(TYP_D, bat_index, virt, phys, size, flags);
0526 }
0527 
0528 int
0529 setibat (int bat_index, unsigned long virt, unsigned long phys,
0530          unsigned int size, int flags)
0531 {
0532     return setbat(TYP_I, bat_index, virt, phys, size, flags);
0533 }
0534 
0535 int
0536 getdbat (int idx, unsigned long *pu, unsigned long *pl)
0537 {
0538     return getbat (TYP_D, idx, pu, pl);
0539 }
0540 
0541 int
0542 getibat (int idx, unsigned long *pu, unsigned long *pl)
0543 {
0544     return getbat (TYP_I, idx, pu, pl);
0545 }