source: rtems/c/src/lib/libcpu/powerpc/mpc6xx/mmu/bat.c @ 10098f9

4.104.115
Last change on this file since 10098f9 was 10098f9, checked in by Till Straumann <strauman@…>, on Dec 2, 2009 at 12:22:42 AM

2009-12-01 Till Straumann <strauman@…>

  • mpc6xx/mmu/bat.c, mpc6xx/mmu/pte121.c: skip data- streaming (dssall etc.) instructions on PPC_PSIM currently (unimplemented by PSIM :-( ).
  • Property mode set to 100644
File size: 14.6 KB
Line 
1/*
2 * bat.c
3 *
4 *          This file contains the implementation of C function to
5 *          Instantiate 60x/7xx ppc Block Address Translation (BAT) registers.
6 *          More detailed information can be found on motorola
7 *          site and more precisely in the following book :
8 *
9 *              MPC750
10 *              Risc Microporcessor User's Manual
11 *              Mtorola REF : MPC750UM/AD 8/97
12 *
13 * Copyright (C) 1999  Eric Valette (valette@crf.canon.fr)
14 *                     Canon Centre Recherche France.
15 *
16 *  The license and distribution terms for this file may be
17 *  found in found in the file LICENSE in this distribution or at
18 *  http://www.rtems.com/license/LICENSE.
19 *
20 * $Id$
21 */
22#include <rtems.h>
23#include <libcpu/bat.h>
24#include <libcpu/spr.h>
25#include <rtems/bspIo.h>
26
27#include <libcpu/cpuIdent.h>
28
29#define TYP_I   1
30#define TYP_D   0
31
32typedef union
33{                               /* BAT register values to be loaded */
34  BAT bat;
35  struct
36  {
37    unsigned int u, l;
38  } words;
39} ubat;
40
41typedef struct batrange
42{                               /* stores address ranges mapped by BATs */
43  unsigned long start;
44  unsigned long limit;
45  unsigned long phys;
46} batrange;
47
48batrange bat_addrs[2][8] = { { {0,} } };
49
50/* could encode this in bat_addrs but I don't touch that one for bwds compat. reasons */
51/* bitmask of used bats */
52static unsigned bat_in_use[2] = { 0, 0 };
53
54/* define a few macros */
55
56#define CLRBAT_ASM(batu,r)                      \
57        "       sync                 \n"        \
58        "       isync                \n"        \
59        "       li      "#r",    0   \n"        \
60        "       mtspr   "#batu", "#r"\n"        \
61        "       sync                 \n"        \
62        "       isync                \n"
63
64#define SETBAT_ASM(batu, batl, u, l)\
65        "       mtspr   "#batl", "#l" \n"       \
66        "       sync                  \n"       \
67        "       isync                 \n"       \
68        "       mtspr   "#batu", "#u" \n"       \
69        "       sync                  \n"       \
70        "       isync                 \n"
71
72#define CLRBAT(bat)                                     \
73        asm volatile(                                   \
74                CLRBAT_ASM(%0, 0)                       \
75                :                                                       \
76                :"i"(bat##U)                            \
77                :"0")
78
79#define GETBAT(bat,u,l)                         \
80        asm volatile(                                   \
81                "       mfspr %0, %2     \n"    \
82                "       mfspr %1, %3     \n"    \
83                :"=r"(u),"=r"(l)                        \
84                :"i"(bat##U),"i"(bat##L)        \
85                )
86
87#define DECL_SETBAT(lcbat,bat)          \
88void                                                            \
89asm_set##lcbat(unsigned int upper, unsigned int lower)  \
90{                                                                       \
91asm volatile(                                           \
92        CLRBAT_ASM(%0,0)                                \
93        SETBAT_ASM(%0,%1,%2,%3)                 \
94        :                                                               \
95        :"i"(bat##U),                                   \
96         "i"(bat##L),                                   \
97         "r"(upper),"r"(lower)                  \
98        :"0");                                                  \
99}
100
101/* export the 'asm' versions for historic reasons */
102DECL_SETBAT (dbat0, DBAT0)
103DECL_SETBAT (dbat1, DBAT1)
104DECL_SETBAT (dbat2, DBAT2)
105DECL_SETBAT (dbat3, DBAT3)
106
107static DECL_SETBAT (dbat4, DBAT4)
108static DECL_SETBAT (dbat5, DBAT5)
109static DECL_SETBAT (dbat6, DBAT6)
110static DECL_SETBAT (dbat7, DBAT7)
111
112static DECL_SETBAT (ibat0, IBAT0)
113static DECL_SETBAT (ibat1, IBAT1)
114static DECL_SETBAT (ibat2, IBAT2)
115static DECL_SETBAT (ibat3, IBAT3)
116static DECL_SETBAT (ibat4, IBAT4)
117static DECL_SETBAT (ibat5, IBAT5)
118static DECL_SETBAT (ibat6, IBAT6)
119static DECL_SETBAT (ibat7, IBAT7)
120
121
122SPR_RO (HID0);
123
124static void
125set_hid0_sync (unsigned long val)
126{
127  asm volatile (
128    "   sync                    \n"
129    "   isync                   \n"
130    "   mtspr   %0, %1  \n"
131    "   sync                    \n"
132    "   isync                   \n"
133    :
134    :"i" (HID0), "r" (val)
135    :"memory" /* paranoia */
136  );
137}
138
139static void
140bat_addrs_put (ubat * bat, int typ, int idx)
141{
142  unsigned long bl;
143  if (bat->bat.batu.vp || bat->bat.batu.vs) {
144    bat_addrs[typ][idx].start = bat->bat.batu.bepi << 17;
145    bat_addrs[typ][idx].phys = bat->bat.batl.brpn << 17;
146
147    /* extended BL cannot be extracted using BAT union
148     * - let's just hope the upper bits read 0 on pre 745x
149     * CPUs.
150     */
151    bl = (bat->words.u << 15) | ((1 << 17) - 1);
152    bat_addrs[typ][idx].limit = bat_addrs[typ][idx].start + bl;
153
154    bat_in_use[typ] |= (1 << idx);
155  }
156}
157
158/* We don't know how the board was initialized. Therefore,
159 * when 'setdbat' is first used we must initialize our
160 * cache.
161 */
162static void
163bat_addrs_init (void)
164{
165  ubat bat;
166
167  GETBAT (DBAT0, bat.words.u, bat.words.l);
168  bat_addrs_put (&bat, TYP_D, 0);
169  GETBAT (DBAT1, bat.words.u, bat.words.l);
170  bat_addrs_put (&bat, TYP_D, 1);
171  GETBAT (DBAT2, bat.words.u, bat.words.l);
172  bat_addrs_put (&bat, TYP_D, 2);
173  GETBAT (DBAT3, bat.words.u, bat.words.l);
174  bat_addrs_put (&bat, TYP_D, 3);
175
176  GETBAT (IBAT0, bat.words.u, bat.words.l);
177  bat_addrs_put (&bat, TYP_I, 0);
178  GETBAT (IBAT1, bat.words.u, bat.words.l);
179  bat_addrs_put (&bat, TYP_I, 1);
180  GETBAT (IBAT2, bat.words.u, bat.words.l);
181  bat_addrs_put (&bat, TYP_I, 2);
182  GETBAT (IBAT3, bat.words.u, bat.words.l);
183  bat_addrs_put (&bat, TYP_I, 3);
184
185
186  if ( ppc_cpu_has_8_bats() && (HID0_7455_HIGH_BAT_EN & _read_HID0 ())) {
187    GETBAT (DBAT4, bat.words.u, bat.words.l);
188    bat_addrs_put (&bat, TYP_D, 4);
189    GETBAT (DBAT5, bat.words.u, bat.words.l);
190    bat_addrs_put (&bat, TYP_D, 5);
191    GETBAT (DBAT6, bat.words.u, bat.words.l);
192    bat_addrs_put (&bat, TYP_D, 6);
193    GETBAT (DBAT7, bat.words.u, bat.words.l);
194    bat_addrs_put (&bat, TYP_D, 7);
195    GETBAT (IBAT4, bat.words.u, bat.words.l);
196    bat_addrs_put (&bat, TYP_I, 4);
197    GETBAT (IBAT5, bat.words.u, bat.words.l);
198    bat_addrs_put (&bat, TYP_I, 5);
199    GETBAT (IBAT6, bat.words.u, bat.words.l);
200    bat_addrs_put (&bat, TYP_I, 6);
201    GETBAT (IBAT7, bat.words.u, bat.words.l);
202    bat_addrs_put (&bat, TYP_I, 7);
203  }
204}
205
206static void
207do_dssall (void)
208{
209  /* Before changing BATs, 'dssall' must be issued.
210   * We check MSR for MSR_VE and issue a 'dssall' if
211   * MSR_VE is set hoping that
212   *  a) on non-altivec CPUs MSR_VE reads as zero
213   *  b) all altivec CPUs use the same bit
214   * NOTE: psim doesn't implement dssall so we skip if we run on psim
215   */
216  if ( (_read_MSR () & MSR_VE) && PPC_PSIM != get_ppc_cpu_type() ) {
217    /* this construct is needed because we don't know
218     * if this file is compiled with -maltivec.
219     * (I plan to add altivec support outside of
220     * RTEMS core and hence I'd rather not
221     * rely on consistent compiler flags).
222     */
223#define DSSALL  0x7e00066c      /* dssall opcode */
224    asm volatile ("     .long %0"::"i" (DSSALL));
225#undef  DSSALL
226  }
227}
228
229/* Clear I/D bats 4..7 ONLY ON 7455 etc.  */
230static void
231clear_hi_bats (void)
232{
233  do_dssall ();
234  CLRBAT (DBAT4);
235  CLRBAT (DBAT5);
236  CLRBAT (DBAT6);
237  CLRBAT (DBAT7);
238  CLRBAT (IBAT4);
239  CLRBAT (IBAT5);
240  CLRBAT (IBAT6);
241  CLRBAT (IBAT7);
242}
243
244static int
245check_bat_index (int i)
246{
247  unsigned long hid0;
248
249  if (i >= 0 && i < 4)
250    return 0;
251  if (i >= 4 && i < 8) {
252    if ( ! ppc_cpu_has_8_bats() )
253      return -1;
254    /* OK, we're on the right hardware;
255     * check if we are already enabled
256     */
257    hid0 = _read_HID0 ();
258    if (HID0_7455_HIGH_BAT_EN & hid0)
259      return 0;
260    /* No; enable now */
261    clear_hi_bats ();
262    set_hid0_sync (hid0 | HID0_7455_HIGH_BAT_EN);
263    return 0;
264  }
265  return -1;
266}
267
268/* size argument check:
269 *  - must be a power of two or zero
270 *  - must be <= 1<<28 ( non 745x cpu )
271 *  - can be 1<<29..1<31 or 0xffffffff on 745x
272 *  - size < 1<<17 means 0
273 * computes and returns the block mask
274 * RETURNS:
275 *  block mask on success or -1 on error
276 */
277static int
278check_bat_size (unsigned long size)
279{
280  unsigned long bit;
281  unsigned long hid0;
282
283  /* First of all, it must be a power of two */
284  if (0 == size)
285    return 0;
286
287  if (0xffffffff == size) {
288    bit = 32;
289  } else {
290    asm volatile ("     cntlzw %0, %1":"=r" (bit):"r" (size));
291    bit = 31 - bit;
292    if (1 << bit != size)
293      return -1;
294  }
295  /* bit < 17 is not really legal but we aliased it to 0 in the past */
296  if (bit > (11 + 17)) {
297    if ( ! ppc_cpu_has_8_bats() )
298      return -1;
299
300    hid0 = _read_HID0 ();
301    /* Let's enable the larger block size if necessary */
302    if (!(HID0_7455_XBSEN & hid0))
303      set_hid0_sync (hid0 | HID0_7455_XBSEN);
304  }
305
306  return (1 << (bit - 17)) - 1;
307}
308
309static int
310check_overlap (int typ, unsigned long start, unsigned long size)
311{
312  int i;
313  unsigned long limit = start + size - 1;
314  for (i = 0; i < sizeof (bat_addrs[typ]) / sizeof (bat_addrs[typ][0]); i++) {
315    if (!((1 << i) & bat_in_use[typ]))
316      continue;                 /* unused bat */
317    /* safe is 'limit < bat_addrs[t][i].start || start > bat_addrs[t][i].limit */
318    if (limit >= bat_addrs[typ][i].start && start <= bat_addrs[typ][i].limit)
319      return i;
320  }
321  return -1;
322}
323
324
325/* Take no risks -- the essential parts of this routine run with
326 * interrupts disabled!
327 */
328
329static int
330setbat (int typ, int bat_index, unsigned long virt, unsigned long phys,
331         unsigned int size, int flags)
332{
333  unsigned long level;
334  unsigned int bl;
335  int err;
336  int wimgxpp;
337  ubat bat;
338
339  if (check_bat_index (bat_index)) {
340    printk ("Invalid BAT index\n", bat_index);
341    return -1;
342  }
343
344  if ((int) (bl = check_bat_size (size)) < 0) {
345    printk ("Invalid BAT size\n", size);
346    return -1;
347  }
348
349  if (virt & (size - 1)) {
350    printk ("BAT effective address 0x%08x misaligned (size is 0x%08x)\n",
351            virt, size);
352    return -1;
353  }
354
355  if (phys & (size - 1)) {
356    printk ("BAT physical address 0x%08x misaligned (size is 0x%08x)\n", phys,
357            size);
358    return -1;
359  }
360
361  if (virt + size - 1 < virt) {
362    printk ("BAT range invalid: wraps around zero 0x%08x..0x%08x\n", virt,
363            virt + size - 1);
364    return -1;
365  }
366
367  if ( TYP_I == typ && ( ( _PAGE_GUARDED | _PAGE_WRITETHRU ) & flags ) ) {
368        printk("IBAT must not have 'guarded' or 'writethrough' attribute\n");
369        return -1;
370  }
371
372/* must protect the bat_addrs table -- since this routine is only used for board setup
373 * or similar special purposes we don't bother about interrupt latency too much.
374 */
375  rtems_interrupt_disable (level);
376
377  {                             /* might have to initialize our cached data */
378    static char init_done = 0;
379    if (!init_done) {
380      bat_addrs_init ();
381      init_done = 1;
382    }
383  }
384
385  err = check_overlap (typ, virt, size);
386  if ((size >= (1 << 17)) && (err >= 0) && (err != bat_index)) {
387    rtems_interrupt_enable (level);
388    printk ("BATs must not overlap; area 0x%08x..0x%08x hits %cBAT %i\n",
389            virt, virt + size, (TYP_I == typ ? 'I' : 'D'), err);
390    return -1;
391  }
392
393  /* 603, 604, etc. */
394  wimgxpp = flags & (_PAGE_WRITETHRU | _PAGE_NO_CACHE
395                     | _PAGE_COHERENT | _PAGE_GUARDED);
396  wimgxpp |= (flags & _PAGE_RW) ? BPP_RW : BPP_RX;
397  bat.words.u = virt | (bl << 2) | 2;   /* Vs=1, Vp=0 */
398  bat.words.l = phys | wimgxpp;
399  if (flags & _PAGE_USER)
400    bat.bat.batu.vp = 1;
401  bat_addrs[typ][bat_index].start = virt;
402  bat_addrs[typ][bat_index].limit = virt + ((bl + 1) << 17) - 1;
403  bat_addrs[typ][bat_index].phys = phys;
404  bat_in_use[typ] |= 1 << bat_index;
405  if (size < (1 << 17)) {
406    /* size of 0 tells us to switch it off */
407    bat.bat.batu.vp = 0;
408    bat.bat.batu.vs = 0;
409    bat_in_use[typ] &= ~(1 << bat_index);
410    /* mimic old behavior when bl was 0 (bs==0 is actually legal; it doesnt
411     * indicate a size of zero. We now accept bl==0 and look at the size.
412     */
413    bat_addrs[typ][bat_index].limit = virt;
414  }
415  do_dssall ();
416  if ( TYP_I == typ ) {
417          switch (bat_index) {
418                  case 0: asm_setibat0 (bat.words.u, bat.words.l); break;
419                  case 1: asm_setibat1 (bat.words.u, bat.words.l); break;
420                  case 2: asm_setibat2 (bat.words.u, bat.words.l); break;
421                  case 3: asm_setibat3 (bat.words.u, bat.words.l); break;
422                          /* cpu check already done in check_index */
423                  case 4: asm_setibat4 (bat.words.u, bat.words.l); break;
424                  case 5: asm_setibat5 (bat.words.u, bat.words.l); break;
425                  case 6: asm_setibat6 (bat.words.u, bat.words.l); break;
426                  case 7: asm_setibat7 (bat.words.u, bat.words.l); break;
427                  default:                     /* should never get here anyways */
428                          break;
429          }
430  } else {
431          switch (bat_index) {
432                  case 0: asm_setdbat0 (bat.words.u, bat.words.l); break;
433                  case 1: asm_setdbat1 (bat.words.u, bat.words.l); break;
434                  case 2: asm_setdbat2 (bat.words.u, bat.words.l); break;
435                  case 3: asm_setdbat3 (bat.words.u, bat.words.l); break;
436                          /* cpu check already done in check_index */
437                  case 4: asm_setdbat4 (bat.words.u, bat.words.l); break;
438                  case 5: asm_setdbat5 (bat.words.u, bat.words.l); break;
439                  case 6: asm_setdbat6 (bat.words.u, bat.words.l); break;
440                  case 7: asm_setdbat7 (bat.words.u, bat.words.l); break;
441                  default:                     /* should never get here anyways */
442                          break;
443          }
444  }
445  rtems_interrupt_enable (level);
446
447  return 0;
448}
449
450static int
451getbat (int typ, int idx, unsigned long *pu, unsigned long *pl)
452{
453  unsigned long u, l;
454
455  if (check_bat_index (idx)) {
456    printk ("Invalid BAT #%i\n", idx);
457    return -1;
458  }
459  if ( TYP_I == typ ) {
460          switch (idx) {
461                  case 0: GETBAT (IBAT0, u, l); break;
462                  case 1: GETBAT (IBAT1, u, l); break;
463                  case 2: GETBAT (IBAT2, u, l); break;
464                  case 3: GETBAT (IBAT3, u, l); break;
465                                  /* cpu check already done in check_index */
466                  case 4: GETBAT (IBAT4, u, l); break;
467                  case 5: GETBAT (IBAT5, u, l); break;
468                  case 6: GETBAT (IBAT6, u, l); break;
469                  case 7: GETBAT (IBAT7, u, l); break;
470                  default:                     /* should never get here anyways */
471                                  return -1;
472          }
473  } else {
474          switch (idx) {
475                  case 0: GETBAT (DBAT0, u, l); break;
476                  case 1: GETBAT (DBAT1, u, l); break;
477                  case 2: GETBAT (DBAT2, u, l); break;
478                  case 3: GETBAT (DBAT3, u, l); break;
479                                  /* cpu check already done in check_index */
480                  case 4: GETBAT (DBAT4, u, l); break;
481                  case 5: GETBAT (DBAT5, u, l); break;
482                  case 6: GETBAT (DBAT6, u, l); break;
483                  case 7: GETBAT (DBAT7, u, l); break;
484                  default:                     /* should never get here anyways */
485                                  return -1;
486          }
487  }
488  if (pu) {
489    *pu = u;
490  }
491  if (pl) {
492    *pl = l;
493  }
494
495  if (!pu && !pl) {
496    /* dump */
497    ubat b;
498    b.words.u = u;
499    b.words.l = l;
500    printk ("Raw %cBAT %i contents; UPPER: (0x%08x)", (TYP_I == typ ? 'I' : 'D'), idx, u);
501    printk (" BEPI: 0x%08x", b.bat.batu.bepi);
502    printk (" BL: 0x%08x", (u >> 2) & ((1 << 15) - 1));
503    printk (" VS: 0b%i", b.bat.batu.vs);
504    printk (" VP: 0b%i", b.bat.batu.vp);
505    printk ("\n");
506    printk ("                     LOWER: (0x%08x)", l);
507    printk ("  RPN: 0x%08x", b.bat.batl.brpn);
508    printk (" wimg:   0b%1i%1i%1i%1i", b.bat.batl.w, b.bat.batl.i,
509            b.bat.batl.m, b.bat.batl.g);
510    printk (" PP: 0x%1x", b.bat.batl.pp);
511    printk ("\n");
512    printk ("Covering EA Range: ");
513    if (bat_in_use[typ] & (1 << idx))
514      printk ("0x%08x .. 0x%08x\n", bat_addrs[typ][idx].start,
515              bat_addrs[typ][idx].limit);
516    else
517      printk ("<none> (BAT off)\n");
518
519  }
520  return u;
521}
522
523int
524setdbat (int bat_index, unsigned long virt, unsigned long phys,
525         unsigned int size, int flags)
526{
527        return setbat(TYP_D, bat_index, virt, phys, size, flags);
528}
529
530int
531setibat (int bat_index, unsigned long virt, unsigned long phys,
532         unsigned int size, int flags)
533{
534        return setbat(TYP_I, bat_index, virt, phys, size, flags);
535}
536
537int
538getdbat (int idx, unsigned long *pu, unsigned long *pl)
539{
540        return getbat (TYP_D, idx, pu, pl);
541}
542
543int
544getibat (int idx, unsigned long *pu, unsigned long *pl)
545{
546        return getbat (TYP_I, idx, pu, pl);
547}
Note: See TracBrowser for help on using the repository browser.