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

4.104.114.95
Last change on this file since 486d6ec was 486d6ec, checked in by Till Straumann <strauman@…>, on 11/30/07 at 01:05:08

2007-11-29 Till Straumann <strauman@…>

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