linux/arch/mips/mm/c-tx39.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 */
  11#include <linux/init.h>
  12#include <linux/kernel.h>
  13#include <linux/sched.h>
  14#include <linux/smp.h>
  15#include <linux/mm.h>
  16
  17#include <asm/cacheops.h>
  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
  27/* For R3000 cores with R4000 style caches */
  28static unsigned long icache_size, dcache_size;          /* Size in bytes */
  29
  30#include <asm/r4kcache.h>
  31
  32extern int r3k_have_wired_reg;  /* in r3k-tlb.c */
  33
  34/* This sequence is required to ensure icache is disabled immediately */
  35#define TX39_STOP_STREAMING() \
  36__asm__ __volatile__( \
  37        ".set    push\n\t" \
  38        ".set    noreorder\n\t" \
  39        "b       1f\n\t" \
  40        "nop\n\t" \
  41        "1:\n\t" \
  42        ".set pop" \
  43        )
  44
  45/* TX39H-style cache flush routines. */
  46static void tx39h_flush_icache_all(void)
  47{
  48        unsigned long flags, config;
  49
  50        /* disable icache (set ICE#) */
  51        local_irq_save(flags);
  52        config = read_c0_conf();
  53        write_c0_conf(config & ~TX39_CONF_ICE);
  54        TX39_STOP_STREAMING();
  55        blast_icache16();
  56        write_c0_conf(config);
  57        local_irq_restore(flags);
  58}
  59
  60static void tx39h_dma_cache_wback_inv(unsigned long addr, unsigned long size)
  61{
  62        /* Catch bad driver code */
  63        BUG_ON(size == 0);
  64
  65        iob();
  66        blast_inv_dcache_range(addr, addr + size);
  67}
  68
  69
  70/* TX39H2,TX39H3 */
  71static inline void tx39_blast_dcache_page(unsigned long addr)
  72{
  73        if (current_cpu_type() != CPU_TX3912)
  74                blast_dcache16_page(addr);
  75}
  76
  77static inline void tx39_blast_dcache_page_indexed(unsigned long addr)
  78{
  79        blast_dcache16_page_indexed(addr);
  80}
  81
  82static inline void tx39_blast_dcache(void)
  83{
  84        blast_dcache16();
  85}
  86
  87static inline void tx39_blast_icache_page(unsigned long addr)
  88{
  89        unsigned long flags, config;
  90        /* disable icache (set ICE#) */
  91        local_irq_save(flags);
  92        config = read_c0_conf();
  93        write_c0_conf(config & ~TX39_CONF_ICE);
  94        TX39_STOP_STREAMING();
  95        blast_icache16_page(addr);
  96        write_c0_conf(config);
  97        local_irq_restore(flags);
  98}
  99
 100static inline void tx39_blast_icache_page_indexed(unsigned long addr)
 101{
 102        unsigned long flags, config;
 103        /* disable icache (set ICE#) */
 104        local_irq_save(flags);
 105        config = read_c0_conf();
 106        write_c0_conf(config & ~TX39_CONF_ICE);
 107        TX39_STOP_STREAMING();
 108        blast_icache16_page_indexed(addr);
 109        write_c0_conf(config);
 110        local_irq_restore(flags);
 111}
 112
 113static inline void tx39_blast_icache(void)
 114{
 115        unsigned long flags, config;
 116        /* disable icache (set ICE#) */
 117        local_irq_save(flags);
 118        config = read_c0_conf();
 119        write_c0_conf(config & ~TX39_CONF_ICE);
 120        TX39_STOP_STREAMING();
 121        blast_icache16();
 122        write_c0_conf(config);
 123        local_irq_restore(flags);
 124}
 125
 126static void tx39__flush_cache_vmap(void)
 127{
 128        tx39_blast_dcache();
 129}
 130
 131static void tx39__flush_cache_vunmap(void)
 132{
 133        tx39_blast_dcache();
 134}
 135
 136static inline void tx39_flush_cache_all(void)
 137{
 138        if (!cpu_has_dc_aliases)
 139                return;
 140
 141        tx39_blast_dcache();
 142}
 143
 144static inline void tx39___flush_cache_all(void)
 145{
 146        tx39_blast_dcache();
 147        tx39_blast_icache();
 148}
 149
 150static void tx39_flush_cache_mm(struct mm_struct *mm)
 151{
 152        if (!cpu_has_dc_aliases)
 153                return;
 154
 155        if (cpu_context(smp_processor_id(), mm) != 0)
 156                tx39_blast_dcache();
 157}
 158
 159static void tx39_flush_cache_range(struct vm_area_struct *vma,
 160        unsigned long start, unsigned long end)
 161{
 162        if (!cpu_has_dc_aliases)
 163                return;
 164        if (!(cpu_context(smp_processor_id(), vma->vm_mm)))
 165                return;
 166
 167        tx39_blast_dcache();
 168}
 169
 170static void tx39_flush_cache_page(struct vm_area_struct *vma, unsigned long page, unsigned long pfn)
 171{
 172        int exec = vma->vm_flags & VM_EXEC;
 173        struct mm_struct *mm = vma->vm_mm;
 174        pgd_t *pgdp;
 175        pud_t *pudp;
 176        pmd_t *pmdp;
 177        pte_t *ptep;
 178
 179        /*
 180         * If ownes no valid ASID yet, cannot possibly have gotten
 181         * this page into the cache.
 182         */
 183        if (cpu_context(smp_processor_id(), mm) == 0)
 184                return;
 185
 186        page &= PAGE_MASK;
 187        pgdp = pgd_offset(mm, page);
 188        pudp = pud_offset(pgdp, page);
 189        pmdp = pmd_offset(pudp, page);
 190        ptep = pte_offset(pmdp, page);
 191
 192        /*
 193         * If the page isn't marked valid, the page cannot possibly be
 194         * in the cache.
 195         */
 196        if (!(pte_val(*ptep) & _PAGE_PRESENT))
 197                return;
 198
 199        /*
 200         * Doing flushes for another ASID than the current one is
 201         * too difficult since stupid R4k caches do a TLB translation
 202         * for every cache flush operation.  So we do indexed flushes
 203         * in that case, which doesn't overly flush the cache too much.
 204         */
 205        if ((mm == current->active_mm) && (pte_val(*ptep) & _PAGE_VALID)) {
 206                if (cpu_has_dc_aliases || exec)
 207                        tx39_blast_dcache_page(page);
 208                if (exec)
 209                        tx39_blast_icache_page(page);
 210
 211                return;
 212        }
 213
 214        /*
 215         * Do indexed flush, too much work to get the (possible) TLB refills
 216         * to work correctly.
 217         */
 218        if (cpu_has_dc_aliases || exec)
 219                tx39_blast_dcache_page_indexed(page);
 220        if (exec)
 221                tx39_blast_icache_page_indexed(page);
 222}
 223
 224static void local_tx39_flush_data_cache_page(void * addr)
 225{
 226        tx39_blast_dcache_page((unsigned long)addr);
 227}
 228
 229static void tx39_flush_data_cache_page(unsigned long addr)
 230{
 231        tx39_blast_dcache_page(addr);
 232}
 233
 234static void tx39_flush_icache_range(unsigned long start, unsigned long end)
 235{
 236        if (end - start > dcache_size)
 237                tx39_blast_dcache();
 238        else
 239                protected_blast_dcache_range(start, end);
 240
 241        if (end - start > icache_size)
 242                tx39_blast_icache();
 243        else {
 244                unsigned long flags, config;
 245                /* disable icache (set ICE#) */
 246                local_irq_save(flags);
 247                config = read_c0_conf();
 248                write_c0_conf(config & ~TX39_CONF_ICE);
 249                TX39_STOP_STREAMING();
 250                protected_blast_icache_range(start, end);
 251                write_c0_conf(config);
 252                local_irq_restore(flags);
 253        }
 254}
 255
 256static void tx39_dma_cache_wback_inv(unsigned long addr, unsigned long size)
 257{
 258        unsigned long end;
 259
 260        if (((size | addr) & (PAGE_SIZE - 1)) == 0) {
 261                end = addr + size;
 262                do {
 263                        tx39_blast_dcache_page(addr);
 264                        addr += PAGE_SIZE;
 265                } while(addr != end);
 266        } else if (size > dcache_size) {
 267                tx39_blast_dcache();
 268        } else {
 269                blast_dcache_range(addr, addr + size);
 270        }
 271}
 272
 273static void tx39_dma_cache_inv(unsigned long addr, unsigned long size)
 274{
 275        unsigned long end;
 276
 277        if (((size | addr) & (PAGE_SIZE - 1)) == 0) {
 278                end = addr + size;
 279                do {
 280                        tx39_blast_dcache_page(addr);
 281                        addr += PAGE_SIZE;
 282                } while(addr != end);
 283        } else if (size > dcache_size) {
 284                tx39_blast_dcache();
 285        } else {
 286                blast_inv_dcache_range(addr, addr + size);
 287        }
 288}
 289
 290static void tx39_flush_cache_sigtramp(unsigned long addr)
 291{
 292        unsigned long ic_lsize = current_cpu_data.icache.linesz;
 293        unsigned long dc_lsize = current_cpu_data.dcache.linesz;
 294        unsigned long config;
 295        unsigned long flags;
 296
 297        protected_writeback_dcache_line(addr & ~(dc_lsize - 1));
 298
 299        /* disable icache (set ICE#) */
 300        local_irq_save(flags);
 301        config = read_c0_conf();
 302        write_c0_conf(config & ~TX39_CONF_ICE);
 303        TX39_STOP_STREAMING();
 304        protected_flush_icache_line(addr & ~(ic_lsize - 1));
 305        write_c0_conf(config);
 306        local_irq_restore(flags);
 307}
 308
 309static __init void tx39_probe_cache(void)
 310{
 311        unsigned long config;
 312
 313        config = read_c0_conf();
 314
 315        icache_size = 1 << (10 + ((config & TX39_CONF_ICS_MASK) >>
 316                                  TX39_CONF_ICS_SHIFT));
 317        dcache_size = 1 << (10 + ((config & TX39_CONF_DCS_MASK) >>
 318                                  TX39_CONF_DCS_SHIFT));
 319
 320        current_cpu_data.icache.linesz = 16;
 321        switch (current_cpu_type()) {
 322        case CPU_TX3912:
 323                current_cpu_data.icache.ways = 1;
 324                current_cpu_data.dcache.ways = 1;
 325                current_cpu_data.dcache.linesz = 4;
 326                break;
 327
 328        case CPU_TX3927:
 329                current_cpu_data.icache.ways = 2;
 330                current_cpu_data.dcache.ways = 2;
 331                current_cpu_data.dcache.linesz = 16;
 332                break;
 333
 334        case CPU_TX3922:
 335        default:
 336                current_cpu_data.icache.ways = 1;
 337                current_cpu_data.dcache.ways = 1;
 338                current_cpu_data.dcache.linesz = 16;
 339                break;
 340        }
 341}
 342
 343void __cpuinit tx39_cache_init(void)
 344{
 345        extern void build_clear_page(void);
 346        extern void build_copy_page(void);
 347        unsigned long config;
 348
 349        config = read_c0_conf();
 350        config &= ~TX39_CONF_WBON;
 351        write_c0_conf(config);
 352
 353        tx39_probe_cache();
 354
 355        switch (current_cpu_type()) {
 356        case CPU_TX3912:
 357                /* TX39/H core (writethru direct-map cache) */
 358                __flush_cache_vmap      = tx39__flush_cache_vmap;
 359                __flush_cache_vunmap    = tx39__flush_cache_vunmap;
 360                flush_cache_all = tx39h_flush_icache_all;
 361                __flush_cache_all       = tx39h_flush_icache_all;
 362                flush_cache_mm          = (void *) tx39h_flush_icache_all;
 363                flush_cache_range       = (void *) tx39h_flush_icache_all;
 364                flush_cache_page        = (void *) tx39h_flush_icache_all;
 365                flush_icache_range      = (void *) tx39h_flush_icache_all;
 366                local_flush_icache_range = (void *) tx39h_flush_icache_all;
 367
 368                flush_cache_sigtramp    = (void *) tx39h_flush_icache_all;
 369                local_flush_data_cache_page     = (void *) tx39h_flush_icache_all;
 370                flush_data_cache_page   = (void *) tx39h_flush_icache_all;
 371
 372                _dma_cache_wback_inv    = tx39h_dma_cache_wback_inv;
 373
 374                shm_align_mask          = PAGE_SIZE - 1;
 375
 376                break;
 377
 378        case CPU_TX3922:
 379        case CPU_TX3927:
 380        default:
 381                /* TX39/H2,H3 core (writeback 2way-set-associative cache) */
 382                r3k_have_wired_reg = 1;
 383                write_c0_wired(0);      /* set 8 on reset... */
 384                /* board-dependent init code may set WBON */
 385
 386                __flush_cache_vmap      = tx39__flush_cache_vmap;
 387                __flush_cache_vunmap    = tx39__flush_cache_vunmap;
 388
 389                flush_cache_all = tx39_flush_cache_all;
 390                __flush_cache_all = tx39___flush_cache_all;
 391                flush_cache_mm = tx39_flush_cache_mm;
 392                flush_cache_range = tx39_flush_cache_range;
 393                flush_cache_page = tx39_flush_cache_page;
 394                flush_icache_range = tx39_flush_icache_range;
 395                local_flush_icache_range = tx39_flush_icache_range;
 396
 397                flush_cache_sigtramp = tx39_flush_cache_sigtramp;
 398                local_flush_data_cache_page = local_tx39_flush_data_cache_page;
 399                flush_data_cache_page = tx39_flush_data_cache_page;
 400
 401                _dma_cache_wback_inv = tx39_dma_cache_wback_inv;
 402                _dma_cache_wback = tx39_dma_cache_wback_inv;
 403                _dma_cache_inv = tx39_dma_cache_inv;
 404
 405                shm_align_mask = max_t(unsigned long,
 406                                       (dcache_size / current_cpu_data.dcache.ways) - 1,
 407                                       PAGE_SIZE - 1);
 408
 409                break;
 410        }
 411
 412        current_cpu_data.icache.waysize = icache_size / current_cpu_data.icache.ways;
 413        current_cpu_data.dcache.waysize = dcache_size / current_cpu_data.dcache.ways;
 414
 415        current_cpu_data.icache.sets =
 416                current_cpu_data.icache.waysize / current_cpu_data.icache.linesz;
 417        current_cpu_data.dcache.sets =
 418                current_cpu_data.dcache.waysize / current_cpu_data.dcache.linesz;
 419
 420        if (current_cpu_data.dcache.waysize > PAGE_SIZE)
 421                current_cpu_data.dcache.flags |= MIPS_CACHE_ALIASES;
 422
 423        current_cpu_data.icache.waybit = 0;
 424        current_cpu_data.dcache.waybit = 0;
 425
 426        printk("Primary instruction cache %ldkB, linesize %d bytes\n",
 427                icache_size >> 10, current_cpu_data.icache.linesz);
 428        printk("Primary data cache %ldkB, linesize %d bytes\n",
 429                dcache_size >> 10, current_cpu_data.dcache.linesz);
 430
 431        build_clear_page();
 432        build_copy_page();
 433        tx39h_flush_icache_all();
 434}
 435