Merge branch 'mti-next' of git://git.linux-mips.org/pub/scm/sjhill/linux-sjhill into...
[firefly-linux-kernel-4.4.55.git] / arch / mips / mm / tlbex.c
index 3b3822afb059f7393663f0bb7aca2b0eb60629b2..4d46d37875765a3d3024bdee0d043fae9b7a23f7 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/init.h>
 #include <linux/cache.h>
 
+#include <asm/mmu_context.h>
 #include <asm/cacheflush.h>
 #include <asm/pgtable.h>
 #include <asm/war.h>
@@ -305,6 +306,78 @@ static struct uasm_reloc relocs[128] __cpuinitdata;
 static int check_for_high_segbits __cpuinitdata;
 #endif
 
+static void __cpuinit insn_fixup(unsigned int **start, unsigned int **stop,
+                                       unsigned int i_const)
+{
+       unsigned int **p;
+
+       for (p = start; p < stop; p++) {
+#ifndef CONFIG_CPU_MICROMIPS
+               unsigned int *ip;
+
+               ip = *p;
+               *ip = (*ip & 0xffff0000) | i_const;
+#else
+               unsigned short *ip;
+
+               ip = ((unsigned short *)((unsigned int)*p - 1));
+               if ((*ip & 0xf000) == 0x4000) {
+                       *ip &= 0xfff1;
+                       *ip |= (i_const << 1);
+               } else if ((*ip & 0xf000) == 0x6000) {
+                       *ip &= 0xfff1;
+                       *ip |= ((i_const >> 2) << 1);
+               } else {
+                       ip++;
+                       *ip = i_const;
+               }
+#endif
+               local_flush_icache_range((unsigned long)ip,
+                                        (unsigned long)ip + sizeof(*ip));
+       }
+}
+
+#define asid_insn_fixup(section, const)                                        \
+do {                                                                   \
+       extern unsigned int *__start_ ## section;                       \
+       extern unsigned int *__stop_ ## section;                        \
+       insn_fixup(&__start_ ## section, &__stop_ ## section, const);   \
+} while(0)
+
+/*
+ * Caller is assumed to flush the caches before the first context switch.
+ */
+static void __cpuinit setup_asid(unsigned int inc, unsigned int mask,
+                                unsigned int version_mask,
+                                unsigned int first_version)
+{
+       extern asmlinkage void handle_ri_rdhwr_vivt(void);
+       unsigned long *vivt_exc;
+
+#ifdef CONFIG_CPU_MICROMIPS
+       /*
+        * Worst case optimised microMIPS addiu instructions support
+        * only a 3-bit immediate value.
+        */
+       if(inc > 7)
+               panic("Invalid ASID increment value!");
+#endif
+       asid_insn_fixup(__asid_inc, inc);
+       asid_insn_fixup(__asid_mask, mask);
+       asid_insn_fixup(__asid_version_mask, version_mask);
+       asid_insn_fixup(__asid_first_version, first_version);
+
+       /* Patch up the 'handle_ri_rdhwr_vivt' handler. */
+       vivt_exc = (unsigned long *) &handle_ri_rdhwr_vivt;
+#ifdef CONFIG_CPU_MICROMIPS
+       vivt_exc = (unsigned long *)((unsigned long) vivt_exc - 1);
+#endif
+       vivt_exc++;
+       *vivt_exc = (*vivt_exc & ~mask) | mask;
+
+       current_cpu_data.asid_cache = first_version;
+}
+
 static int check_for_high_segbits __cpuinitdata;
 
 static unsigned int kscratch_used_mask __cpuinitdata;
@@ -2030,6 +2103,13 @@ static void __cpuinit build_r4000_tlb_load_handler(void)
 
        uasm_l_nopage_tlbl(&l, p);
        build_restore_work_registers(&p);
+#ifdef CONFIG_CPU_MICROMIPS
+       if ((unsigned long)tlb_do_page_fault_0 & 1) {
+               uasm_i_lui(&p, K0, uasm_rel_hi((long)tlb_do_page_fault_0));
+               uasm_i_addiu(&p, K0, K0, uasm_rel_lo((long)tlb_do_page_fault_0));
+               uasm_i_jr(&p, K0);
+       } else
+#endif
        uasm_i_j(&p, (unsigned long)tlb_do_page_fault_0 & 0x0fffffff);
        uasm_i_nop(&p);
 
@@ -2077,6 +2157,13 @@ static void __cpuinit build_r4000_tlb_store_handler(void)
 
        uasm_l_nopage_tlbs(&l, p);
        build_restore_work_registers(&p);
+#ifdef CONFIG_CPU_MICROMIPS
+       if ((unsigned long)tlb_do_page_fault_1 & 1) {
+               uasm_i_lui(&p, K0, uasm_rel_hi((long)tlb_do_page_fault_1));
+               uasm_i_addiu(&p, K0, K0, uasm_rel_lo((long)tlb_do_page_fault_1));
+               uasm_i_jr(&p, K0);
+       } else
+#endif
        uasm_i_j(&p, (unsigned long)tlb_do_page_fault_1 & 0x0fffffff);
        uasm_i_nop(&p);
 
@@ -2125,6 +2212,13 @@ static void __cpuinit build_r4000_tlb_modify_handler(void)
 
        uasm_l_nopage_tlbm(&l, p);
        build_restore_work_registers(&p);
+#ifdef CONFIG_CPU_MICROMIPS
+       if ((unsigned long)tlb_do_page_fault_1 & 1) {
+               uasm_i_lui(&p, K0, uasm_rel_hi((long)tlb_do_page_fault_1));
+               uasm_i_addiu(&p, K0, K0, uasm_rel_lo((long)tlb_do_page_fault_1));
+               uasm_i_jr(&p, K0);
+       } else
+#endif
        uasm_i_j(&p, (unsigned long)tlb_do_page_fault_1 & 0x0fffffff);
        uasm_i_nop(&p);
 
@@ -2162,6 +2256,7 @@ void __cpuinit build_tlb_refill_handler(void)
        case CPU_TX3922:
        case CPU_TX3927:
 #ifndef CONFIG_MIPS_PGD_C0_CONTEXT
+               setup_asid(0x40, 0xfc0, 0xf000, ASID_FIRST_VERSION_R3000);
                if (cpu_has_local_ebase)
                        build_r3000_tlb_refill_handler();
                if (!run_once) {
@@ -2187,6 +2282,11 @@ void __cpuinit build_tlb_refill_handler(void)
                break;
 
        default:
+#ifndef CONFIG_MIPS_MT_SMTC
+               setup_asid(0x1, 0xff, 0xff00, ASID_FIRST_VERSION_R4000);
+#else
+               setup_asid(0x1, smtc_asid_mask, 0xff00, ASID_FIRST_VERSION_R4000);
+#endif
                if (!run_once) {
                        scratch_reg = allocate_kscratch();
 #ifdef CONFIG_MIPS_PGD_C0_CONTEXT