linux/arch/mips/mm/c-tx39.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * r2300.c: R2000 and R3000 specific mmu/cache code.
   4 *
   5 * Copyright (C) 1996 David S. Miller (davem@davemloft.net)
   6 *
   7 * with a lot of changes to make this thing work for R3000s
   8 * Tx39XX R4k style caches added. HK
   9 * Copyright (C) 1998, 1999, 2000 Harald Koerfgen
  10 * Copyright (C) 1998 Gleb Raiko & Vladimir Roganov
  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/cacheops.h>
  19#include <asm/page.h>
  20#include <asm/pgtable.h>
  21#include <asm/mmu_context.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
  32/* This sequence is required to ensure icache is disabled immediately */
  33#define TX39_STOP_STREAMING() \
  34__asm__ __volatile__( \
  35        ".set    push\n\t" \
  36        ".set    noreorder\n\t" \
  37        "b       1f\n\t" \
  38        "nop\n\t" \
  39        "1:\n\t" \
  40        ".set pop" \
  41        )
  42
  43/* TX39H-style cache flush routines. */
  44static void tx39h_flush_icache_all(void)
  45{
  46        unsigned long flags, config;
  47
  48        /* disable icache (set ICE#) */
  49        local_irq_save(flags);
  50        config = read_c0_conf();
  51        write_c0_conf(config & ~TX39_CONF_ICE);
  52        TX39_STOP_STREAMING();
  53        blast_icache16();
  54        write_c0_conf(config);
  55        local_irq_restore(flags);
  56}
  57
  58static void tx39h_dma_cache_wback_inv(unsigned long addr, unsigned long size)
  59{
  60        /* Catch bad driver code */
  61        BUG_ON(size == 0);
  62
  63        iob();
  64        blast_inv_dcache_range(addr, addr + size);
  65}
  66
  67
  68/* TX39H2,TX39H3 */
  69static inline void tx39_blast_dcache_page(unsigned long addr)
  70{
  71        if (current_cpu_type() != CPU_TX3912)
  72                blast_dcache16_page(addr);
  73}
  74
  75static inline void tx39_blast_dcache_page_indexed(unsigned long addr)
  76{
  77        blast_dcache16_page_indexed(addr);
  78}
  79
  80static inline void tx39_blast_dcache(void)
  81{
  82        blast_dcache16();
  83}
  84
  85static inline void tx39_blast_icache_page(unsigned long addr)
  86{
  87        unsigned long flags, config;
  88        /* disable icache (set ICE#) */
  89        local_irq_save(flags);
  90        config = read_c0_conf();
  91        write_c0_conf(config & ~TX39_CONF_ICE);
  92        TX39_STOP_STREAMING();
  93        blast_icache16_page(addr);
  94        write_c0_conf(config);
  95        local_irq_restore(flags);
  96}
  97
  98static inline void tx39_blast_icache_page_indexed(unsigned long addr)
  99{
 100        unsigned long flags, config;
 101        /* disable icache (set ICE#) */
 102        local_irq_save(flags);
 103        config = read_c0_conf();
 104        write_c0_conf(config & ~TX39_CONF_ICE);
 105        TX39_STOP_STREAMING();
 106        blast_icache16_page_indexed(addr);
 107        write_c0_conf(config);
 108        local_irq_restore(flags);
 109}
 110
 111static inline void tx39_blast_icache(void)
 112{
 113        unsigned long flags, config;
 114        /* disable icache (set ICE#) */
 115        local_irq_save(flags);
 116        config = read_c0_conf();
 117        write_c0_conf(config & ~TX39_CONF_ICE);
 118        TX39_STOP_STREAMING();
 119        blast_icache16();
 120        write_c0_conf(config);
 121        local_irq_restore(flags);
 122}
 123
 124static void tx39__flush_cache_vmap(void)
 125{
 126        tx39_blast_dcache();
 127}
 128
 129static void tx39__flush_cache_vunmap(void)
 130{
 131        tx39_blast_dcache();
 132}
 133
 134static inline void tx39_flush_cache_all(void)
 135{
 136        if (!cpu_has_dc_aliases)
 137                return;
 138
 139        tx39_blast_dcache();
 140}
 141
 142static inline void tx39___flush_cache_all(void)
 143{
 144        tx39_blast_dcache();
 145        tx39_blast_icache();
 146}
 147
 148static void tx39_flush_cache_mm(struct mm_struct *mm)
 149{
 150        if (!cpu_has_dc_aliases)
 151                return;
 152
 153        if (cpu_context(smp_processor_id(), mm) != 0)
 154                tx39_blast_dcache();
 155}
 156
 157static void tx39_flush_cache_range(struct vm_area_struct *vma,
 158        unsigned long start, unsigned long end)
 159{
 160        if (!cpu_has_dc_aliases)
 161                return;
 162        if (!(cpu_context(smp_processor_id(), vma->vm_mm)))
 163                return;
 164
 165        tx39_blast_dcache();
 166}
 167
 168static void tx39_flush_cache_page(struct vm_area_struct *vma, unsigned long page, unsigned long pfn)
 169{
 170        int exec = vma->vm_flags & VM_EXEC;
 171        struct mm_struct *mm = vma->vm_mm;
 172        pgd_t *pgdp;
 173        p4d_t *p4dp;
 174        pud_t *pudp;
 175        pmd_t *pmdp;
 176        pte_t *ptep;
 177
 178        /*
 179         * If ownes no valid ASID yet, cannot possibly have gotten
 180         * this page into the cache.
 181         */
 182        if (cpu_context(smp_processor_id(), mm) == 0)
 183                return;
 184
 185        page &= PAGE_MASK;
 186        pgdp = pgd_offset(mm, page);
 187        p4dp = p4d_offset(pgdp, page);
 188        pudp = pud_offset(p4dp, 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_flush_kernel_vmap_range(unsigned long vaddr, int size)
 257{
 258        BUG();
 259}
 260
 261static void tx39_dma_cache_wback_inv(unsigned long addr, unsigned long size)
 262{
 263        unsigned long end;
 264
 265        if (((size | addr) & (PAGE_SIZE - 1)) == 0) {
 266                end = addr + size;
 267                do {
 268                        tx39_blast_dcache_page(addr);
 269                        addr += PAGE_SIZE;
 270                } while(addr != end);
 271        } else if (size > dcache_size) {
 272                tx39_blast_dcache();
 273        } else {
 274                blast_dcache_range(addr, addr + size);
 275        }
 276}
 277
 278static void tx39_dma_cache_inv(unsigned long addr, unsigned long size)
 279{
 280        unsigned long end;
 281
 282        if (((size | addr) & (PAGE_SIZE - 1)) == 0) {
 283                end = addr + size;
 284                do {
 285                        tx39_blast_dcache_page(addr);
 286                        addr += PAGE_SIZE;
 287                } while(addr != end);
 288        } else if (size > dcache_size) {
 289                tx39_blast_dcache();
 290        } else {
 291                blast_inv_dcache_range(addr, addr + size);
 292        }
 293}
 294
 295static __init void tx39_probe_cache(void)
 296{
 297        unsigned long config;
 298
 299        config = read_c0_conf();
 300
 301        icache_size = 1 << (10 + ((config & TX39_CONF_ICS_MASK) >>
 302                                  TX39_CONF_ICS_SHIFT));
 303        dcache_size = 1 << (10 + ((config & TX39_CONF_DCS_MASK) >>
 304                                  TX39_CONF_DCS_SHIFT));
 305
 306        current_cpu_data.icache.linesz = 16;
 307        switch (current_cpu_type()) {
 308        case CPU_TX3912:
 309                current_cpu_data.icache.ways = 1;
 310                current_cpu_data.dcache.ways = 1;
 311                current_cpu_data.dcache.linesz = 4;
 312                break;
 313
 314        case CPU_TX3927:
 315                current_cpu_data.icache.ways = 2;
 316                current_cpu_data.dcache.ways = 2;
 317                current_cpu_data.dcache.linesz = 16;
 318                break;
 319
 320        case CPU_TX3922:
 321        default:
 322                current_cpu_data.icache.ways = 1;
 323                current_cpu_data.dcache.ways = 1;
 324                current_cpu_data.dcache.linesz = 16;
 325                break;
 326        }
 327}
 328
 329void tx39_cache_init(void)
 330{
 331        extern void build_clear_page(void);
 332        extern void build_copy_page(void);
 333        unsigned long config;
 334
 335        config = read_c0_conf();
 336        config &= ~TX39_CONF_WBON;
 337        write_c0_conf(config);
 338
 339        tx39_probe_cache();
 340
 341        switch (current_cpu_type()) {
 342        case CPU_TX3912:
 343                /* TX39/H core (writethru direct-map cache) */
 344                __flush_cache_vmap      = tx39__flush_cache_vmap;
 345                __flush_cache_vunmap    = tx39__flush_cache_vunmap;
 346                flush_cache_all = tx39h_flush_icache_all;
 347                __flush_cache_all       = tx39h_flush_icache_all;
 348                flush_cache_mm          = (void *) tx39h_flush_icache_all;
 349                flush_cache_range       = (void *) tx39h_flush_icache_all;
 350                flush_cache_page        = (void *) tx39h_flush_icache_all;
 351                flush_icache_range      = (void *) tx39h_flush_icache_all;
 352                local_flush_icache_range = (void *) tx39h_flush_icache_all;
 353
 354                local_flush_data_cache_page     = (void *) tx39h_flush_icache_all;
 355                flush_data_cache_page   = (void *) tx39h_flush_icache_all;
 356
 357                _dma_cache_wback_inv    = tx39h_dma_cache_wback_inv;
 358
 359                shm_align_mask          = PAGE_SIZE - 1;
 360
 361                break;
 362
 363        case CPU_TX3922:
 364        case CPU_TX3927:
 365        default:
 366                /* TX39/H2,H3 core (writeback 2way-set-associative cache) */
 367                /* board-dependent init code may set WBON */
 368
 369                __flush_cache_vmap      = tx39__flush_cache_vmap;
 370                __flush_cache_vunmap    = tx39__flush_cache_vunmap;
 371
 372                flush_cache_all = tx39_flush_cache_all;
 373                __flush_cache_all = tx39___flush_cache_all;
 374                flush_cache_mm = tx39_flush_cache_mm;
 375                flush_cache_range = tx39_flush_cache_range;
 376                flush_cache_page = tx39_flush_cache_page;
 377                flush_icache_range = tx39_flush_icache_range;
 378                local_flush_icache_range = tx39_flush_icache_range;
 379
 380                __flush_kernel_vmap_range = tx39_flush_kernel_vmap_range;
 381
 382                local_flush_data_cache_page = local_tx39_flush_data_cache_page;
 383                flush_data_cache_page = tx39_flush_data_cache_page;
 384
 385                _dma_cache_wback_inv = tx39_dma_cache_wback_inv;
 386                _dma_cache_wback = tx39_dma_cache_wback_inv;
 387                _dma_cache_inv = tx39_dma_cache_inv;
 388
 389                shm_align_mask = max_t(unsigned long,
 390                                       (dcache_size / current_cpu_data.dcache.ways) - 1,
 391                                       PAGE_SIZE - 1);
 392
 393                break;
 394        }
 395
 396        __flush_icache_user_range = flush_icache_range;
 397        __local_flush_icache_user_range = local_flush_icache_range;
 398
 399        current_cpu_data.icache.waysize = icache_size / current_cpu_data.icache.ways;
 400        current_cpu_data.dcache.waysize = dcache_size / current_cpu_data.dcache.ways;
 401
 402        current_cpu_data.icache.sets =
 403                current_cpu_data.icache.waysize / current_cpu_data.icache.linesz;
 404        current_cpu_data.dcache.sets =
 405                current_cpu_data.dcache.waysize / current_cpu_data.dcache.linesz;
 406
 407        if (current_cpu_data.dcache.waysize > PAGE_SIZE)
 408                current_cpu_data.dcache.flags |= MIPS_CACHE_ALIASES;
 409
 410        current_cpu_data.icache.waybit = 0;
 411        current_cpu_data.dcache.waybit = 0;
 412
 413        pr_info("Primary instruction cache %ldkB, linesize %d bytes\n",
 414                icache_size >> 10, current_cpu_data.icache.linesz);
 415        pr_info("Primary data cache %ldkB, linesize %d bytes\n",
 416                dcache_size >> 10, current_cpu_data.dcache.linesz);
 417
 418        build_clear_page();
 419        build_copy_page();
 420        tx39h_flush_icache_all();
 421}
 422