MIPS: Add support for FTLBs
authorLeonid Yegoshin <Leonid.Yegoshin@imgtec.com>
Thu, 14 Nov 2013 16:12:31 +0000 (16:12 +0000)
committerRalf Baechle <ralf@linux-mips.org>
Wed, 22 Jan 2014 19:19:00 +0000 (20:19 +0100)
The Fixed Page Size TLB (FTLB) is a set-associative dual entry TLB. Its
purpose is to reduce the number of TLB misses by increasing the effective
TLB size and keep the implementation complexity to minimum levels.
A supported core can have both VTLB and FTLB.

Reviewed-by: James Hogan <james.hogan@imgtec.com>
Reviewed-by: Paul Burton <paul.burton@imgtec.com>
Signed-off-by: Leonid Yegoshin <Leonid.Yegoshin@imgtec.com>
Signed-off-by: Markos Chandras <markos.chandras@imgtec.com>
Signed-off-by: John Crispin <blogic@openwrt.org>
Patchwork: http://patchwork.linux-mips.org/patch/6139/

arch/mips/include/asm/cpu-info.h
arch/mips/include/asm/mipsregs.h
arch/mips/include/asm/page.h
arch/mips/kernel/cpu-probe.c
arch/mips/kernel/genex.S
arch/mips/kernel/traps.c
arch/mips/mm/tlb-r4k.c

