linux/arch/mips/mm/c-r3k.c
<<
>>
Prefs
   1/*
   2 * r2300.c: R2000 and R3000 specific mmu/cache code.
   3 *
   4 * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
   5 *
   6 * with a lot of changes to make this thing work for R3000s
   7 * Tx39XX R4k style caches added. HK
   8 * Copyright (C) 1998, 1999, 2000 Harald Koerfgen
   9 * Copyright (C) 1998 Gleb Raiko & Vladimir Roganov
  10 * Copyright (C) 2001, 2004, 2007  Maciej W. Rozycki
  11 */
  12#include <linux/init.h>
  13#include <linux/kernel.h>
  14#include <linux/sched.h>
  15#include <linux/smp.h>
  16#include <linux/mm.h>
  17
  18#include <asm/page.h>
  19#include <asm/pgtable.h>
  20#include <asm/mmu_context.h>
  21#include <asm/system.h>
  22#include <asm/isadep.h>
  23#include <asm/io.h>
  24#include <asm/bootinfo.h>
  25#include <asm/cpu.h>
  26
  27static unsigned long icache_size, dcache_size;          /* Size in bytes */
  28static unsigned long icache_lsize, dcache_lsize;        /* Size in bytes */
  29
  30unsigned long __cpuinit r3k_cache_size(unsigned long ca_flags)
  31{
  32        unsigned long flags, status, dummy, size;
  33        volatile unsigned long *p;
  34
  35        p = (volatile unsigned long *) KSEG0;
  36
  37        flags = read_c0_status();
  38
  39        /* isolate cache space */
  40        write_c0_status((ca_flags|flags)&~ST0_IEC);
  41
  42        *p = 0xa5a55a5a;
  43        dummy = *p;
  44        status = read_c0_status();
  45
  46        if (dummy != 0xa5a55a5a || (status & ST0_CM)) {
  47                size = 0;
  48        } else {
  49                for (size = 128; size <= 0x40000; size <<= 1)
  50                        *(p + size) = 0;
  51                *p = -1;
  52                for (size = 128;
  53                     (size <= 0x40000) && (*(p + size) == 0);
  54                     size <<= 1)
  55                        ;
  56                if (size > 0x40000)
  57                        size = 0;
  58        }
  59
  60        write_c0_status(flags);
  61
  62        return size * sizeof(*p);
  63}
  64
  65unsigned long __cpuinit r3k_cache_lsize(unsigned long ca_flags)
  66{
  67        unsigned long flags, status, lsize, i;
  68        volatile unsigned long *p;
  69
  70        p = (volatile unsigned long *) KSEG0;
  71
  72        flags = read_c0_status();
  73
  74        /* isolate cache space */
  75        write_c0_status((ca_flags|flags)&~ST0_IEC);
  76
  77        for (i = 0; i < 128; i++)
  78                *(p + i) = 0;
  79        *(volatile unsigned char *)p = 0;
  80        for (lsize = 1; lsize < 128; lsize <<= 1) {
  81                *(p + lsize);
  82                status = read_c0_status();
  83                if (!(status & ST0_CM))
  84                        break;
  85        }
  86        for (i = 0; i < 128; i += lsize)
  87                *(volatile unsigned char *)(p + i) = 0;
  88
  89        write_c0_status(flags);
  90
  91        return lsize * sizeof(*p);
  92}
  93
  94static void __cpuinit r3k_probe_cache(void)
  95{
  96        dcache_size = r3k_cache_size(ST0_ISC);
  97        if (dcache_size)
  98                dcache_lsize = r3k_cache_lsize(ST0_ISC);
  99
 100        icache_size = r3k_cache_size(ST0_ISC|ST0_SWC);
 101        if (icache_size)
 102                icache_lsize = r3k_cache_lsize(ST0_ISC|ST0_SWC);
 103}
 104
 105static void r3k_flush_icache_range(unsigned long start, unsigned long end)
 106{
 107        unsigned long size, i, flags;
 108        volatile unsigned char *p;
 109
 110        size = end - start;
 111        if (size > icache_size || KSEGX(start) != KSEG0) {
 112                start = KSEG0;
 113                size = icache_size;
 114        }
 115        p = (char *)start;
 116
 117        flags = read_c0_status();
 118
 119        /* isolate cache space */
 120        write_c0_status((ST0_ISC|ST0_SWC|flags)&~ST0_IEC);
 121
 122        for (i = 0; i < size; i += 0x080) {
 123                asm(    "sb\t$0, 0x000(%0)\n\t"
 124                        "sb\t$0, 0x004(%0)\n\t"
 125                        "sb\t$0, 0x008(%0)\n\t"
 126                        "sb\t$0, 0x00c(%0)\n\t"
 127                        "sb\t$0, 0x010(%0)\n\t"
 128                        "sb\t$0, 0x014(%0)\n\t"
 129                        "sb\t$0, 0x018(%0)\n\t"
 130                        "sb\t$0, 0x01c(%0)\n\t"
 131                        "sb\t$0, 0x020(%0)\n\t"
 132                        "sb\t$0, 0x024(%0)\n\t"
 133                        "sb\t$0, 0x028(%0)\n\t"
 134                        "sb\t$0, 0x02c(%0)\n\t"
 135                        "sb\t$0, 0x030(%0)\n\t"
 136                        "sb\t$0, 0x034(%0)\n\t"
 137                        "sb\t$0, 0x038(%0)\n\t"
 138                        "sb\t$0, 0x03c(%0)\n\t"
 139                        "sb\t$0, 0x040(%0)\n\t"
 140                        "sb\t$0, 0x044(%0)\n\t"
 141                        "sb\t$0, 0x048(%0)\n\t"
 142                        "sb\t$0, 0x04c(%0)\n\t"
 143                        "sb\t$0, 0x050(%0)\n\t"
 144                        "sb\t$0, 0x054(%0)\n\t"
 145                        "sb\t$0, 0x058(%0)\n\t"
 146                        "sb\t$0, 0x05c(%0)\n\t"
 147                        "sb\t$0, 0x060(%0)\n\t"
 148                        "sb\t$0, 0x064(%0)\n\t"
 149                        "sb\t$0, 0x068(%0)\n\t"
 150                        "sb\t$0, 0x06c(%0)\n\t"
 151                        "sb\t$0, 0x070(%0)\n\t"
 152                        "sb\t$0, 0x074(%0)\n\t"
 153                        "sb\t$0, 0x078(%0)\n\t"
 154                        "sb\t$0, 0x07c(%0)\n\t"
 155                        : : "r" (p) );
 156                p += 0x080;
 157        }
 158
 159        write_c0_status(flags);
 160}
 161
 162static void r3k_flush_dcache_range(unsigned long start, unsigned long end)
 163{
 164        unsigned long size, i, flags;
 165        volatile unsigned char *p;
 166
 167        size = end - start;
 168        if (size > dcache_size || KSEGX(start) != KSEG0) {
 169                start = KSEG0;
 170                size = dcache_size;
 171        }
 172        p = (char *)start;
 173
 174        flags = read_c0_status();
 175
 176        /* isolate cache space */
 177        write_c0_status((ST0_ISC|flags)&~ST0_IEC);
 178
 179        for (i = 0; i < size; i += 0x080) {
 180                asm(    "sb\t$0, 0x000(%0)\n\t"
 181                        "sb\t$0, 0x004(%0)\n\t"
 182                        "sb\t$0, 0x008(%0)\n\t"
 183                        "sb\t$0, 0x00c(%0)\n\t"
 184                        "sb\t$0, 0x010(%0)\n\t"
 185                        "sb\t$0, 0x014(%0)\n\t"
 186                        "sb\t$0, 0x018(%0)\n\t"
 187                        "sb\t$0, 0x01c(%0)\n\t"
 188                        "sb\t$0, 0x020(%0)\n\t"
 189                        "sb\t$0, 0x024(%0)\n\t"
 190                        "sb\t$0, 0x028(%0)\n\t"
 191                        "sb\t$0, 0x02c(%0)\n\t"
 192                        "sb\t$0, 0x030(%0)\n\t"
 193                        "sb\t$0, 0x034(%0)\n\t"
 194                        "sb\t$0, 0x038(%0)\n\t"
 195                        "sb\t$0, 0x03c(%0)\n\t"
 196                        "sb\t$0, 0x040(%0)\n\t"
 197                        "sb\t$0, 0x044(%0)\n\t"
 198                        "sb\t$0, 0x048(%0)\n\t"
 199                        "sb\t$0, 0x04c(%0)\n\t"
 200                        "sb\t$0, 0x050(%0)\n\t"
 201                        "sb\t$0, 0x054(%0)\n\t"
 202                        "sb\t$0, 0x058(%0)\n\t"
 203                        "sb\t$0, 0x05c(%0)\n\t"
 204                        "sb\t$0, 0x060(%0)\n\t"
 205                        "sb\t$0, 0x064(%0)\n\t"
 206                        "sb\t$0, 0x068(%0)\n\t"
 207                        "sb\t$0, 0x06c(%0)\n\t"
 208                        "sb\t$0, 0x070(%0)\n\t"
 209                        "sb\t$0, 0x074(%0)\n\t"
 210                        "sb\t$0, 0x078(%0)\n\t"
 211                        "sb\t$0, 0x07c(%0)\n\t"
 212                        : : "r" (p) );
 213                p += 0x080;
 214        }
 215
 216        write_c0_status(flags);
 217}
 218
 219static inline void r3k_flush_cache_all(void)
 220{
 221}
 222
 223static inline void r3k___flush_cache_all(void)
 224{
 225        r3k_flush_dcache_range(KSEG0, KSEG0 + dcache_size);
 226        r3k_flush_icache_range(KSEG0, KSEG0 + icache_size);
 227}
 228
 229static void r3k_flush_cache_mm(struct mm_struct *mm)
 230{
 231}
 232
 233static void r3k_flush_cache_range(struct vm_area_struct *vma,
 234                                  unsigned long start, unsigned long end)
 235{
 236}
 237
 238static void r3k_flush_cache_page(struct vm_area_struct *vma,
 239                                 unsigned long addr, unsigned long pfn)
 240{
 241        unsigned long kaddr = KSEG0ADDR(pfn << PAGE_SHIFT);
 242        int exec = vma->vm_flags & VM_EXEC;
 243        struct mm_struct *mm = vma->vm_mm;
 244        pgd_t *pgdp;
 245        pud_t *pudp;
 246        pmd_t *pmdp;
 247        pte_t *ptep;
 248
 249        pr_debug("cpage[%08lx,%08lx]\n",
 250                 cpu_context(smp_processor_id(), mm), addr);
 251
 252        /* No ASID => no such page in the cache.  */
 253        if (cpu_context(smp_processor_id(), mm) == 0)
 254                return;
 255
 256        pgdp = pgd_offset(mm, addr);
 257        pudp = pud_offset(pgdp, addr);
 258        pmdp = pmd_offset(pudp, addr);
 259        ptep = pte_offset(pmdp, addr);
 260
 261        /* Invalid => no such page in the cache.  */
 262        if (!(pte_val(*ptep) & _PAGE_PRESENT))
 263                return;
 264
 265        r3k_flush_dcache_range(kaddr, kaddr + PAGE_SIZE);
 266        if (exec)
 267                r3k_flush_icache_range(kaddr, kaddr + PAGE_SIZE);
 268}
 269
 270static void local_r3k_flush_data_cache_page(void *addr)
 271{
 272}
 273
 274static void r3k_flush_data_cache_page(unsigned long addr)
 275{
 276}
 277
 278static void r3k_flush_cache_sigtramp(unsigned long addr)
 279{
 280        unsigned long flags;
 281
 282        pr_debug("csigtramp[%08lx]\n", addr);
 283
 284        flags = read_c0_status();
 285
 286        write_c0_status(flags&~ST0_IEC);
 287
 288        /* Fill the TLB to avoid an exception with caches isolated. */
 289        asm(    "lw\t$0, 0x000(%0)\n\t"
 290                "lw\t$0, 0x004(%0)\n\t"
 291                : : "r" (addr) );
 292
 293        write_c0_status((ST0_ISC|ST0_SWC|flags)&~ST0_IEC);
 294
 295        asm(    "sb\t$0, 0x000(%0)\n\t"
 296                "sb\t$0, 0x004(%0)\n\t"
 297                : : "r" (addr) );
 298
 299        write_c0_status(flags);
 300}
 301
 302static void r3k_dma_cache_wback_inv(unsigned long start, unsigned long size)
 303{
 304        /* Catch bad driver code */
 305        BUG_ON(size == 0);
 306
 307        iob();
 308        r3k_flush_dcache_range(start, start + size);
 309}
 310
 311void __cpuinit r3k_cache_init(void)
 312{
 313        extern void build_clear_page(void);
 314        extern void build_copy_page(void);
 315
 316        r3k_probe_cache();
 317
 318        flush_cache_all = r3k_flush_cache_all;
 319        __flush_cache_all = r3k___flush_cache_all;
 320        flush_cache_mm = r3k_flush_cache_mm;
 321        flush_cache_range = r3k_flush_cache_range;
 322        flush_cache_page = r3k_flush_cache_page;
 323        flush_icache_range = r3k_flush_icache_range;
 324        local_flush_icache_range = r3k_flush_icache_range;
 325
 326        flush_cache_sigtramp = r3k_flush_cache_sigtramp;
 327        local_flush_data_cache_page = local_r3k_flush_data_cache_page;
 328        flush_data_cache_page = r3k_flush_data_cache_page;
 329
 330        _dma_cache_wback_inv = r3k_dma_cache_wback_inv;
 331        _dma_cache_wback = r3k_dma_cache_wback_inv;
 332        _dma_cache_inv = r3k_dma_cache_wback_inv;
 333
 334        printk("Primary instruction cache %ldkB, linesize %ld bytes.\n",
 335                icache_size >> 10, icache_lsize);
 336        printk("Primary data cache %ldkB, linesize %ld bytes.\n",
 337                dcache_size >> 10, dcache_lsize);
 338
 339        build_clear_page();
 340        build_copy_page();
 341}
 342