Add support for extended ASIDs as determined by the Config4.AE bit.
Since the only supported CPUs known to implement this are Netlogic XLP
and MIPS I6400, select this variable ASID support based upon
CONFIG_CPU_XLP and CONFIG_CPU_MIPSR6.
Signed-off-by: Paul Burton <paul.burton@imgtec.com>
Signed-off-by: James Hogan <james.hogan@imgtec.com>
Cc: Jayachandran C. <jchandra@broadcom.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: Radim Krčmář <rkrcmar@redhat.com>
Cc: linux-mips@linux-mips.org
Cc: kvm@vger.kernel.org
Patchwork: https://patchwork.linux-mips.org/patch/13211/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
        select CPU_HAS_PREFETCH
        select CPU_MIPSR2
        select CPU_SUPPORTS_HUGEPAGES
+       select MIPS_ASID_BITS_VARIABLE
        help
          Netlogic Microsystems XLP processors.
 endchoice
        bool
        default y if CPU_MIPS32_R6 || CPU_MIPS64_R6
        select HAVE_ARCH_BITREVERSE
+       select MIPS_ASID_BITS_VARIABLE
        select MIPS_SPRAM
 
 config EVA
 
 config MIPS_ASID_BITS
        int
+       default 0 if MIPS_ASID_BITS_VARIABLE
        default 6 if CPU_R3000 || CPU_TX39XX
        default 8
 
+config MIPS_ASID_BITS_VARIABLE
+       bool
+
 #
 # - Highmem only makes sense for the 32-bit kernel.
 # - The current highmem code will only work properly on physically indexed
 
 
 struct cpuinfo_mips {
        unsigned long           asid_cache;
+#ifdef CONFIG_MIPS_ASID_BITS_VARIABLE
+       unsigned long           asid_mask;
+#endif
 
        /*
         * Capability and feature descriptor structure for MIPS CPU
 
 static inline unsigned long cpu_asid_mask(struct cpuinfo_mips *cpuinfo)
 {
+#ifdef CONFIG_MIPS_ASID_BITS_VARIABLE
+       return cpuinfo->asid_mask;
+#endif
        return ((1 << CONFIG_MIPS_ASID_BITS) - 1) << CONFIG_MIPS_ASID_SHIFT;
 }
 
+static inline void set_cpu_asid_mask(struct cpuinfo_mips *cpuinfo,
+                                    unsigned long asid_mask)
+{
+#ifdef CONFIG_MIPS_ASID_BITS_VARIABLE
+       cpuinfo->asid_mask = asid_mask;
+#endif
+}
+
 #endif /* __ASM_CPU_INFO_H */
 
 #include <linux/mm.h>
 #include <linux/kbuild.h>
 #include <linux/suspend.h>
+#include <asm/cpu-info.h>
 #include <asm/pm.h>
 #include <asm/ptrace.h>
 #include <asm/processor.h>
 }
 #endif
 
+void output_cpuinfo_defines(void)
+{
+       COMMENT(" MIPS cpuinfo offsets. ");
+       DEFINE(CPUINFO_SIZE, sizeof(struct cpuinfo_mips));
+#ifdef CONFIG_MIPS_ASID_BITS_VARIABLE
+       OFFSET(CPUINFO_ASID_MASK, cpuinfo_mips, asid_mask);
+#endif
+}
+
 void output_kvm_defines(void)
 {
        COMMENT(" KVM/MIPS Specfic offsets. ");
 
        unsigned int newcf4;
        unsigned int mmuextdef;
        unsigned int ftlb_page = MIPS_CONF4_FTLBPAGESIZE;
+       unsigned long asid_mask;
 
        config4 = read_c0_config4();
 
 
        c->kscratch_mask = (config4 >> 16) & 0xff;
 
+       asid_mask = MIPS_ENTRYHI_ASID;
+       if (config4 & MIPS_CONF4_AE)
+               asid_mask |= MIPS_ENTRYHI_ASIDX;
+       set_cpu_asid_mask(c, asid_mask);
+
+       /*
+        * Warn if the computed ASID mask doesn't match the mask the kernel
+        * is built for. This may indicate either a serious problem or an
+        * easy optimisation opportunity, but either way should be addressed.
+        */
+       WARN_ON(asid_mask != cpu_asid_mask(c));
+
        return config4 & MIPS_CONF_M;
 }
 
 
        .set    noreorder
        /* check if TLB contains a entry for EPC */
        MFC0    k1, CP0_ENTRYHI
-       andi    k1, MIPS_ENTRYHI_ASID
+       andi    k1, MIPS_ENTRYHI_ASID | MIPS_ENTRYHI_ASIDX
        MFC0    k0, CP0_EPC
        PTR_SRL k0, _PAGE_SHIFT + 1
        PTR_SLL k0, _PAGE_SHIFT + 1
 
        INT_SLL t2, t2, 2                   /* x4 */
        REG_ADDU t3, t1, t2
        LONG_L  k0, (t3)
+#ifdef CONFIG_MIPS_ASID_BITS_VARIABLE
+       li      t3, CPUINFO_SIZE/4
+       mul     t2, t2, t3              /* x sizeof(struct cpuinfo_mips)/4 */
+       LONG_L  t2, (cpu_data + CPUINFO_ASID_MASK)(t2)
+       and     k0, k0, t2
+#else
        andi    k0, k0, MIPS_ENTRYHI_ASID
+#endif
        mtc0    k0, CP0_ENTRYHI
        ehb
 
        INT_SLL t2, t2, 2               /* x4 */
        REG_ADDU t3, t1, t2
        LONG_L  k0, (t3)
+#ifdef CONFIG_MIPS_ASID_BITS_VARIABLE
+       li      t3, CPUINFO_SIZE/4
+       mul     t2, t2, t3              /* x sizeof(struct cpuinfo_mips)/4 */
+       LONG_L  t2, (cpu_data + CPUINFO_ASID_MASK)(t2)
+       and     k0, k0, t2
+#else
        andi    k0, k0, MIPS_ENTRYHI_ASID
+#endif
        mtc0    k0, CP0_ENTRYHI
        ehb