diff options
Diffstat (limited to 'arch/mips/mm')
-rw-r--r-- | arch/mips/mm/dma-default.c | 2 | ||||
-rw-r--r-- | arch/mips/mm/init.c | 5 | ||||
-rw-r--r-- | arch/mips/mm/tlbex.c | 30 |
3 files changed, 32 insertions, 5 deletions
diff --git a/arch/mips/mm/dma-default.c b/arch/mips/mm/dma-default.c index 76903c72764..f60b3dc0fc6 100644 --- a/arch/mips/mm/dma-default.c +++ b/arch/mips/mm/dma-default.c @@ -35,7 +35,7 @@ static inline unsigned long dma_addr_to_virt(dma_addr_t dma_addr) static inline int cpu_is_noncoherent_r10000(struct device *dev) { return !plat_device_is_coherent(dev) && - (current_cpu_data.cputype == CPU_R10000 && + (current_cpu_data.cputype == CPU_R10000 || current_cpu_data.cputype == CPU_R12000); } diff --git a/arch/mips/mm/init.c b/arch/mips/mm/init.c index 09d91505b90..5240432e6d1 100644 --- a/arch/mips/mm/init.c +++ b/arch/mips/mm/init.c @@ -8,6 +8,7 @@ * Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved. */ +#include <linux/bug.h> #include <linux/init.h> #include <linux/module.h> #include <linux/signal.h> @@ -132,6 +133,8 @@ void *kmap_coherent(struct page *page, unsigned long addr) pte_t pte; int tlbidx; + BUG_ON(Page_dcache_dirty(page)); + inc_preempt_count(); idx = (addr >> PAGE_SHIFT) & (FIX_N_COLOURS - 1); #ifdef CONFIG_MIPS_MT_SMTC @@ -208,7 +211,7 @@ void copy_user_highpage(struct page *to, struct page *from, void *vfrom, *vto; vto = kmap_atomic(to, KM_USER1); - if (cpu_has_dc_aliases) { + if (cpu_has_dc_aliases && !Page_dcache_dirty(from)) { vfrom = kmap_coherent(from, vaddr); copy_page(vto, vfrom); kunmap_coherent(); diff --git a/arch/mips/mm/tlbex.c b/arch/mips/mm/tlbex.c index 4ec0964b839..6c425b05244 100644 --- a/arch/mips/mm/tlbex.c +++ b/arch/mips/mm/tlbex.c @@ -58,6 +58,21 @@ static __init int __maybe_unused r10000_llsc_war(void) } /* + * Found by experiment: At least some revisions of the 4kc throw under + * some circumstances a machine check exception, triggered by invalid + * values in the index register. Delaying the tlbp instruction until + * after the next branch, plus adding an additional nop in front of + * tlbwi/tlbwr avoids the invalid index register values. Nobody knows + * why; it's not an issue caused by the core RTL. + * + */ +static __init int __attribute__((unused)) m4kc_tlbp_war(void) +{ + return (current_cpu_data.processor_id & 0xffff00) == + (PRID_COMP_MIPS | PRID_IMP_4KC); +} + +/* * A little micro-assembler, intended for TLB refill handler * synthesizing. It is intentionally kept simple, does only support * a subset of instructions, and does not try to hide pipeline effects @@ -78,7 +93,7 @@ enum fields SET = 0x200 }; -#define OP_MASK 0x2f +#define OP_MASK 0x3f #define OP_SH 26 #define RS_MASK 0x1f #define RS_SH 21 @@ -92,7 +107,7 @@ enum fields #define IMM_SH 0 #define JIMM_MASK 0x3ffffff #define JIMM_SH 0 -#define FUNC_MASK 0x2f +#define FUNC_MASK 0x3f #define FUNC_SH 0 #define SET_MASK 0x7 #define SET_SH 0 @@ -894,6 +909,8 @@ static __init void build_tlb_write_entry(u32 **p, struct label **l, case CPU_20KC: case CPU_25KF: case CPU_LOONGSON2: + if (m4kc_tlbp_war()) + i_nop(p); tlbw(p); break; @@ -1705,7 +1722,8 @@ build_r4000_tlbchange_handler_head(u32 **p, struct label **l, l_smp_pgtable_change(l, *p); # endif iPTE_LW(p, l, pte, ptr); /* get even pte */ - build_tlb_probe_entry(p); + if (!m4kc_tlbp_war()) + build_tlb_probe_entry(p); } static void __init @@ -1747,6 +1765,8 @@ static void __init build_r4000_tlb_load_handler(void) build_r4000_tlbchange_handler_head(&p, &l, &r, K0, K1); build_pte_present(&p, &l, &r, K0, K1, label_nopage_tlbl); + if (m4kc_tlbp_war()) + build_tlb_probe_entry(&p); build_make_valid(&p, &r, K0, K1); build_r4000_tlbchange_handler_tail(&p, &l, &r, K0, K1); @@ -1781,6 +1801,8 @@ static void __init build_r4000_tlb_store_handler(void) build_r4000_tlbchange_handler_head(&p, &l, &r, K0, K1); build_pte_writable(&p, &l, &r, K0, K1, label_nopage_tlbs); + if (m4kc_tlbp_war()) + build_tlb_probe_entry(&p); build_make_write(&p, &r, K0, K1); build_r4000_tlbchange_handler_tail(&p, &l, &r, K0, K1); @@ -1815,6 +1837,8 @@ static void __init build_r4000_tlb_modify_handler(void) build_r4000_tlbchange_handler_head(&p, &l, &r, K0, K1); build_pte_modifiable(&p, &l, &r, K0, K1, label_nopage_tlbm); + if (m4kc_tlbp_war()) + build_tlb_probe_entry(&p); /* Present and writable bits set, set accessed and dirty bits. */ build_make_write(&p, &r, K0, K1); build_r4000_tlbchange_handler_tail(&p, &l, &r, K0, K1); |