MIPS: Add support for FTLBs
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/
This commit is contained in:
parent
601cfa7b6f
commit
75b5b5e0a2
@ -52,6 +52,9 @@ struct cpuinfo_mips {
|
|||||||
unsigned int cputype;
|
unsigned int cputype;
|
||||||
int isa_level;
|
int isa_level;
|
||||||
int tlbsize;
|
int tlbsize;
|
||||||
|
int tlbsizevtlb;
|
||||||
|
int tlbsizeftlbsets;
|
||||||
|
int tlbsizeftlbways;
|
||||||
struct cache_desc icache; /* Primary I-cache */
|
struct cache_desc icache; /* Primary I-cache */
|
||||||
struct cache_desc dcache; /* Primary D or combined I/D cache */
|
struct cache_desc dcache; /* Primary D or combined I/D cache */
|
||||||
struct cache_desc scache; /* Secondary cache */
|
struct cache_desc scache; /* Secondary cache */
|
||||||
|
@ -645,6 +645,8 @@
|
|||||||
#define MIPS_CONF5_K (_ULCAST_(1) << 30)
|
#define MIPS_CONF5_K (_ULCAST_(1) << 30)
|
||||||
|
|
||||||
#define MIPS_CONF6_SYND (_ULCAST_(1) << 13)
|
#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)
|
#define MIPS_CONF7_WII (_ULCAST_(1) << 31)
|
||||||
|
|
||||||
|
@ -11,6 +11,8 @@
|
|||||||
|
|
||||||
#include <spaces.h>
|
#include <spaces.h>
|
||||||
#include <linux/const.h>
|
#include <linux/const.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <asm/mipsregs.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* PAGE_SHIFT determines the page size
|
* PAGE_SHIFT determines the page size
|
||||||
@ -33,6 +35,29 @@
|
|||||||
#define PAGE_SIZE (_AC(1,UL) << PAGE_SHIFT)
|
#define PAGE_SIZE (_AC(1,UL) << PAGE_SHIFT)
|
||||||
#define PAGE_MASK (~((1 << PAGE_SHIFT) - 1))
|
#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
|
#ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT
|
||||||
#define HPAGE_SHIFT (PAGE_SHIFT + PAGE_SHIFT - 3)
|
#define HPAGE_SHIFT (PAGE_SHIFT + PAGE_SHIFT - 3)
|
||||||
#define HPAGE_SIZE (_AC(1,UL) << HPAGE_SHIFT)
|
#define HPAGE_SIZE (_AC(1,UL) << HPAGE_SHIFT)
|
||||||
|
@ -163,6 +163,25 @@ static void set_isa(struct cpuinfo_mips *c, unsigned int isa)
|
|||||||
static char unknown_isa[] = KERN_ERR \
|
static char unknown_isa[] = KERN_ERR \
|
||||||
"Unsupported ISA type, c0.config0: %d.";
|
"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)
|
static inline unsigned int decode_config0(struct cpuinfo_mips *c)
|
||||||
{
|
{
|
||||||
unsigned int config0;
|
unsigned int config0;
|
||||||
@ -170,8 +189,13 @@ static inline unsigned int decode_config0(struct cpuinfo_mips *c)
|
|||||||
|
|
||||||
config0 = read_c0_config();
|
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;
|
c->options |= MIPS_CPU_TLB;
|
||||||
|
|
||||||
isa = (config0 & MIPS_CONF_AT) >> 13;
|
isa = (config0 & MIPS_CONF_AT) >> 13;
|
||||||
switch (isa) {
|
switch (isa) {
|
||||||
case 0:
|
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_FPU;
|
||||||
c->options |= MIPS_CPU_32FPR;
|
c->options |= MIPS_CPU_32FPR;
|
||||||
}
|
}
|
||||||
if (cpu_has_tlb)
|
if (cpu_has_tlb) {
|
||||||
c->tlbsize = ((config1 & MIPS_CONF1_TLBS) >> 25) + 1;
|
c->tlbsize = ((config1 & MIPS_CONF1_TLBS) >> 25) + 1;
|
||||||
|
c->tlbsizevtlb = c->tlbsize;
|
||||||
|
c->tlbsizeftlbsets = 0;
|
||||||
|
}
|
||||||
|
|
||||||
return config1 & MIPS_CONF_M;
|
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)
|
static inline unsigned int decode_config4(struct cpuinfo_mips *c)
|
||||||
{
|
{
|
||||||
unsigned int config4;
|
unsigned int config4;
|
||||||
|
unsigned int newcf4;
|
||||||
|
unsigned int mmuextdef;
|
||||||
|
unsigned int ftlb_page = MIPS_CONF4_FTLBPAGESIZE;
|
||||||
|
|
||||||
config4 = read_c0_config4();
|
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 (cpu_has_tlb) {
|
||||||
if (((config4 & MIPS_CONF4_IE) >> 29) == 2)
|
if (((config4 & MIPS_CONF4_IE) >> 29) == 2)
|
||||||
c->options |= MIPS_CPU_TLBINV;
|
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;
|
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;
|
c->scache.flags = MIPS_CACHE_NOT_PRESENT;
|
||||||
|
|
||||||
|
/* Enable FTLB if present */
|
||||||
|
set_ftlb_enable(c, 1);
|
||||||
|
|
||||||
ok = decode_config0(c); /* Read Config registers. */
|
ok = decode_config0(c); /* Read Config registers. */
|
||||||
BUG_ON(!ok); /* Arch spec violation! */
|
BUG_ON(!ok); /* Arch spec violation! */
|
||||||
if (ok)
|
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)
|
static inline void cpu_probe_mips(struct cpuinfo_mips *c, unsigned int cpu)
|
||||||
{
|
{
|
||||||
decode_configs(c);
|
|
||||||
switch (c->processor_id & PRID_IMP_MASK) {
|
switch (c->processor_id & PRID_IMP_MASK) {
|
||||||
case PRID_IMP_4KC:
|
case PRID_IMP_4KC:
|
||||||
c->cputype = CPU_4KC;
|
c->cputype = CPU_4KC;
|
||||||
@ -756,6 +819,8 @@ static inline void cpu_probe_mips(struct cpuinfo_mips *c, unsigned int cpu)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
decode_configs(c);
|
||||||
|
|
||||||
spram_config();
|
spram_config();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -476,6 +476,7 @@ NESTED(nmi_handler, PT_SIZE, sp)
|
|||||||
BUILD_HANDLER ov ov sti silent /* #12 */
|
BUILD_HANDLER ov ov sti silent /* #12 */
|
||||||
BUILD_HANDLER tr tr sti silent /* #13 */
|
BUILD_HANDLER tr tr sti silent /* #13 */
|
||||||
BUILD_HANDLER fpe fpe fpe silent /* #15 */
|
BUILD_HANDLER fpe fpe fpe silent /* #15 */
|
||||||
|
BUILD_HANDLER ftlb ftlb none silent /* #16 */
|
||||||
BUILD_HANDLER mdmx mdmx sti silent /* #22 */
|
BUILD_HANDLER mdmx mdmx sti silent /* #22 */
|
||||||
#ifdef CONFIG_HARDWARE_WATCHPOINTS
|
#ifdef CONFIG_HARDWARE_WATCHPOINTS
|
||||||
/*
|
/*
|
||||||
|
@ -78,6 +78,7 @@ extern asmlinkage void handle_cpu(void);
|
|||||||
extern asmlinkage void handle_ov(void);
|
extern asmlinkage void handle_ov(void);
|
||||||
extern asmlinkage void handle_tr(void);
|
extern asmlinkage void handle_tr(void);
|
||||||
extern asmlinkage void handle_fpe(void);
|
extern asmlinkage void handle_fpe(void);
|
||||||
|
extern asmlinkage void handle_ftlb(void);
|
||||||
extern asmlinkage void handle_mdmx(void);
|
extern asmlinkage void handle_mdmx(void);
|
||||||
extern asmlinkage void handle_watch(void);
|
extern asmlinkage void handle_watch(void);
|
||||||
extern asmlinkage void handle_mt(void);
|
extern asmlinkage void handle_mt(void);
|
||||||
@ -1460,6 +1461,34 @@ asmlinkage void cache_parity_error(void)
|
|||||||
panic("Can't handle the cache error!");
|
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.
|
* SDBBP EJTAG debug exception handler.
|
||||||
* We skip the instruction and return to the next instruction.
|
* 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)
|
if (cpu_has_fpu && !cpu_has_nofpuex)
|
||||||
set_except_vector(15, handle_fpe);
|
set_except_vector(15, handle_fpe);
|
||||||
|
|
||||||
|
set_except_vector(16, handle_ftlb);
|
||||||
set_except_vector(22, handle_mdmx);
|
set_except_vector(22, handle_mdmx);
|
||||||
|
|
||||||
if (cpu_has_mcheck)
|
if (cpu_has_mcheck)
|
||||||
|
@ -72,7 +72,7 @@ void local_flush_tlb_all(void)
|
|||||||
{
|
{
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
unsigned long old_ctx;
|
unsigned long old_ctx;
|
||||||
int entry;
|
int entry, ftlbhighset;
|
||||||
|
|
||||||
ENTER_CRITICAL(flags);
|
ENTER_CRITICAL(flags);
|
||||||
/* Save old context and create impossible VPN2 value */
|
/* Save old context and create impossible VPN2 value */
|
||||||
@ -83,10 +83,21 @@ void local_flush_tlb_all(void)
|
|||||||
entry = read_c0_wired();
|
entry = read_c0_wired();
|
||||||
|
|
||||||
/* Blast 'em all away. */
|
/* Blast 'em all away. */
|
||||||
if (cpu_has_tlbinv && current_cpu_data.tlbsize) {
|
if (cpu_has_tlbinv) {
|
||||||
|
if (current_cpu_data.tlbsizevtlb) {
|
||||||
write_c0_index(0);
|
write_c0_index(0);
|
||||||
mtc0_tlbw_hazard();
|
mtc0_tlbw_hazard();
|
||||||
tlbinvf(); /* invalidate VTLB */
|
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 {
|
} else {
|
||||||
while (entry < current_cpu_data.tlbsize) {
|
while (entry < current_cpu_data.tlbsize) {
|
||||||
/* Make sure all entries differ. */
|
/* 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);
|
start = round_down(start, PAGE_SIZE << 1);
|
||||||
end = round_up(end, PAGE_SIZE << 1);
|
end = round_up(end, PAGE_SIZE << 1);
|
||||||
size = (end - start) >> (PAGE_SHIFT + 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 oldpid = read_c0_entryhi();
|
||||||
int newpid = cpu_asid(cpu, mm);
|
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);
|
ENTER_CRITICAL(flags);
|
||||||
size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
|
size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
|
||||||
size = (size + 1) >> 1;
|
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();
|
int pid = read_c0_entryhi();
|
||||||
|
|
||||||
start &= (PAGE_MASK << 1);
|
start &= (PAGE_MASK << 1);
|
||||||
|
Loading…
Reference in New Issue
Block a user