diff options
Diffstat (limited to 'arch/mips/mm/tlbex.c')
| -rw-r--r-- | arch/mips/mm/tlbex.c | 98 | 
1 files changed, 96 insertions, 2 deletions
diff --git a/arch/mips/mm/tlbex.c b/arch/mips/mm/tlbex.c index e80e10bafc83..a08dd53a1cc5 100644 --- a/arch/mips/mm/tlbex.c +++ b/arch/mips/mm/tlbex.c @@ -429,6 +429,7 @@ static void build_r3000_tlb_refill_handler(void)  		 (unsigned int)(p - tlb_handler));  	memcpy((void *)ebase, tlb_handler, 0x80); +	local_flush_icache_range(ebase, ebase + 0x80);  	dump_handler("r3000_tlb_refill", (u32 *)ebase, 32);  } @@ -1299,6 +1300,7 @@ static void build_r4000_tlb_refill_handler(void)  	}  #ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT  	uasm_l_tlb_huge_update(&l, p); +	UASM_i_LW(&p, K0, 0, K1);  	build_huge_update_entries(&p, htlb_info.huge_pte, K1);  	build_huge_tlb_write_entry(&p, &l, &r, K0, tlb_random,  				   htlb_info.restore_scratch); @@ -1415,6 +1417,7 @@ static void build_r4000_tlb_refill_handler(void)  		 final_len);  	memcpy((void *)ebase, final_handler, 0x100); +	local_flush_icache_range(ebase, ebase + 0x100);  	dump_handler("r4000_tlb_refill", (u32 *)ebase, 64);  } @@ -1919,7 +1922,7 @@ static void build_r4000_tlb_load_handler(void)  	if (m4kc_tlbp_war())  		build_tlb_probe_entry(&p); -	if (cpu_has_rixi) { +	if (cpu_has_rixi && !cpu_has_rixiex) {  		/*  		 * If the page is not _PAGE_VALID, RI or XI could not  		 * have triggered it.  Skip the expensive test.. @@ -1986,7 +1989,7 @@ static void build_r4000_tlb_load_handler(void)  	build_pte_present(&p, &r, wr.r1, wr.r2, wr.r3, label_nopage_tlbl);  	build_tlb_probe_entry(&p); -	if (cpu_has_rixi) { +	if (cpu_has_rixi && !cpu_has_rixiex) {  		/*  		 * If the page is not _PAGE_VALID, RI or XI could not  		 * have triggered it.  Skip the expensive test.. @@ -2194,6 +2197,94 @@ static void flush_tlb_handlers(void)  			   (unsigned long)tlbmiss_handler_setup_pgd_end);  } +static void print_htw_config(void) +{ +	unsigned long config; +	unsigned int pwctl; +	const int field = 2 * sizeof(unsigned long); + +	config = read_c0_pwfield(); +	pr_debug("PWField (0x%0*lx): GDI: 0x%02lx  UDI: 0x%02lx  MDI: 0x%02lx  PTI: 0x%02lx  PTEI: 0x%02lx\n", +		field, config, +		(config & MIPS_PWFIELD_GDI_MASK) >> MIPS_PWFIELD_GDI_SHIFT, +		(config & MIPS_PWFIELD_UDI_MASK) >> MIPS_PWFIELD_UDI_SHIFT, +		(config & MIPS_PWFIELD_MDI_MASK) >> MIPS_PWFIELD_MDI_SHIFT, +		(config & MIPS_PWFIELD_PTI_MASK) >> MIPS_PWFIELD_PTI_SHIFT, +		(config & MIPS_PWFIELD_PTEI_MASK) >> MIPS_PWFIELD_PTEI_SHIFT); + +	config = read_c0_pwsize(); +	pr_debug("PWSize  (0x%0*lx): GDW: 0x%02lx  UDW: 0x%02lx  MDW: 0x%02lx  PTW: 0x%02lx  PTEW: 0x%02lx\n", +		field, config, +		(config & MIPS_PWSIZE_GDW_MASK) >> MIPS_PWSIZE_GDW_SHIFT, +		(config & MIPS_PWSIZE_UDW_MASK) >> MIPS_PWSIZE_UDW_SHIFT, +		(config & MIPS_PWSIZE_MDW_MASK) >> MIPS_PWSIZE_MDW_SHIFT, +		(config & MIPS_PWSIZE_PTW_MASK) >> MIPS_PWSIZE_PTW_SHIFT, +		(config & MIPS_PWSIZE_PTEW_MASK) >> MIPS_PWSIZE_PTEW_SHIFT); + +	pwctl = read_c0_pwctl(); +	pr_debug("PWCtl   (0x%x): PWEn: 0x%x  DPH: 0x%x  HugePg: 0x%x  Psn: 0x%x\n", +		pwctl, +		(pwctl & MIPS_PWCTL_PWEN_MASK) >> MIPS_PWCTL_PWEN_SHIFT, +		(pwctl & MIPS_PWCTL_DPH_MASK) >> MIPS_PWCTL_DPH_SHIFT, +		(pwctl & MIPS_PWCTL_HUGEPG_MASK) >> MIPS_PWCTL_HUGEPG_SHIFT, +		(pwctl & MIPS_PWCTL_PSN_MASK) >> MIPS_PWCTL_PSN_SHIFT); +} + +static void config_htw_params(void) +{ +	unsigned long pwfield, pwsize, ptei; +	unsigned int config; + +	/* +	 * We are using 2-level page tables, so we only need to +	 * setup GDW and PTW appropriately. UDW and MDW will remain 0. +	 * The default value of GDI/UDI/MDI/PTI is 0xc. It is illegal to +	 * write values less than 0xc in these fields because the entire +	 * write will be dropped. As a result of which, we must preserve +	 * the original reset values and overwrite only what we really want. +	 */ + +	pwfield = read_c0_pwfield(); +	/* re-initialize the GDI field */ +	pwfield &= ~MIPS_PWFIELD_GDI_MASK; +	pwfield |= PGDIR_SHIFT << MIPS_PWFIELD_GDI_SHIFT; +	/* re-initialize the PTI field including the even/odd bit */ +	pwfield &= ~MIPS_PWFIELD_PTI_MASK; +	pwfield |= PAGE_SHIFT << MIPS_PWFIELD_PTI_SHIFT; +	/* Set the PTEI right shift */ +	ptei = _PAGE_GLOBAL_SHIFT << MIPS_PWFIELD_PTEI_SHIFT; +	pwfield |= ptei; +	write_c0_pwfield(pwfield); +	/* Check whether the PTEI value is supported */ +	back_to_back_c0_hazard(); +	pwfield = read_c0_pwfield(); +	if (((pwfield & MIPS_PWFIELD_PTEI_MASK) << MIPS_PWFIELD_PTEI_SHIFT) +		!= ptei) { +		pr_warn("Unsupported PTEI field value: 0x%lx. HTW will not be enabled", +			ptei); +		/* +		 * Drop option to avoid HTW being enabled via another path +		 * (eg htw_reset()) +		 */ +		current_cpu_data.options &= ~MIPS_CPU_HTW; +		return; +	} + +	pwsize = ilog2(PTRS_PER_PGD) << MIPS_PWSIZE_GDW_SHIFT; +	pwsize |= ilog2(PTRS_PER_PTE) << MIPS_PWSIZE_PTW_SHIFT; +	write_c0_pwsize(pwsize); + +	/* Make sure everything is set before we enable the HTW */ +	back_to_back_c0_hazard(); + +	/* Enable HTW and disable the rest of the pwctl fields */ +	config = 1 << MIPS_PWCTL_PWEN_SHIFT; +	write_c0_pwctl(config); +	pr_info("Hardware Page Table Walker enabled\n"); + +	print_htw_config(); +} +  void build_tlb_refill_handler(void)  {  	/* @@ -2258,5 +2349,8 @@ void build_tlb_refill_handler(void)  		}  		if (cpu_has_local_ebase)  			build_r4000_tlb_refill_handler(); +		if (cpu_has_htw) +			config_htw_params(); +  	}  }  | 