index 21c8e29c8f91e87486f90d3aaa7fbe79220add5b..8f7adf0ac1e383539e742df12133bfc3c0fdbda4 100644 (file)
@@ -52,6 +52,9 @@ struct cpuinfo_mips {
        unsigned int            cputype;
        int                     isa_level;
        int                     tlbsize;
+       int                     tlbsizevtlb;
+       int                     tlbsizeftlbsets;
+       int                     tlbsizeftlbways;
        struct cache_desc       icache; /* Primary I-cache */
        struct cache_desc       dcache; /* Primary D or combined I/D cache */
        struct cache_desc       scache; /* Secondary cache */
index d9910a1e754a87b1a847c5c1ad98d64d312192ea..cb57e07faa24d316bc41220652109f996c35e35f 100644 (file)
 #define MIPS_CONF5_K           (_ULCAST_(1) << 30)
 
 #define MIPS_CONF6_SYND                (_ULCAST_(1) << 13)
+/* proAptiv FTLB on/off bit */
+#define MIPS_CONF6_FTLBEN      (_ULCAST_(1) << 15)
 
 #define MIPS_CONF7_WII         (_ULCAST_(1) << 31)
 
index f6be4741f7e839d30d1c7d6b30ab666db4221350..5e08bcc74897ab35c377c2d3b4cca9e7a9829824 100644 (file)
@@ -11,6 +11,8 @@
 
 #include <spaces.h>
 #include <linux/const.h>
+#include <linux/kernel.h>
+#include <asm/mipsregs.h>
 
 /*
  * PAGE_SHIFT determines the page size
 #define PAGE_SIZE      (_AC(1,UL) << PAGE_SHIFT)
 #define PAGE_MASK      (~((1 << PAGE_SHIFT) - 1))
 
+/*
+ * This is used for calculating the real page sizes
+ * for FTLB or VTLB + FTLB confugrations.
+ */
+static inline unsigned int page_size_ftlb(unsigned int mmuextdef)
+{
+       switch (mmuextdef) {
+       case MIPS_CONF4_MMUEXTDEF_FTLBSIZEEXT:
+               if (PAGE_SIZE == (1 << 30))
+                       return 5;
+               if (PAGE_SIZE == (1llu << 32))
+                       return 6;
+               if (PAGE_SIZE > (256 << 10))
+                       return 7; /* reserved */
+                       /* fall through */
+       case MIPS_CONF4_MMUEXTDEF_VTLBSIZEEXT:
+               return (PAGE_SHIFT - 10) / 2;
+       default:
+               panic("Invalid FTLB configuration with Conf4_mmuextdef=%d value\n",
+                     mmuextdef >> 14);
+       }
+}
+
 #ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT
 #define HPAGE_SHIFT    (PAGE_SHIFT + PAGE_SHIFT - 3)
 #define HPAGE_SIZE     (_AC(1,UL) << HPAGE_SHIFT)
index f86414ebe05ed163039a34fe7eaa3bd3b9e2ee7b..65b61bb8882db084a6bab1b1be15ae80f1c72636 100644 (file)
@@ -163,6 +163,25 @@ static void set_isa(struct cpuinfo_mips *c, unsigned int isa)
 static char unknown_isa[] = KERN_ERR \
        "Unsupported ISA type, c0.config0: %d.";
 
+static void set_ftlb_enable(struct cpuinfo_mips *c, int enable)
+{
+       unsigned int config6;
+       /*
+        * Config6 is implementation dependent and it's currently only
+        * used by proAptiv
+        */
+       if (c->cputype == CPU_PROAPTIV) {
+               config6 = read_c0_config6();
+               if (enable)
+                       /* Enable FTLB */
+                       write_c0_config6(config6 | MIPS_CONF6_FTLBEN);
+               else
+                       /* Disable FTLB */
+                       write_c0_config6(config6 &  ~MIPS_CONF6_FTLBEN);
+               back_to_back_c0_hazard();
+       }
+}
+
 static inline unsigned int decode_config0(struct cpuinfo_mips *c)
 {
        unsigned int config0;
@@ -170,8 +189,13 @@ static inline unsigned int decode_config0(struct cpuinfo_mips *c)
 
        config0 = read_c0_config();
 
-       if (((config0 & MIPS_CONF_MT) >> 7) == 1)
+       /*
+        * Look for Standard TLB or Dual VTLB and FTLB
+        */
+       if ((((config0 & MIPS_CONF_MT) >> 7) == 1) ||
+           (((config0 & MIPS_CONF_MT) >> 7) == 4))
                c->options |= MIPS_CPU_TLB;
+
        isa = (config0 & MIPS_CONF_AT) >> 13;
        switch (isa) {
        case 0:
@@ -226,8 +250,11 @@ static inline unsigned int decode_config1(struct cpuinfo_mips *c)
                c->options |= MIPS_CPU_FPU;
                c->options |= MIPS_CPU_32FPR;
        }
-       if (cpu_has_tlb)
+       if (cpu_has_tlb) {
                c->tlbsize = ((config1 & MIPS_CONF1_TLBS) >> 25) + 1;
+               c->tlbsizevtlb = c->tlbsize;
+               c->tlbsizeftlbsets = 0;
+       }
 
        return config1 & MIPS_CONF_M;
 }
@@ -281,16 +308,50 @@ static inline unsigned int decode_config3(struct cpuinfo_mips *c)
 static inline unsigned int decode_config4(struct cpuinfo_mips *c)
 {
        unsigned int config4;
+       unsigned int newcf4;
+       unsigned int mmuextdef;
+       unsigned int ftlb_page = MIPS_CONF4_FTLBPAGESIZE;
 
        config4 = read_c0_config4();
 
-       if ((config4 & MIPS_CONF4_MMUEXTDEF) == MIPS_CONF4_MMUEXTDEF_MMUSIZEEXT
-           && cpu_has_tlb)
-               c->tlbsize += (config4 & MIPS_CONF4_MMUSIZEEXT) * 0x40;
-
        if (cpu_has_tlb) {
                if (((config4 & MIPS_CONF4_IE) >> 29) == 2)
                        c->options |= MIPS_CPU_TLBINV;
+               mmuextdef = config4 & MIPS_CONF4_MMUEXTDEF;
+               switch (mmuextdef) {
+               case MIPS_CONF4_MMUEXTDEF_MMUSIZEEXT:
+                       c->tlbsize += (config4 & MIPS_CONF4_MMUSIZEEXT) * 0x40;
+                       c->tlbsizevtlb = c->tlbsize;
+                       break;
+               case MIPS_CONF4_MMUEXTDEF_VTLBSIZEEXT:
+                       c->tlbsizevtlb +=
+                               ((config4 & MIPS_CONF4_VTLBSIZEEXT) >>
+                                 MIPS_CONF4_VTLBSIZEEXT_SHIFT) * 0x40;
+                       c->tlbsize = c->tlbsizevtlb;
+                       ftlb_page = MIPS_CONF4_VFTLBPAGESIZE;
+                       /* fall through */
+               case MIPS_CONF4_MMUEXTDEF_FTLBSIZEEXT:
+                       newcf4 = (config4 & ~ftlb_page) |
+                               (page_size_ftlb(mmuextdef) <<
+                                MIPS_CONF4_FTLBPAGESIZE_SHIFT);
+                       write_c0_config4(newcf4);
+                       back_to_back_c0_hazard();
+                       config4 = read_c0_config4();
+                       if (config4 != newcf4) {
+                               pr_err("PAGE_SIZE 0x%lx is not supported by FTLB (config4=0x%x)\n",
+                                      PAGE_SIZE, config4);
+                               /* Switch FTLB off */
+                               set_ftlb_enable(c, 0);
+                               break;
+                       }
+                       c->tlbsizeftlbsets = 1 <<
+                               ((config4 & MIPS_CONF4_FTLBSETS) >>
+                                MIPS_CONF4_FTLBSETS_SHIFT);
+                       c->tlbsizeftlbways = ((config4 & MIPS_CONF4_FTLBWAYS) >>
+                                             MIPS_CONF4_FTLBWAYS_SHIFT) + 2;
+                       c->tlbsize += c->tlbsizeftlbways * c->tlbsizeftlbsets;
+                       break;
+               }
        }
 
        c->kscratch_mask = (config4 >> 16) & 0xff;
@@ -319,6 +380,9 @@ static void decode_configs(struct cpuinfo_mips *c)
 
        c->scache.flags = MIPS_CACHE_NOT_PRESENT;
 
+       /* Enable FTLB if present */
+       set_ftlb_enable(c, 1);
+
        ok = decode_config0(c);                 /* Read Config registers.  */
        BUG_ON(!ok);                            /* Arch spec violation!  */
        if (ok)
@@ -682,7 +746,6 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
 
 static inline void cpu_probe_mips(struct cpuinfo_mips *c, unsigned int cpu)
 {
-       decode_configs(c);
        switch (c->processor_id & PRID_IMP_MASK) {
        case PRID_IMP_4KC:
                c->cputype = CPU_4KC;
@@ -756,6 +819,8 @@ static inline void cpu_probe_mips(struct cpuinfo_mips *c, unsigned int cpu)
                break;
        }
 
+       decode_configs(c);
+
        spram_config();
 }
 
index 47d7583cd67ff7de77d5010c89d8bcbba234c2d0..d84f6a5095023ea5ff1bd052822937b8f87ac5dc 100644 (file)
@@ -476,6 +476,7 @@ NESTED(nmi_handler, PT_SIZE, sp)
        BUILD_HANDLER ov ov sti silent                  /* #12 */
        BUILD_HANDLER tr tr sti silent                  /* #13 */
        BUILD_HANDLER fpe fpe fpe silent                /* #15 */
+       BUILD_HANDLER ftlb ftlb none silent             /* #16 */
        BUILD_HANDLER mdmx mdmx sti silent              /* #22 */
 #ifdef CONFIG_HARDWARE_WATCHPOINTS
        /*
index e98f3ab2a01878546b5862f8a33f8eaedcae7edb..39370e1d43621772f2bc2c64ff65a5b38dcb5128 100644 (file)
@@ -78,6 +78,7 @@ extern asmlinkage void handle_cpu(void);
 extern asmlinkage void handle_ov(void);
 extern asmlinkage void handle_tr(void);
 extern asmlinkage void handle_fpe(void);
+extern asmlinkage void handle_ftlb(void);
 extern asmlinkage void handle_mdmx(void);
 extern asmlinkage void handle_watch(void);
 extern asmlinkage void handle_mt(void);
@@ -1460,6 +1461,34 @@ asmlinkage void cache_parity_error(void)
        panic("Can't handle the cache error!");
 }
 
+asmlinkage void do_ftlb(void)
+{
+       const int field = 2 * sizeof(unsigned long);
+       unsigned int reg_val;
+
+       /* For the moment, report the problem and hang. */
+       if (cpu_has_mips_r2 &&
+           ((current_cpu_data.processor_id && 0xff0000) == PRID_COMP_MIPS)) {
+               pr_err("FTLB error exception, cp0_ecc=0x%08x:\n",
+                      read_c0_ecc());
+               pr_err("cp0_errorepc == %0*lx\n", field, read_c0_errorepc());
+               reg_val = read_c0_cacheerr();
+               pr_err("c0_cacheerr == %08x\n", reg_val);
+
+               if ((reg_val & 0xc0000000) == 0xc0000000) {
+                       pr_err("Decoded c0_cacheerr: FTLB parity error\n");
+               } else {
+                       pr_err("Decoded c0_cacheerr: %s cache fault in %s reference.\n",
+                              reg_val & (1<<30) ? "secondary" : "primary",
+                              reg_val & (1<<31) ? "data" : "insn");
+               }
+       } else {
+               pr_err("FTLB error exception\n");
+       }
+       /* Just print the cacheerr bits for now */
+       cache_parity_error();
+}
+
 /*
  * SDBBP EJTAG debug exception handler.
  * We skip the instruction and return to the next instruction.
@@ -2009,6 +2038,7 @@ void __init trap_init(void)
        if (cpu_has_fpu && !cpu_has_nofpuex)
                set_except_vector(15, handle_fpe);
 
+       set_except_vector(16, handle_ftlb);
        set_except_vector(22, handle_mdmx);
 
        if (cpu_has_mcheck)
index 427dcacca586b9931fc5d85856fc5cff23a2e5ce..ae4ca24507072f8cd3a94b02a935442091aae76b 100644 (file)
@@ -72,7 +72,7 @@ void local_flush_tlb_all(void)
 {
        unsigned long flags;
        unsigned long old_ctx;
-       int entry;
+       int entry, ftlbhighset;
 
        ENTER_CRITICAL(flags);
        /* Save old context and create impossible VPN2 value */
@@ -83,10 +83,21 @@ void local_flush_tlb_all(void)
        entry = read_c0_wired();
 
        /* Blast 'em all away. */
-       if (cpu_has_tlbinv && current_cpu_data.tlbsize) {
-               write_c0_index(0);
-               mtc0_tlbw_hazard();
-               tlbinvf();  /* invalidate VTLB */
+       if (cpu_has_tlbinv) {
+               if (current_cpu_data.tlbsizevtlb) {
+                       write_c0_index(0);
+                       mtc0_tlbw_hazard();
+                       tlbinvf();  /* invalidate VTLB */
+               }
+               ftlbhighset = current_cpu_data.tlbsizevtlb +
+                       current_cpu_data.tlbsizeftlbsets;
+               for (entry = current_cpu_data.tlbsizevtlb;
+                    entry < ftlbhighset;
+                    entry++) {
+                       write_c0_index(entry);
+                       mtc0_tlbw_hazard();
+                       tlbinvf();  /* invalidate one FTLB set */
+               }
        } else {
                while (entry < current_cpu_data.tlbsize) {
                        /* Make sure all entries differ. */
@@ -134,7 +145,9 @@ void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
                start = round_down(start, PAGE_SIZE << 1);
                end = round_up(end, PAGE_SIZE << 1);
                size = (end - start) >> (PAGE_SHIFT + 1);
-               if (size <= current_cpu_data.tlbsize/2) {
+               if (size <= (current_cpu_data.tlbsizeftlbsets ?
+                            current_cpu_data.tlbsize / 8 :
+                            current_cpu_data.tlbsize / 2)) {
                        int oldpid = read_c0_entryhi();
                        int newpid = cpu_asid(cpu, mm);
 
@@ -173,7 +186,9 @@ void local_flush_tlb_kernel_range(unsigned long start, unsigned long end)
        ENTER_CRITICAL(flags);
        size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
        size = (size + 1) >> 1;
-       if (size <= current_cpu_data.tlbsize / 2) {
+       if (size <= (current_cpu_data.tlbsizeftlbsets ?
+                    current_cpu_data.tlbsize / 8 :
+                    current_cpu_data.tlbsize / 2)) {
                int pid = read_c0_entryhi();
 
                start &= (PAGE_MASK << 1);