linux/arch/powerpc/mm/8xx_mmu.c
<<
>>
Prefs
   1/*
   2 * This file contains the routines for initializing the MMU
   3 * on the 8xx series of chips.
   4 *  -- christophe
   5 *
   6 *  Derived from arch/powerpc/mm/40x_mmu.c:
   7 *
   8 *  This program is free software; you can redistribute it and/or
   9 *  modify it under the terms of the GNU General Public License
  10 *  as published by the Free Software Foundation; either version
  11 *  2 of the License, or (at your option) any later version.
  12 *
  13 */
  14
  15#include <linux/memblock.h>
  16
  17#include "mmu_decl.h"
  18
  19extern int __map_without_ltlbs;
  20/*
  21 * MMU_init_hw does the chip-specific initialization of the MMU hardware.
  22 */
  23void __init MMU_init_hw(void)
  24{
  25        /* Nothing to do for the time being but keep it similar to other PPC */
  26}
  27
  28#define LARGE_PAGE_SIZE_4M      (1<<22)
  29#define LARGE_PAGE_SIZE_8M      (1<<23)
  30#define LARGE_PAGE_SIZE_64M     (1<<26)
  31
  32unsigned long __init mmu_mapin_ram(unsigned long top)
  33{
  34        unsigned long v, s, mapped;
  35        phys_addr_t p;
  36
  37        v = KERNELBASE;
  38        p = 0;
  39        s = top;
  40
  41        if (__map_without_ltlbs)
  42                return 0;
  43
  44#ifdef CONFIG_PPC_4K_PAGES
  45        while (s >= LARGE_PAGE_SIZE_8M) {
  46                pmd_t *pmdp;
  47                unsigned long val = p | MD_PS8MEG;
  48
  49                pmdp = pmd_offset(pud_offset(pgd_offset_k(v), v), v);
  50                *pmdp++ = __pmd(val);
  51                *pmdp++ = __pmd(val + LARGE_PAGE_SIZE_4M);
  52
  53                v += LARGE_PAGE_SIZE_8M;
  54                p += LARGE_PAGE_SIZE_8M;
  55                s -= LARGE_PAGE_SIZE_8M;
  56        }
  57#else /* CONFIG_PPC_16K_PAGES */
  58        while (s >= LARGE_PAGE_SIZE_64M) {
  59                pmd_t *pmdp;
  60                unsigned long val = p | MD_PS8MEG;
  61
  62                pmdp = pmd_offset(pud_offset(pgd_offset_k(v), v), v);
  63                *pmdp++ = __pmd(val);
  64
  65                v += LARGE_PAGE_SIZE_64M;
  66                p += LARGE_PAGE_SIZE_64M;
  67                s -= LARGE_PAGE_SIZE_64M;
  68        }
  69#endif
  70
  71        mapped = top - s;
  72
  73        /* If the size of RAM is not an exact power of two, we may not
  74         * have covered RAM in its entirety with 8 MiB
  75         * pages. Consequently, restrict the top end of RAM currently
  76         * allocable so that calls to the MEMBLOCK to allocate PTEs for "tail"
  77         * coverage with normal-sized pages (or other reasons) do not
  78         * attempt to allocate outside the allowed range.
  79         */
  80        memblock_set_current_limit(mapped);
  81
  82        return mapped;
  83}
  84
  85void setup_initial_memory_limit(phys_addr_t first_memblock_base,
  86                                phys_addr_t first_memblock_size)
  87{
  88        /* We don't currently support the first MEMBLOCK not mapping 0
  89         * physical on those processors
  90         */
  91        BUG_ON(first_memblock_base != 0);
  92
  93#ifdef CONFIG_PIN_TLB
  94        /* 8xx can only access 24MB at the moment */
  95        memblock_set_current_limit(min_t(u64, first_memblock_size, 0x01800000));
  96#else
  97        /* 8xx can only access 8MB at the moment */
  98        memblock_set_current_limit(min_t(u64, first_memblock_size, 0x00800000));
  99#endif
 100}
 101
 102/*
 103 * Set up to use a given MMU context.
 104 * id is context number, pgd is PGD pointer.
 105 *
 106 * We place the physical address of the new task page directory loaded
 107 * into the MMU base register, and set the ASID compare register with
 108 * the new "context."
 109 */
 110void set_context(unsigned long id, pgd_t *pgd)
 111{
 112        s16 offset = (s16)(__pa(swapper_pg_dir));
 113
 114#ifdef CONFIG_BDI_SWITCH
 115        pgd_t   **ptr = *(pgd_t ***)(KERNELBASE + 0xf0);
 116
 117        /* Context switch the PTE pointer for the Abatron BDI2000.
 118         * The PGDIR is passed as second argument.
 119         */
 120        *(ptr + 1) = pgd;
 121#endif
 122
 123        /* Register M_TW will contain base address of level 1 table minus the
 124         * lower part of the kernel PGDIR base address, so that all accesses to
 125         * level 1 table are done relative to lower part of kernel PGDIR base
 126         * address.
 127         */
 128        mtspr(SPRN_M_TW, __pa(pgd) - offset);
 129
 130        /* Update context */
 131        mtspr(SPRN_M_CASID, id);
 132        /* sync */
 133        mb();
 134}
 135
 136void flush_instruction_cache(void)
 137{
 138        isync();
 139        mtspr(SPRN_IC_CST, IDC_INVALL);
 140        isync();
 141}
 142