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

Last change on this file since 73f8d93 was 73f8d93, checked in by Sebastian Huber <sebastian.huber@…>, on Feb 15, 2017 at 10:09:50 AM

bsps/powerpc: Fix warnings

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