2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* r2300.c: R2000 and R3000 specific mmu/cache code.
|
|
|
|
*
|
2011-04-05 05:15:29 +08:00
|
|
|
* Copyright (C) 1996 David S. Miller (davem@davemloft.net)
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
|
|
|
* with a lot of changes to make this thing work for R3000s
|
|
|
|
* Tx39XX R4k style caches added. HK
|
|
|
|
* Copyright (C) 1998, 1999, 2000 Harald Koerfgen
|
|
|
|
* Copyright (C) 1998 Gleb Raiko & Vladimir Roganov
|
|
|
|
* Copyright (C) 2002 Ralf Baechle
|
|
|
|
* Copyright (C) 2002 Maciej W. Rozycki
|
|
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/sched.h>
|
2009-06-19 21:05:26 +08:00
|
|
|
#include <linux/smp.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/mm.h>
|
|
|
|
|
|
|
|
#include <asm/page.h>
|
|
|
|
#include <asm/pgtable.h>
|
|
|
|
#include <asm/mmu_context.h>
|
2011-11-29 00:11:28 +08:00
|
|
|
#include <asm/tlbmisc.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <asm/isadep.h>
|
|
|
|
#include <asm/io.h>
|
|
|
|
#include <asm/bootinfo.h>
|
|
|
|
#include <asm/cpu.h>
|
|
|
|
|
|
|
|
#undef DEBUG_TLB
|
|
|
|
|
|
|
|
extern void build_tlb_refill_handler(void);
|
|
|
|
|
|
|
|
/* CP0 hazard avoidance. */
|
|
|
|
#define BARRIER \
|
|
|
|
__asm__ __volatile__( \
|
|
|
|
".set push\n\t" \
|
|
|
|
".set noreorder\n\t" \
|
|
|
|
"nop\n\t" \
|
|
|
|
".set pop\n\t")
|
|
|
|
|
2015-05-27 21:15:15 +08:00
|
|
|
static int r3k_have_wired_reg; /* Should be in cpu_data? */
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* TLB operations. */
|
2015-05-27 21:15:08 +08:00
|
|
|
static void local_flush_tlb_from(int entry)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
unsigned long old_ctx;
|
|
|
|
|
2013-05-14 04:56:44 +08:00
|
|
|
old_ctx = read_c0_entryhi() & ASID_MASK;
|
2005-04-17 06:20:36 +08:00
|
|
|
write_c0_entrylo0(0);
|
|
|
|
for (; entry < current_cpu_data.tlbsize; entry++) {
|
|
|
|
write_c0_index(entry << 8);
|
|
|
|
write_c0_entryhi((entry | 0x80000) << 12);
|
|
|
|
BARRIER;
|
|
|
|
tlb_write_indexed();
|
|
|
|
}
|
|
|
|
write_c0_entryhi(old_ctx);
|
2015-05-27 21:15:08 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void local_flush_tlb_all(void)
|
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
#ifdef DEBUG_TLB
|
|
|
|
printk("[tlball]");
|
|
|
|
#endif
|
|
|
|
local_irq_save(flags);
|
|
|
|
local_flush_tlb_from(r3k_have_wired_reg ? read_c0_wired() : 8);
|
2005-04-17 06:20:36 +08:00
|
|
|
local_irq_restore(flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
void local_flush_tlb_mm(struct mm_struct *mm)
|
|
|
|
{
|
|
|
|
int cpu = smp_processor_id();
|
|
|
|
|
|
|
|
if (cpu_context(cpu, mm) != 0) {
|
|
|
|
#ifdef DEBUG_TLB
|
|
|
|
printk("[tlbmm<%lu>]", (unsigned long)cpu_context(cpu, mm));
|
|
|
|
#endif
|
|
|
|
drop_mmu_context(mm, cpu);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
|
|
|
|
unsigned long end)
|
|
|
|
{
|
|
|
|
struct mm_struct *mm = vma->vm_mm;
|
|
|
|
int cpu = smp_processor_id();
|
|
|
|
|
|
|
|
if (cpu_context(cpu, mm) != 0) {
|
2009-05-20 14:12:32 +08:00
|
|
|
unsigned long size, flags;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#ifdef DEBUG_TLB
|
|
|
|
printk("[tlbrange<%lu,0x%08lx,0x%08lx>]",
|
2013-05-14 04:56:44 +08:00
|
|
|
cpu_context(cpu, mm) & ASID_MASK, start, end);
|
2005-04-17 06:20:36 +08:00
|
|
|
#endif
|
|
|
|
local_irq_save(flags);
|
|
|
|
size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
|
|
|
|
if (size <= current_cpu_data.tlbsize) {
|
2013-05-14 04:56:44 +08:00
|
|
|
int oldpid = read_c0_entryhi() & ASID_MASK;
|
|
|
|
int newpid = cpu_context(cpu, mm) & ASID_MASK;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
start &= PAGE_MASK;
|
|
|
|
end += PAGE_SIZE - 1;
|
|
|
|
end &= PAGE_MASK;
|
|
|
|
while (start < end) {
|
|
|
|
int idx;
|
|
|
|
|
|
|
|
write_c0_entryhi(start | newpid);
|
|
|
|
start += PAGE_SIZE; /* BARRIER */
|
|
|
|
tlb_probe();
|
|
|
|
idx = read_c0_index();
|
|
|
|
write_c0_entrylo0(0);
|
|
|
|
write_c0_entryhi(KSEG0);
|
|
|
|
if (idx < 0) /* BARRIER */
|
|
|
|
continue;
|
|
|
|
tlb_write_indexed();
|
|
|
|
}
|
|
|
|
write_c0_entryhi(oldpid);
|
|
|
|
} else {
|
|
|
|
drop_mmu_context(mm, cpu);
|
|
|
|
}
|
|
|
|
local_irq_restore(flags);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void local_flush_tlb_kernel_range(unsigned long start, unsigned long end)
|
|
|
|
{
|
2009-05-20 14:12:32 +08:00
|
|
|
unsigned long size, flags;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#ifdef DEBUG_TLB
|
|
|
|
printk("[tlbrange<%lu,0x%08lx,0x%08lx>]", start, end);
|
|
|
|
#endif
|
|
|
|
local_irq_save(flags);
|
|
|
|
size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
|
|
|
|
if (size <= current_cpu_data.tlbsize) {
|
|
|
|
int pid = read_c0_entryhi();
|
|
|
|
|
|
|
|
start &= PAGE_MASK;
|
|
|
|
end += PAGE_SIZE - 1;
|
|
|
|
end &= PAGE_MASK;
|
|
|
|
|
|
|
|
while (start < end) {
|
|
|
|
int idx;
|
|
|
|
|
|
|
|
write_c0_entryhi(start);
|
|
|
|
start += PAGE_SIZE; /* BARRIER */
|
|
|
|
tlb_probe();
|
|
|
|
idx = read_c0_index();
|
|
|
|
write_c0_entrylo0(0);
|
|
|
|
write_c0_entryhi(KSEG0);
|
|
|
|
if (idx < 0) /* BARRIER */
|
|
|
|
continue;
|
|
|
|
tlb_write_indexed();
|
|
|
|
}
|
|
|
|
write_c0_entryhi(pid);
|
|
|
|
} else {
|
|
|
|
local_flush_tlb_all();
|
|
|
|
}
|
|
|
|
local_irq_restore(flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
|
|
|
|
{
|
|
|
|
int cpu = smp_processor_id();
|
|
|
|
|
2014-07-06 07:23:58 +08:00
|
|
|
if (cpu_context(cpu, vma->vm_mm) != 0) {
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned long flags;
|
|
|
|
int oldpid, newpid, idx;
|
|
|
|
|
|
|
|
#ifdef DEBUG_TLB
|
|
|
|
printk("[tlbpage<%lu,0x%08lx>]", cpu_context(cpu, vma->vm_mm), page);
|
|
|
|
#endif
|
2013-05-14 04:56:44 +08:00
|
|
|
newpid = cpu_context(cpu, vma->vm_mm) & ASID_MASK;
|
2005-04-17 06:20:36 +08:00
|
|
|
page &= PAGE_MASK;
|
|
|
|
local_irq_save(flags);
|
2013-05-14 04:56:44 +08:00
|
|
|
oldpid = read_c0_entryhi() & ASID_MASK;
|
2005-04-17 06:20:36 +08:00
|
|
|
write_c0_entryhi(page | newpid);
|
|
|
|
BARRIER;
|
|
|
|
tlb_probe();
|
|
|
|
idx = read_c0_index();
|
|
|
|
write_c0_entrylo0(0);
|
|
|
|
write_c0_entryhi(KSEG0);
|
|
|
|
if (idx < 0) /* BARRIER */
|
|
|
|
goto finish;
|
|
|
|
tlb_write_indexed();
|
|
|
|
|
|
|
|
finish:
|
|
|
|
write_c0_entryhi(oldpid);
|
|
|
|
local_irq_restore(flags);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void __update_tlb(struct vm_area_struct *vma, unsigned long address, pte_t pte)
|
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
int idx, pid;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Handle debugger faulting in for debugee.
|
|
|
|
*/
|
|
|
|
if (current->active_mm != vma->vm_mm)
|
|
|
|
return;
|
|
|
|
|
2013-05-14 04:56:44 +08:00
|
|
|
pid = read_c0_entryhi() & ASID_MASK;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#ifdef DEBUG_TLB
|
2013-05-14 04:56:44 +08:00
|
|
|
if ((pid != (cpu_context(cpu, vma->vm_mm) & ASID_MASK)) || (cpu_context(cpu, vma->vm_mm) == 0)) {
|
2005-04-17 06:20:36 +08:00
|
|
|
printk("update_mmu_cache: Wheee, bogus tlbpid mmpid=%lu tlbpid=%d\n",
|
|
|
|
(cpu_context(cpu, vma->vm_mm)), pid);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
local_irq_save(flags);
|
|
|
|
address &= PAGE_MASK;
|
|
|
|
write_c0_entryhi(address | pid);
|
|
|
|
BARRIER;
|
|
|
|
tlb_probe();
|
|
|
|
idx = read_c0_index();
|
|
|
|
write_c0_entrylo0(pte_val(pte));
|
|
|
|
write_c0_entryhi(address | pid);
|
|
|
|
if (idx < 0) { /* BARRIER */
|
|
|
|
tlb_write_random();
|
|
|
|
} else {
|
|
|
|
tlb_write_indexed();
|
|
|
|
}
|
|
|
|
write_c0_entryhi(pid);
|
|
|
|
local_irq_restore(flags);
|
|
|
|
}
|
|
|
|
|
2011-08-03 01:51:08 +08:00
|
|
|
void add_wired_entry(unsigned long entrylo0, unsigned long entrylo1,
|
|
|
|
unsigned long entryhi, unsigned long pagemask)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
unsigned long old_ctx;
|
|
|
|
static unsigned long wired = 0;
|
|
|
|
|
|
|
|
if (r3k_have_wired_reg) { /* TX39XX */
|
|
|
|
unsigned long old_pagemask;
|
|
|
|
unsigned long w;
|
|
|
|
|
|
|
|
#ifdef DEBUG_TLB
|
|
|
|
printk("[tlbwired<entry lo0 %8x, hi %8x\n, pagemask %8x>]\n",
|
|
|
|
entrylo0, entryhi, pagemask);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
local_irq_save(flags);
|
|
|
|
/* Save old context and create impossible VPN2 value */
|
2013-05-14 04:56:44 +08:00
|
|
|
old_ctx = read_c0_entryhi() & ASID_MASK;
|
2005-04-17 06:20:36 +08:00
|
|
|
old_pagemask = read_c0_pagemask();
|
|
|
|
w = read_c0_wired();
|
|
|
|
write_c0_wired(w + 1);
|
|
|
|
write_c0_index(w << 8);
|
|
|
|
write_c0_pagemask(pagemask);
|
|
|
|
write_c0_entryhi(entryhi);
|
|
|
|
write_c0_entrylo0(entrylo0);
|
|
|
|
BARRIER;
|
|
|
|
tlb_write_indexed();
|
|
|
|
|
|
|
|
write_c0_entryhi(old_ctx);
|
|
|
|
write_c0_pagemask(old_pagemask);
|
|
|
|
local_flush_tlb_all();
|
|
|
|
local_irq_restore(flags);
|
|
|
|
|
|
|
|
} else if (wired < 8) {
|
|
|
|
#ifdef DEBUG_TLB
|
|
|
|
printk("[tlbwired<entry lo0 %8x, hi %8x\n>]\n",
|
|
|
|
entrylo0, entryhi);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
local_irq_save(flags);
|
2013-05-14 04:56:44 +08:00
|
|
|
old_ctx = read_c0_entryhi() & ASID_MASK;
|
2005-04-17 06:20:36 +08:00
|
|
|
write_c0_entrylo0(entrylo0);
|
|
|
|
write_c0_entryhi(entryhi);
|
|
|
|
write_c0_index(wired);
|
|
|
|
wired++; /* BARRIER */
|
|
|
|
tlb_write_indexed();
|
|
|
|
write_c0_entryhi(old_ctx);
|
|
|
|
local_flush_tlb_all();
|
|
|
|
local_irq_restore(flags);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
MIPS: Delete __cpuinit/__CPUINIT usage from MIPS code
commit 3747069b25e419f6b51395f48127e9812abc3596 upstream.
The __cpuinit type of throwaway sections might have made sense
some time ago when RAM was more constrained, but now the savings
do not offset the cost and complications. For example, the fix in
commit 5e427ec2d0 ("x86: Fix bit corruption at CPU resume time")
is a good example of the nasty type of bugs that can be created
with improper use of the various __init prefixes.
After a discussion on LKML[1] it was decided that cpuinit should go
the way of devinit and be phased out. Once all the users are gone,
we can then finally remove the macros themselves from linux/init.h.
Note that some harmless section mismatch warnings may result, since
notify_cpu_starting() and cpu_up() are arch independent (kernel/cpu.c)
and are flagged as __cpuinit -- so if we remove the __cpuinit from
the arch specific callers, we will also get section mismatch warnings.
As an intermediate step, we intend to turn the linux/init.h cpuinit
related content into no-ops as early as possible, since that will get
rid of these warnings. In any case, they are temporary and harmless.
Here, we remove all the MIPS __cpuinit from C code and __CPUINIT
from asm files. MIPS is interesting in this respect, because there
are also uasm users hiding behind their own renamed versions of the
__cpuinit macros.
[1] https://lkml.org/lkml/2013/5/20/589
[ralf@linux-mips.org: Folded in Paul's followup fix.]
Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/5494/
Patchwork: https://patchwork.linux-mips.org/patch/5495/
Patchwork: https://patchwork.linux-mips.org/patch/5509/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2013-06-18 21:38:59 +08:00
|
|
|
void tlb_init(void)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2015-05-27 21:15:15 +08:00
|
|
|
switch (current_cpu_type()) {
|
|
|
|
case CPU_TX3922:
|
|
|
|
case CPU_TX3927:
|
|
|
|
r3k_have_wired_reg = 1;
|
|
|
|
write_c0_wired(0); /* Set to 8 on reset... */
|
|
|
|
break;
|
|
|
|
}
|
2015-05-27 21:15:08 +08:00
|
|
|
local_flush_tlb_from(0);
|
2005-04-17 06:20:36 +08:00
|
|
|
build_tlb_refill_handler();
|
|
|
|
}
|