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

4.104.115
Last change on this file since ac7af4a was 4099386, checked in by Ralf Corsepius <ralf.corsepius@…>, on 10/22/09 at 01:57:51

2009-10-22 Ralf Corsépius <ralf.corsepius@…>

  • mpc6xx/mmu/bat.c: Add missing prototypes.
  • Property mode set to 100644
File size: 14.5 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   */
215  if (_read_MSR () & MSR_VE) {
216    /* this construct is needed because we don't know
217     * if this file is compiled with -maltivec.
218     * (I plan to add altivec support outside of
219     * RTEMS core and hence I'd rather not
220     * rely on consistent compiler flags).
221     */
222#define DSSALL  0x7e00066c      /* dssall opcode */
223    asm volatile ("     .long %0"::"i" (DSSALL));
224#undef  DSSALL
225  }
226}
227
228/* Clear I/D bats 4..7 ONLY ON 7455 etc.  */
229static void
230clear_hi_bats (void)
231{
232  do_dssall ();
233  CLRBAT (DBAT4);
234  CLRBAT (DBAT5);
235  CLRBAT (DBAT6);
236  CLRBAT (DBAT7);
237  CLRBAT (IBAT4);
238  CLRBAT (IBAT5);
239  CLRBAT (IBAT6);
240  CLRBAT (IBAT7);
241}
242
243static int
244check_bat_index (int i)
245{
246  unsigned long hid0;
247
248  if (i >= 0 && i < 4)
249    return 0;
250  if (i >= 4 && i < 8) {
251    if ( ! ppc_cpu_has_8_bats() )
252      return -1;
253    /* OK, we're on the right hardware;
254     * check if we are already enabled
255     */
256    hid0 = _read_HID0 ();
257    if (HID0_7455_HIGH_BAT_EN & hid0)
258      return 0;
259    /* No; enable now */
260    clear_hi_bats ();
261    set_hid0_sync (hid0 | HID0_7455_HIGH_BAT_EN);
262    return 0;
263  }
264  return -1;
265}
266
267/* size argument check:
268 *  - must be a power of two or zero
269 *  - must be <= 1<<28 ( non 745x cpu )
270 *  - can be 1<<29..1<31 or 0xffffffff on 745x
271 *  - size < 1<<17 means 0
272 * computes and returns the block mask
273 * RETURNS:
274 *  block mask on success or -1 on error
275 */
276static int
277check_bat_size (unsigned long size)
278{
279  unsigned long bit;
280  unsigned long hid0;
281
282  /* First of all, it must be a power of two */
283  if (0 == size)
284    return 0;
285
286  if (0xffffffff == size) {
287    bit = 32;
288  } else {
289    asm volatile ("     cntlzw %0, %1":"=r" (bit):"r" (size));
290    bit = 31 - bit;
291    if (1 << bit != size)
292      return -1;
293  }
294  /* bit < 17 is not really legal but we aliased it to 0 in the past */
295  if (bit > (11 + 17)) {
296    if ( ! ppc_cpu_has_8_bats() )
297      return -1;
298
299    hid0 = _read_HID0 ();
300    /* Let's enable the larger block size if necessary */
301    if (!(HID0_7455_XBSEN & hid0))
302      set_hid0_sync (hid0 | HID0_7455_XBSEN);
303  }
304
305  return (1 << (bit - 17)) - 1;
306}
307
308static int
309check_overlap (int typ, unsigned long start, unsigned long size)
310{
311  int i;
312  unsigned long limit = start + size - 1;
313  for (i = 0; i < sizeof (bat_addrs[typ]) / sizeof (bat_addrs[typ][0]); i++) {
314    if (!((1 << i) & bat_in_use[typ]))
315      continue;                 /* unused bat */
316    /* safe is 'limit < bat_addrs[t][i].start || start > bat_addrs[t][i].limit */
317    if (limit >= bat_addrs[typ][i].start && start <= bat_addrs[typ][i].limit)
318      return i;
319  }
320  return -1;
321}
322
323
324/* Take no risks -- the essential parts of this routine run with
325 * interrupts disabled!
326 */
327
328static int
329setbat (int typ, int bat_index, unsigned long virt, unsigned long phys,
330         unsigned int size, int flags)
331{
332  unsigned long level;
333  unsigned int bl;
334  int err;
335  int wimgxpp;
336  ubat bat;
337
338  if (check_bat_index (bat_index)) {
339    printk ("Invalid BAT index\n", bat_index);
340    return -1;
341  }
342
343  if ((int) (bl = check_bat_size (size)) < 0) {
344    printk ("Invalid BAT size\n", size);
345    return -1;
346  }
347
348  if (virt & (size - 1)) {
349    printk ("BAT effective address 0x%08x misaligned (size is 0x%08x)\n",
350            virt, size);
351    return -1;
352  }
353
354  if (phys & (size - 1)) {
355    printk ("BAT physical address 0x%08x misaligned (size is 0x%08x)\n", phys,
356            size);
357    return -1;
358  }
359
360  if (virt + size - 1 < virt) {
361    printk ("BAT range invalid: wraps around zero 0x%08x..0x%08x\n", virt,
362            virt + size - 1);
363    return -1;
364  }
365
366  if ( TYP_I == typ && ( ( _PAGE_GUARDED | _PAGE_WRITETHRU ) & flags ) ) {
367        printk("IBAT must not have 'guarded' or 'writethrough' attribute\n");
368        return -1;
369  }
370
371/* must protect the bat_addrs table -- since this routine is only used for board setup
372 * or similar special purposes we don't bother about interrupt latency too much.
373 */
374  rtems_interrupt_disable (level);
375
376  {                             /* might have to initialize our cached data */
377    static char init_done = 0;
378    if (!init_done) {
379      bat_addrs_init ();
380      init_done = 1;
381    }
382  }
383 
384  err = check_overlap (typ, virt, size);
385  if ((size >= (1 << 17)) && (err >= 0) && (err != bat_index)) {
386    rtems_interrupt_enable (level);
387    printk ("BATs must not overlap; area 0x%08x..0x%08x hits %cBAT %i\n",
388            virt, virt + size, (TYP_I == typ ? 'I' : 'D'), err);
389    return -1;
390  }
391
392  /* 603, 604, etc. */
393  wimgxpp = flags & (_PAGE_WRITETHRU | _PAGE_NO_CACHE
394                     | _PAGE_COHERENT | _PAGE_GUARDED);
395  wimgxpp |= (flags & _PAGE_RW) ? BPP_RW : BPP_RX;
396  bat.words.u = virt | (bl << 2) | 2;   /* Vs=1, Vp=0 */
397  bat.words.l = phys | wimgxpp;
398  if (flags & _PAGE_USER)
399    bat.bat.batu.vp = 1;
400  bat_addrs[typ][bat_index].start = virt;
401  bat_addrs[typ][bat_index].limit = virt + ((bl + 1) << 17) - 1;
402  bat_addrs[typ][bat_index].phys = phys;
403  bat_in_use[typ] |= 1 << bat_index;
404  if (size < (1 << 17)) {
405    /* size of 0 tells us to switch it off */
406    bat.bat.batu.vp = 0;
407    bat.bat.batu.vs = 0;
408    bat_in_use[typ] &= ~(1 << bat_index);
409    /* mimic old behavior when bl was 0 (bs==0 is actually legal; it doesnt
410     * indicate a size of zero. We now accept bl==0 and look at the size.
411     */
412    bat_addrs[typ][bat_index].limit = virt;
413  }
414  do_dssall ();
415  if ( TYP_I == typ ) {
416          switch (bat_index) {
417                  case 0: asm_setibat0 (bat.words.u, bat.words.l); break;
418                  case 1: asm_setibat1 (bat.words.u, bat.words.l); break;
419                  case 2: asm_setibat2 (bat.words.u, bat.words.l); break;
420                  case 3: asm_setibat3 (bat.words.u, bat.words.l); break;
421                          /* cpu check already done in check_index */
422                  case 4: asm_setibat4 (bat.words.u, bat.words.l); break;
423                  case 5: asm_setibat5 (bat.words.u, bat.words.l); break;
424                  case 6: asm_setibat6 (bat.words.u, bat.words.l); break;
425                  case 7: asm_setibat7 (bat.words.u, bat.words.l); break;
426                  default:                     /* should never get here anyways */
427                          break;
428          }
429  } else {
430          switch (bat_index) {
431                  case 0: asm_setdbat0 (bat.words.u, bat.words.l); break;
432                  case 1: asm_setdbat1 (bat.words.u, bat.words.l); break;
433                  case 2: asm_setdbat2 (bat.words.u, bat.words.l); break;
434                  case 3: asm_setdbat3 (bat.words.u, bat.words.l); break;
435                          /* cpu check already done in check_index */
436                  case 4: asm_setdbat4 (bat.words.u, bat.words.l); break;
437                  case 5: asm_setdbat5 (bat.words.u, bat.words.l); break;
438                  case 6: asm_setdbat6 (bat.words.u, bat.words.l); break;
439                  case 7: asm_setdbat7 (bat.words.u, bat.words.l); break;
440                  default:                     /* should never get here anyways */
441                          break;
442          }
443  }
444  rtems_interrupt_enable (level);
445
446  return 0;
447}
448
449static int
450getbat (int typ, int idx, unsigned long *pu, unsigned long *pl)
451{
452  unsigned long u, l;
453
454  if (check_bat_index (idx)) {
455    printk ("Invalid BAT #%i\n", idx);
456    return -1;
457  }
458  if ( TYP_I == typ ) {
459          switch (idx) {
460                  case 0: GETBAT (IBAT0, u, l); break;
461                  case 1: GETBAT (IBAT1, u, l); break;
462                  case 2: GETBAT (IBAT2, u, l); break;
463                  case 3: GETBAT (IBAT3, u, l); break;
464                                  /* cpu check already done in check_index */
465                  case 4: GETBAT (IBAT4, u, l); break;
466                  case 5: GETBAT (IBAT5, u, l); break;
467                  case 6: GETBAT (IBAT6, u, l); break;
468                  case 7: GETBAT (IBAT7, u, l); break;
469                  default:                     /* should never get here anyways */
470                                  return -1;
471          }
472  } else {
473          switch (idx) {
474                  case 0: GETBAT (DBAT0, u, l); break;
475                  case 1: GETBAT (DBAT1, u, l); break;
476                  case 2: GETBAT (DBAT2, u, l); break;
477                  case 3: GETBAT (DBAT3, u, l); break;
478                                  /* cpu check already done in check_index */
479                  case 4: GETBAT (DBAT4, u, l); break;
480                  case 5: GETBAT (DBAT5, u, l); break;
481                  case 6: GETBAT (DBAT6, u, l); break;
482                  case 7: GETBAT (DBAT7, u, l); break;
483                  default:                     /* should never get here anyways */
484                                  return -1;
485          }
486  }
487  if (pu) {
488    *pu = u;
489  }
490  if (pl) {
491    *pl = l;
492  }
493
494  if (!pu && !pl) {
495    /* dump */
496    ubat b;
497    b.words.u = u;
498    b.words.l = l;
499    printk ("Raw %cBAT %i contents; UPPER: (0x%08x)", (TYP_I == typ ? 'I' : 'D'), idx, u);
500    printk (" BEPI: 0x%08x", b.bat.batu.bepi);
501    printk (" BL: 0x%08x", (u >> 2) & ((1 << 15) - 1));
502    printk (" VS: 0b%i", b.bat.batu.vs);
503    printk (" VP: 0b%i", b.bat.batu.vp);
504    printk ("\n");
505    printk ("                     LOWER: (0x%08x)", l);
506    printk ("  RPN: 0x%08x", b.bat.batl.brpn);
507    printk (" wimg:   0b%1i%1i%1i%1i", b.bat.batl.w, b.bat.batl.i,
508            b.bat.batl.m, b.bat.batl.g);
509    printk (" PP: 0x%1x", b.bat.batl.pp);
510    printk ("\n");
511    printk ("Covering EA Range: ");
512    if (bat_in_use[typ] & (1 << idx))
513      printk ("0x%08x .. 0x%08x\n", bat_addrs[typ][idx].start,
514              bat_addrs[typ][idx].limit);
515    else
516      printk ("<none> (BAT off)\n");
517
518  }
519  return u;
520}
521
522int
523setdbat (int bat_index, unsigned long virt, unsigned long phys,
524         unsigned int size, int flags)
525{
526        return setbat(TYP_D, bat_index, virt, phys, size, flags);
527}
528
529int
530setibat (int bat_index, unsigned long virt, unsigned long phys,
531         unsigned int size, int flags)
532{
533        return setbat(TYP_I, bat_index, virt, phys, size, flags);
534}
535
536int
537getdbat (int idx, unsigned long *pu, unsigned long *pl)
538{
539        return getbat (TYP_D, idx, pu, pl);
540}
541
542int
543getibat (int idx, unsigned long *pu, unsigned long *pl)
544{
545        return getbat (TYP_I, idx, pu, pl);
546}
Note: See TracBrowser for help on using the repository browser.