123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380 |
- /*
- * AVR32 TLB operations
- *
- * Copyright (C) 2004-2006 Atmel Corporation
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
- #include <linux/mm.h>
- #include <asm/mmu_context.h>
- #define _TLBEHI_I 0x100
- void show_dtlb_entry(unsigned int index)
- {
- unsigned int tlbehi, tlbehi_save, tlbelo, mmucr, mmucr_save;
- unsigned long flags;
- local_irq_save(flags);
- mmucr_save = sysreg_read(MMUCR);
- tlbehi_save = sysreg_read(TLBEHI);
- mmucr = mmucr_save & 0x13;
- mmucr |= index << 14;
- sysreg_write(MMUCR, mmucr);
- asm volatile("tlbr" : : : "memory");
- cpu_sync_pipeline();
- tlbehi = sysreg_read(TLBEHI);
- tlbelo = sysreg_read(TLBELO);
- printk("%2u: %c %c %02x %05x %05x %o %o %c %c %c %c\n",
- index,
- (tlbehi & 0x200)?'1':'0',
- (tlbelo & 0x100)?'1':'0',
- (tlbehi & 0xff),
- (tlbehi >> 12), (tlbelo >> 12),
- (tlbelo >> 4) & 7, (tlbelo >> 2) & 3,
- (tlbelo & 0x200)?'1':'0',
- (tlbelo & 0x080)?'1':'0',
- (tlbelo & 0x001)?'1':'0',
- (tlbelo & 0x002)?'1':'0');
- sysreg_write(MMUCR, mmucr_save);
- sysreg_write(TLBEHI, tlbehi_save);
- cpu_sync_pipeline();
- local_irq_restore(flags);
- }
- void dump_dtlb(void)
- {
- unsigned int i;
- printk("ID V G ASID VPN PFN AP SZ C B W D\n");
- for (i = 0; i < 32; i++)
- show_dtlb_entry(i);
- }
- static unsigned long last_mmucr;
- static inline void set_replacement_pointer(unsigned shift)
- {
- unsigned long mmucr, mmucr_save;
- mmucr = mmucr_save = sysreg_read(MMUCR);
- /* Does this mapping already exist? */
- __asm__ __volatile__(
- " tlbs\n"
- " mfsr %0, %1"
- : "=r"(mmucr)
- : "i"(SYSREG_MMUCR));
- if (mmucr & SYSREG_BIT(MMUCR_N)) {
- /* Not found -- pick a not-recently-accessed entry */
- unsigned long rp;
- unsigned long tlbar = sysreg_read(TLBARLO);
- rp = 32 - fls(tlbar);
- if (rp == 32) {
- rp = 0;
- sysreg_write(TLBARLO, -1L);
- }
- mmucr &= 0x13;
- mmucr |= (rp << shift);
- sysreg_write(MMUCR, mmucr);
- }
- last_mmucr = mmucr;
- }
- static void update_dtlb(unsigned long address, pte_t pte, unsigned long asid)
- {
- unsigned long vpn;
- vpn = (address & MMU_VPN_MASK) | _TLBEHI_VALID | asid;
- sysreg_write(TLBEHI, vpn);
- cpu_sync_pipeline();
- set_replacement_pointer(14);
- sysreg_write(TLBELO, pte_val(pte) & _PAGE_FLAGS_HARDWARE_MASK);
- /* Let's go */
- asm volatile("nop\n\ttlbw" : : : "memory");
- cpu_sync_pipeline();
- }
- void update_mmu_cache(struct vm_area_struct *vma,
- unsigned long address, pte_t pte)
- {
- unsigned long flags;
- /* ptrace may call this routine */
- if (vma && current->active_mm != vma->vm_mm)
- return;
- local_irq_save(flags);
- update_dtlb(address, pte, get_asid());
- local_irq_restore(flags);
- }
- void __flush_tlb_page(unsigned long asid, unsigned long page)
- {
- unsigned long mmucr, tlbehi;
- page |= asid;
- sysreg_write(TLBEHI, page);
- cpu_sync_pipeline();
- asm volatile("tlbs");
- mmucr = sysreg_read(MMUCR);
- if (!(mmucr & SYSREG_BIT(MMUCR_N))) {
- unsigned long tlbarlo;
- unsigned long entry;
- /* Clear the "valid" bit */
- tlbehi = sysreg_read(TLBEHI);
- tlbehi &= ~_TLBEHI_VALID;
- sysreg_write(TLBEHI, tlbehi);
- cpu_sync_pipeline();
- /* mark the entry as "not accessed" */
- entry = (mmucr >> 14) & 0x3f;
- tlbarlo = sysreg_read(TLBARLO);
- tlbarlo |= (0x80000000 >> entry);
- sysreg_write(TLBARLO, tlbarlo);
- /* update the entry with valid bit clear */
- asm volatile("tlbw");
- cpu_sync_pipeline();
- }
- }
- void flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
- {
- if (vma->vm_mm && vma->vm_mm->context != NO_CONTEXT) {
- unsigned long flags, asid;
- unsigned long saved_asid = MMU_NO_ASID;
- asid = vma->vm_mm->context & MMU_CONTEXT_ASID_MASK;
- page &= PAGE_MASK;
- local_irq_save(flags);
- if (vma->vm_mm != current->mm) {
- saved_asid = get_asid();
- set_asid(asid);
- }
- __flush_tlb_page(asid, page);
- if (saved_asid != MMU_NO_ASID)
- set_asid(saved_asid);
- local_irq_restore(flags);
- }
- }
- void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
- unsigned long end)
- {
- struct mm_struct *mm = vma->vm_mm;
- if (mm->context != NO_CONTEXT) {
- unsigned long flags;
- int size;
- local_irq_save(flags);
- size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
- if (size > (MMU_DTLB_ENTRIES / 4)) { /* Too many entries to flush */
- mm->context = NO_CONTEXT;
- if (mm == current->mm)
- activate_context(mm);
- } else {
- unsigned long asid = mm->context & MMU_CONTEXT_ASID_MASK;
- unsigned long saved_asid = MMU_NO_ASID;
- start &= PAGE_MASK;
- end += (PAGE_SIZE - 1);
- end &= PAGE_MASK;
- if (mm != current->mm) {
- saved_asid = get_asid();
- set_asid(asid);
- }
- while (start < end) {
- __flush_tlb_page(asid, start);
- start += PAGE_SIZE;
- }
- if (saved_asid != MMU_NO_ASID)
- set_asid(saved_asid);
- }
- local_irq_restore(flags);
- }
- }
- /*
- * TODO: If this is only called for addresses > TASK_SIZE, we can probably
- * skip the ASID stuff and just use the Global bit...
- */
- void flush_tlb_kernel_range(unsigned long start, unsigned long end)
- {
- unsigned long flags;
- int size;
- local_irq_save(flags);
- size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
- if (size > (MMU_DTLB_ENTRIES / 4)) { /* Too many entries to flush */
- flush_tlb_all();
- } else {
- unsigned long asid = init_mm.context & MMU_CONTEXT_ASID_MASK;
- unsigned long saved_asid = get_asid();
- start &= PAGE_MASK;
- end += (PAGE_SIZE - 1);
- end &= PAGE_MASK;
- set_asid(asid);
- while (start < end) {
- __flush_tlb_page(asid, start);
- start += PAGE_SIZE;
- }
- set_asid(saved_asid);
- }
- local_irq_restore(flags);
- }
- void flush_tlb_mm(struct mm_struct *mm)
- {
- /* Invalidate all TLB entries of this process by getting a new ASID */
- if (mm->context != NO_CONTEXT) {
- unsigned long flags;
- local_irq_save(flags);
- mm->context = NO_CONTEXT;
- if (mm == current->mm)
- activate_context(mm);
- local_irq_restore(flags);
- }
- }
- void flush_tlb_all(void)
- {
- unsigned long flags;
- local_irq_save(flags);
- sysreg_write(MMUCR, sysreg_read(MMUCR) | SYSREG_BIT(MMUCR_I));
- local_irq_restore(flags);
- }
- #ifdef CONFIG_PROC_FS
- #include <linux/seq_file.h>
- #include <linux/proc_fs.h>
- #include <linux/init.h>
- static void *tlb_start(struct seq_file *tlb, loff_t *pos)
- {
- static unsigned long tlb_index;
- if (*pos >= 32)
- return NULL;
- tlb_index = 0;
- return &tlb_index;
- }
- static void *tlb_next(struct seq_file *tlb, void *v, loff_t *pos)
- {
- unsigned long *index = v;
- if (*index >= 31)
- return NULL;
- ++*pos;
- ++*index;
- return index;
- }
- static void tlb_stop(struct seq_file *tlb, void *v)
- {
- }
- static int tlb_show(struct seq_file *tlb, void *v)
- {
- unsigned int tlbehi, tlbehi_save, tlbelo, mmucr, mmucr_save;
- unsigned long flags;
- unsigned long *index = v;
- if (*index == 0)
- seq_puts(tlb, "ID V G ASID VPN PFN AP SZ C B W D\n");
- BUG_ON(*index >= 32);
- local_irq_save(flags);
- mmucr_save = sysreg_read(MMUCR);
- tlbehi_save = sysreg_read(TLBEHI);
- mmucr = mmucr_save & 0x13;
- mmucr |= *index << 14;
- sysreg_write(MMUCR, mmucr);
- asm volatile("tlbr" : : : "memory");
- cpu_sync_pipeline();
- tlbehi = sysreg_read(TLBEHI);
- tlbelo = sysreg_read(TLBELO);
- sysreg_write(MMUCR, mmucr_save);
- sysreg_write(TLBEHI, tlbehi_save);
- cpu_sync_pipeline();
- local_irq_restore(flags);
- seq_printf(tlb, "%2lu: %c %c %02x %05x %05x %o %o %c %c %c %c\n",
- *index,
- (tlbehi & 0x200)?'1':'0',
- (tlbelo & 0x100)?'1':'0',
- (tlbehi & 0xff),
- (tlbehi >> 12), (tlbelo >> 12),
- (tlbelo >> 4) & 7, (tlbelo >> 2) & 3,
- (tlbelo & 0x200)?'1':'0',
- (tlbelo & 0x080)?'1':'0',
- (tlbelo & 0x001)?'1':'0',
- (tlbelo & 0x002)?'1':'0');
- return 0;
- }
- static struct seq_operations tlb_ops = {
- .start = tlb_start,
- .next = tlb_next,
- .stop = tlb_stop,
- .show = tlb_show,
- };
- static int tlb_open(struct inode *inode, struct file *file)
- {
- return seq_open(file, &tlb_ops);
- }
- static const struct file_operations proc_tlb_operations = {
- .open = tlb_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = seq_release,
- };
- static int __init proctlb_init(void)
- {
- struct proc_dir_entry *entry;
- entry = create_proc_entry("tlb", 0, NULL);
- if (entry)
- entry->proc_fops = &proc_tlb_operations;
- return 0;
- }
- late_initcall(proctlb_init);
- #endif /* CONFIG_PROC_FS */
|