tlb-flush.c 4.5 KB
Newer Older
1 2 3 4
/*
 * TLB flushing operations for SH with an MMU.
 *
 *  Copyright (C) 1999  Niibe Yutaka
5
 *  Copyright (C) 2003 - 2006  Paul Mundt
6 7 8 9 10 11
 *
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file "COPYING" in the main directory of this archive
 * for more details.
 */
#include <linux/mm.h>
12
#include <linux/io.h>
13 14
#include <asm/mmu_context.h>
#include <asm/tlbflush.h>
15
#include <asm/cacheflush.h>
16

17
void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
18
{
P
Paul Mundt 已提交
19 20 21
	unsigned int cpu = smp_processor_id();

	if (vma->vm_mm && cpu_context(cpu, vma->vm_mm) != NO_CONTEXT) {
22 23 24 25
		unsigned long flags;
		unsigned long asid;
		unsigned long saved_asid = MMU_NO_ASID;

P
Paul Mundt 已提交
26
		asid = cpu_asid(cpu, vma->vm_mm);
27 28 29 30 31 32 33
		page &= PAGE_MASK;

		local_irq_save(flags);
		if (vma->vm_mm != current->mm) {
			saved_asid = get_asid();
			set_asid(asid);
		}
34
		local_flush_tlb_one(asid, page);
35 36 37 38 39 40
		if (saved_asid != MMU_NO_ASID)
			set_asid(saved_asid);
		local_irq_restore(flags);
	}
}

41 42
void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
			   unsigned long end)
43 44
{
	struct mm_struct *mm = vma->vm_mm;
P
Paul Mundt 已提交
45
	unsigned int cpu = smp_processor_id();
46

P
Paul Mundt 已提交
47
	if (cpu_context(cpu, mm) != NO_CONTEXT) {
48 49 50 51 52 53
		unsigned long flags;
		int size;

		local_irq_save(flags);
		size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
		if (size > (MMU_NTLB_ENTRIES/4)) { /* Too many TLB to flush */
P
Paul Mundt 已提交
54
			cpu_context(cpu, mm) = NO_CONTEXT;
55
			if (mm == current->mm)
P
Paul Mundt 已提交
56
				activate_context(mm, cpu);
57
		} else {
P
Paul Mundt 已提交
58
			unsigned long asid;
59 60
			unsigned long saved_asid = MMU_NO_ASID;

P
Paul Mundt 已提交
61
			asid = cpu_asid(cpu, mm);
62 63 64 65 66 67 68 69
			start &= PAGE_MASK;
			end += (PAGE_SIZE - 1);
			end &= PAGE_MASK;
			if (mm != current->mm) {
				saved_asid = get_asid();
				set_asid(asid);
			}
			while (start < end) {
70
				local_flush_tlb_one(asid, start);
71 72 73 74 75 76 77 78 79
				start += PAGE_SIZE;
			}
			if (saved_asid != MMU_NO_ASID)
				set_asid(saved_asid);
		}
		local_irq_restore(flags);
	}
}

80
void local_flush_tlb_kernel_range(unsigned long start, unsigned long end)
81
{
P
Paul Mundt 已提交
82
	unsigned int cpu = smp_processor_id();
83 84 85 86 87 88
	unsigned long flags;
	int size;

	local_irq_save(flags);
	size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
	if (size > (MMU_NTLB_ENTRIES/4)) { /* Too many TLB to flush */
89
		local_flush_tlb_all();
90
	} else {
P
Paul Mundt 已提交
91
		unsigned long asid;
92 93
		unsigned long saved_asid = get_asid();

P
Paul Mundt 已提交
94
		asid = cpu_asid(cpu, &init_mm);
95 96 97 98 99
		start &= PAGE_MASK;
		end += (PAGE_SIZE - 1);
		end &= PAGE_MASK;
		set_asid(asid);
		while (start < end) {
100
			local_flush_tlb_one(asid, start);
101 102 103 104 105 106 107
			start += PAGE_SIZE;
		}
		set_asid(saved_asid);
	}
	local_irq_restore(flags);
}

108
void local_flush_tlb_mm(struct mm_struct *mm)
109
{
P
Paul Mundt 已提交
110 111
	unsigned int cpu = smp_processor_id();

112 113
	/* Invalidate all TLB of this process. */
	/* Instead of invalidating each TLB, we get new MMU context. */
P
Paul Mundt 已提交
114
	if (cpu_context(cpu, mm) != NO_CONTEXT) {
115 116 117
		unsigned long flags;

		local_irq_save(flags);
P
Paul Mundt 已提交
118
		cpu_context(cpu, mm) = NO_CONTEXT;
119
		if (mm == current->mm)
P
Paul Mundt 已提交
120
			activate_context(mm, cpu);
121 122 123 124
		local_irq_restore(flags);
	}
}

125
void local_flush_tlb_all(void)
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
{
	unsigned long flags, status;

	/*
	 * Flush all the TLB.
	 *
	 * Write to the MMU control register's bit:
	 *	TF-bit for SH-3, TI-bit for SH-4.
	 *      It's same position, bit #2.
	 */
	local_irq_save(flags);
	status = ctrl_inl(MMUCR);
	status |= 0x04;
	ctrl_outl(status, MMUCR);
	ctrl_barrier();
	local_irq_restore(flags);
}
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183

void update_mmu_cache(struct vm_area_struct *vma,
		      unsigned long address, pte_t pte)
{
	unsigned long flags;
	unsigned long pteval;
	unsigned long vpn;
	struct page *page;
	unsigned long pfn = pte_pfn(pte);
	struct address_space *mapping;

	if (!pfn_valid(pfn))
		return;

	page = pfn_to_page(pfn);
	mapping = page_mapping(page);
	if (mapping) {
		unsigned long phys = pte_val(pte) & PTE_PHYS_MASK;
		int dirty = test_and_clear_bit(PG_dcache_dirty, &page->flags);

		if (dirty)
			__flush_wback_region((void *)P1SEGADDR(phys),
					     PAGE_SIZE);
	}

	local_irq_save(flags);

	/* Set PTEH register */
	vpn = (address & MMU_VPN_MASK) | get_asid();
	ctrl_outl(vpn, MMU_PTEH);

	pteval = pte_val(pte);

#ifdef CONFIG_CPU_HAS_PTEA
	/* Set PTEA register */
	/* TODO: make this look less hacky */
	ctrl_outl(((pteval >> 28) & 0xe) | (pteval & 0x1), MMU_PTEA);
#endif

	/* Set PTEL register */
	pteval &= _PAGE_FLAGS_HARDWARE_MASK; /* drop software flags */
184
#if defined(CONFIG_SH_WRITETHROUGH) && defined(CONFIG_CPU_SH4)
185 186 187 188 189 190 191 192 193
	pteval |= _PAGE_WT;
#endif
	/* conveniently, we want all the software flags to be 0 anyway */
	ctrl_outl(pteval, MMU_PTEL);

	/* Load the TLB */
	asm volatile("ldtlb": /* no output */ : /* no input */ : "memory");
	local_irq_restore(flags);
}