linux/arch/score/mm/tlb-score.c
<<
>>
Prefs
   1/*
   2 * arch/score/mm/tlb-score.c
   3 *
   4 * Score Processor version.
   5 *
   6 * Copyright (C) 2009 Sunplus Core Technology Co., Ltd.
   7 *  Lennox Wu <lennox.wu@sunplusct.com>
   8 *  Chen Liqin <liqin.chen@sunplusct.com>
   9 *
  10 * This program is free software; you can redistribute it and/or modify
  11 * it under the terms of the GNU General Public License as published by
  12 * the Free Software Foundation; either version 2 of the License, or
  13 * (at your option) any later version.
  14 *
  15 * This program is distributed in the hope that it will be useful,
  16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18 * GNU General Public License for more details.
  19 *
  20 * You should have received a copy of the GNU General Public License
  21 * along with this program; if not, see the file COPYING, or write
  22 * to the Free Software Foundation, Inc.,
  23 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  24 */
  25
  26#include <linux/highmem.h>
  27#include <linux/module.h>
  28
  29#include <asm/irq.h>
  30#include <asm/mmu_context.h>
  31#include <asm/tlb.h>
  32
  33#define TLBSIZE 32
  34
  35unsigned long asid_cache = ASID_FIRST_VERSION;
  36EXPORT_SYMBOL(asid_cache);
  37
  38void local_flush_tlb_all(void)
  39{
  40        unsigned long flags;
  41        unsigned long old_ASID;
  42        int entry;
  43
  44        local_irq_save(flags);
  45        old_ASID = pevn_get() & ASID_MASK;
  46        pectx_set(0);                   /* invalid */
  47        entry = tlblock_get();          /* skip locked entries*/
  48
  49        for (; entry < TLBSIZE; entry++) {
  50                tlbpt_set(entry);
  51                pevn_set(KSEG1);
  52                barrier();
  53                tlb_write_indexed();
  54        }
  55        pevn_set(old_ASID);
  56        local_irq_restore(flags);
  57}
  58
  59/*
  60 * If mm is currently active_mm, we can't really drop it. Instead,
  61 * we will get a new one for it.
  62 */
  63static inline void
  64drop_mmu_context(struct mm_struct *mm)
  65{
  66        unsigned long flags;
  67
  68        local_irq_save(flags);
  69        get_new_mmu_context(mm);
  70        pevn_set(mm->context & ASID_MASK);
  71        local_irq_restore(flags);
  72}
  73
  74void local_flush_tlb_mm(struct mm_struct *mm)
  75{
  76        if (mm->context != 0)
  77                drop_mmu_context(mm);
  78}
  79
  80void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
  81        unsigned long end)
  82{
  83        struct mm_struct *mm = vma->vm_mm;
  84        unsigned long vma_mm_context = mm->context;
  85        if (mm->context != 0) {
  86                unsigned long flags;
  87                int size;
  88
  89                local_irq_save(flags);
  90                size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
  91                if (size <= TLBSIZE) {
  92                        int oldpid = pevn_get() & ASID_MASK;
  93                        int newpid = vma_mm_context & ASID_MASK;
  94
  95                        start &= PAGE_MASK;
  96                        end += (PAGE_SIZE - 1);
  97                        end &= PAGE_MASK;
  98                        while (start < end) {
  99                                int idx;
 100
 101                                pevn_set(start | newpid);
 102                                start += PAGE_SIZE;
 103                                barrier();
 104                                tlb_probe();
 105                                idx = tlbpt_get();
 106                                pectx_set(0);
 107                                pevn_set(KSEG1);
 108                                if (idx < 0)
 109                                        continue;
 110                                tlb_write_indexed();
 111                        }
 112                        pevn_set(oldpid);
 113                } else {
 114                        /* Bigger than TLBSIZE, get new ASID directly */
 115                        get_new_mmu_context(mm);
 116                        if (mm == current->active_mm)
 117                                pevn_set(vma_mm_context & ASID_MASK);
 118                }
 119                local_irq_restore(flags);
 120        }
 121}
 122
 123void local_flush_tlb_kernel_range(unsigned long start, unsigned long end)
 124{
 125        unsigned long flags;
 126        int size;
 127
 128        local_irq_save(flags);
 129        size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
 130        if (size <= TLBSIZE) {
 131                int pid = pevn_get();
 132
 133                start &= PAGE_MASK;
 134                end += PAGE_SIZE - 1;
 135                end &= PAGE_MASK;
 136
 137                while (start < end) {
 138                        long idx;
 139
 140                        pevn_set(start);
 141                        start += PAGE_SIZE;
 142                        tlb_probe();
 143                        idx = tlbpt_get();
 144                        if (idx < 0)
 145                                continue;
 146                        pectx_set(0);
 147                        pevn_set(KSEG1);
 148                        barrier();
 149                        tlb_write_indexed();
 150                }
 151                pevn_set(pid);
 152        } else {
 153                local_flush_tlb_all();
 154        }
 155
 156        local_irq_restore(flags);
 157}
 158
 159void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
 160{
 161        if (vma && vma->vm_mm->context != 0) {
 162                unsigned long flags;
 163                int oldpid, newpid, idx;
 164                unsigned long vma_ASID = vma->vm_mm->context;
 165
 166                newpid = vma_ASID & ASID_MASK;
 167                page &= PAGE_MASK;
 168                local_irq_save(flags);
 169                oldpid = pevn_get() & ASID_MASK;
 170                pevn_set(page | newpid);
 171                barrier();
 172                tlb_probe();
 173                idx = tlbpt_get();
 174                pectx_set(0);
 175                pevn_set(KSEG1);
 176                if (idx < 0)            /* p_bit(31) - 1: miss, 0: hit*/
 177                        goto finish;
 178                barrier();
 179                tlb_write_indexed();
 180finish:
 181                pevn_set(oldpid);
 182                local_irq_restore(flags);
 183        }
 184}
 185
 186/*
 187 * This one is only used for pages with the global bit set so we don't care
 188 * much about the ASID.
 189 */
 190void local_flush_tlb_one(unsigned long page)
 191{
 192        unsigned long flags;
 193        int oldpid, idx;
 194
 195        local_irq_save(flags);
 196        oldpid = pevn_get();
 197        page &= (PAGE_MASK << 1);
 198        pevn_set(page);
 199        barrier();
 200        tlb_probe();
 201        idx = tlbpt_get();
 202        pectx_set(0);
 203        if (idx >= 0) {
 204                /* Make sure all entries differ. */
 205                pevn_set(KSEG1);
 206                barrier();
 207                tlb_write_indexed();
 208        }
 209        pevn_set(oldpid);
 210        local_irq_restore(flags);
 211}
 212
 213void __update_tlb(struct vm_area_struct *vma, unsigned long address, pte_t pte)
 214{
 215        unsigned long flags;
 216        int idx, pid;
 217
 218        /*
 219         * Handle debugger faulting in for debugee.
 220         */
 221        if (current->active_mm != vma->vm_mm)
 222                return;
 223
 224        pid = pevn_get() & ASID_MASK;
 225
 226        local_irq_save(flags);
 227        address &= PAGE_MASK;
 228        pevn_set(address | pid);
 229        barrier();
 230        tlb_probe();
 231        idx = tlbpt_get();
 232        pectx_set(pte_val(pte));
 233        pevn_set(address | pid);
 234        if (idx < 0)
 235                tlb_write_random();
 236        else
 237                tlb_write_indexed();
 238
 239        pevn_set(pid);
 240        local_irq_restore(flags);
 241}
 242
 243void __cpuinit tlb_init(void)
 244{
 245        tlblock_set(0);
 246        local_flush_tlb_all();
 247        memcpy((void *)(EXCEPTION_VECTOR_BASE_ADDR + 0x100),
 248                        &score7_FTLB_refill_Handler, 0xFC);
 249        flush_icache_range(EXCEPTION_VECTOR_BASE_ADDR + 0x100,
 250                        EXCEPTION_VECTOR_BASE_ADDR + 0x1FC);
 251}
 252