Skip to content

Commit

Permalink
MIPS: Add support for FTLBs
Browse files Browse the repository at this point in the history
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/
  • Loading branch information
Leonid Yegoshin authored and Ralf Baechle committed Jan 22, 2014
1 parent 601cfa7 commit 75b5b5e
Show file tree
Hide file tree
Showing 7 changed files with 155 additions and 14 deletions.
3 changes: 3 additions & 0 deletions arch/mips/include/asm/cpu-info.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
2 changes: 2 additions & 0 deletions arch/mips/include/asm/mipsregs.h
Original file line number Diff line number Diff line change
Expand Up @@ -645,6 +645,8 @@
#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)

Expand Down
25 changes: 25 additions & 0 deletions arch/mips/include/asm/page.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -33,6 +35,29 @@
#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)
Expand Down
79 changes: 72 additions & 7 deletions arch/mips/kernel/cpu-probe.c
Original file line number Diff line number Diff line change
Expand Up @@ -163,15 +163,39 @@ 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;
int isa;

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:
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -756,6 +819,8 @@ static inline void cpu_probe_mips(struct cpuinfo_mips *c, unsigned int cpu)
break;
}

decode_configs(c);

spram_config();
}

Expand Down
1 change: 1 addition & 0 deletions arch/mips/kernel/genex.S
Original file line number Diff line number Diff line change
Expand Up @@ -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
/*
Expand Down
30 changes: 30 additions & 0 deletions arch/mips/kernel/traps.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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)
Expand Down
29 changes: 22 additions & 7 deletions arch/mips/mm/tlb-r4k.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand All @@ -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. */
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit 75b5b5e

Please sign in to comment.