| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | /*
 | 
					
						
							| 
									
										
										
										
											2009-09-24 14:11:24 +00:00
										 |  |  |  * Blackfin CPLB exception handling for when MPU in on | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2009-09-24 14:11:24 +00:00
										 |  |  |  * Copyright 2008-2009 Analog Devices Inc. | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2009-09-24 14:11:24 +00:00
										 |  |  |  * Licensed under the GPL-2 or later. | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2009-09-24 14:11:24 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | #include <linux/module.h>
 | 
					
						
							|  |  |  | #include <linux/mm.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <asm/blackfin.h>
 | 
					
						
							| 
									
										
										
										
											2008-10-16 23:25:34 +08:00
										 |  |  | #include <asm/cacheflush.h>
 | 
					
						
							| 
									
										
										
										
											2009-08-07 01:20:58 +00:00
										 |  |  | #include <asm/cplb.h>
 | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | #include <asm/cplbinit.h>
 | 
					
						
							|  |  |  | #include <asm/mmu_context.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-01-07 23:14:38 +08:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * WARNING | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This file is compiled with certain -ffixed-reg options.  We have to | 
					
						
							|  |  |  |  * make sure not to call any functions here that could clobber these | 
					
						
							|  |  |  |  * registers. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | int page_mask_nelts; | 
					
						
							|  |  |  | int page_mask_order; | 
					
						
							| 
									
										
										
										
											2008-11-18 17:48:22 +08:00
										 |  |  | unsigned long *current_rwx_mask[NR_CPUS]; | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-11-18 17:48:22 +08:00
										 |  |  | int nr_dcplb_miss[NR_CPUS], nr_icplb_miss[NR_CPUS]; | 
					
						
							|  |  |  | int nr_icplb_supv_miss[NR_CPUS], nr_dcplb_prot[NR_CPUS]; | 
					
						
							|  |  |  | int nr_cplb_flush[NR_CPUS]; | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-01-20 07:25:31 +00:00
										 |  |  | #ifdef CONFIG_EXCPT_IRQ_SYSC_L1
 | 
					
						
							|  |  |  | #define MGR_ATTR __attribute__((l1_text))
 | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  | #define MGR_ATTR
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Given the contents of the status register, return the index of the | 
					
						
							|  |  |  |  * CPLB that caused the fault. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static inline int faulting_cplb_index(int status) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int signbits = __builtin_bfin_norm_fr1x32(status & 0xFFFF); | 
					
						
							|  |  |  | 	return 30 - signbits; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Given the contents of the status register and the DCPLB_DATA contents, | 
					
						
							|  |  |  |  * return true if a write access should be permitted. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static inline int write_permitted(int status, unsigned long data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (status & FAULT_USERSUPV) | 
					
						
							|  |  |  | 		return !!(data & CPLB_SUPV_WR); | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		return !!(data & CPLB_USER_WR); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Counters to implement round-robin replacement.  */ | 
					
						
							| 
									
										
										
										
											2008-11-18 17:48:22 +08:00
										 |  |  | static int icplb_rr_index[NR_CPUS], dcplb_rr_index[NR_CPUS]; | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Find an ICPLB entry to be evicted and return its index. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2010-01-20 07:25:31 +00:00
										 |  |  | MGR_ATTR static int evict_one_icplb(unsigned int cpu) | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | { | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 	for (i = first_switched_icplb; i < MAX_CPLBS; i++) | 
					
						
							| 
									
										
										
										
											2008-11-18 17:48:22 +08:00
										 |  |  | 		if ((icplb_tbl[cpu][i].data & CPLB_VALID) == 0) | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 			return i; | 
					
						
							| 
									
										
										
										
											2008-11-18 17:48:22 +08:00
										 |  |  | 	i = first_switched_icplb + icplb_rr_index[cpu]; | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 	if (i >= MAX_CPLBS) { | 
					
						
							|  |  |  | 		i -= MAX_CPLBS - first_switched_icplb; | 
					
						
							| 
									
										
										
										
											2008-11-18 17:48:22 +08:00
										 |  |  | 		icplb_rr_index[cpu] -= MAX_CPLBS - first_switched_icplb; | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-11-18 17:48:22 +08:00
										 |  |  | 	icplb_rr_index[cpu]++; | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 	return i; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-01-20 07:25:31 +00:00
										 |  |  | MGR_ATTR static int evict_one_dcplb(unsigned int cpu) | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | { | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 	for (i = first_switched_dcplb; i < MAX_CPLBS; i++) | 
					
						
							| 
									
										
										
										
											2008-11-18 17:48:22 +08:00
										 |  |  | 		if ((dcplb_tbl[cpu][i].data & CPLB_VALID) == 0) | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 			return i; | 
					
						
							| 
									
										
										
										
											2008-11-18 17:48:22 +08:00
										 |  |  | 	i = first_switched_dcplb + dcplb_rr_index[cpu]; | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 	if (i >= MAX_CPLBS) { | 
					
						
							|  |  |  | 		i -= MAX_CPLBS - first_switched_dcplb; | 
					
						
							| 
									
										
										
										
											2008-11-18 17:48:22 +08:00
										 |  |  | 		dcplb_rr_index[cpu] -= MAX_CPLBS - first_switched_dcplb; | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-11-18 17:48:22 +08:00
										 |  |  | 	dcplb_rr_index[cpu]++; | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 	return i; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-01-20 07:25:31 +00:00
										 |  |  | MGR_ATTR static noinline int dcplb_miss(unsigned int cpu) | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | { | 
					
						
							|  |  |  | 	unsigned long addr = bfin_read_DCPLB_FAULT_ADDR(); | 
					
						
							|  |  |  | 	int status = bfin_read_DCPLB_STATUS(); | 
					
						
							|  |  |  | 	unsigned long *mask; | 
					
						
							|  |  |  | 	int idx; | 
					
						
							|  |  |  | 	unsigned long d_data; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-11-18 17:48:22 +08:00
										 |  |  | 	nr_dcplb_miss[cpu]++; | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	d_data = CPLB_SUPV_WR | CPLB_VALID | CPLB_DIRTY | PAGE_SIZE_4KB; | 
					
						
							| 
									
										
										
										
											2009-06-16 09:48:33 +00:00
										 |  |  | #ifdef CONFIG_BFIN_EXTMEM_DCACHEABLE
 | 
					
						
							| 
									
										
										
										
											2009-06-10 06:26:26 +00:00
										 |  |  | 	if (bfin_addr_dcacheable(addr)) { | 
					
						
							| 
									
										
										
										
											2008-04-23 07:26:23 +08:00
										 |  |  | 		d_data |= CPLB_L1_CHBL | ANOMALY_05000158_WORKAROUND; | 
					
						
							| 
									
										
										
										
											2009-06-16 09:48:33 +00:00
										 |  |  | # ifdef CONFIG_BFIN_EXTMEM_WRITETHROUGH
 | 
					
						
							| 
									
										
										
										
											2008-04-23 07:26:23 +08:00
										 |  |  | 		d_data |= CPLB_L1_AOW | CPLB_WT; | 
					
						
							| 
									
										
										
										
											2009-06-16 09:48:33 +00:00
										 |  |  | # endif
 | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-04-23 07:26:23 +08:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2009-06-16 09:48:33 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (L2_LENGTH && addr >= L2_START && addr < L2_START + L2_LENGTH) { | 
					
						
							|  |  |  | 		addr = L2_START; | 
					
						
							|  |  |  | 		d_data = L2_DMEMORY; | 
					
						
							|  |  |  | 	} else if (addr >= physical_mem_end) { | 
					
						
							| 
									
										
										
										
											2009-12-02 02:50:43 +00:00
										 |  |  | 		if (addr >= ASYNC_BANK0_BASE && addr < ASYNC_BANK3_BASE + ASYNC_BANK3_SIZE) { | 
					
						
							| 
									
										
										
										
											2009-12-07 10:05:58 +00:00
										 |  |  | 			mask = current_rwx_mask[cpu]; | 
					
						
							|  |  |  | 			if (mask) { | 
					
						
							|  |  |  | 				int page = (addr - (ASYNC_BANK0_BASE - _ramend)) >> PAGE_SHIFT; | 
					
						
							|  |  |  | 				int idx = page >> 5; | 
					
						
							|  |  |  | 				int bit = 1 << (page & 31); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				if (mask[idx] & bit) | 
					
						
							|  |  |  | 					d_data |= CPLB_USER_RD; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2008-04-24 05:44:32 +08:00
										 |  |  | 		} else if (addr >= BOOT_ROM_START && addr < BOOT_ROM_START + BOOT_ROM_LENGTH | 
					
						
							|  |  |  | 		    && (status & (FAULT_RW | FAULT_USERSUPV)) == FAULT_USERSUPV) { | 
					
						
							|  |  |  | 			addr &= ~(1 * 1024 * 1024 - 1); | 
					
						
							|  |  |  | 			d_data &= ~PAGE_SIZE_4KB; | 
					
						
							| 
									
										
										
										
											2008-04-24 07:23:36 +08:00
										 |  |  | 			d_data |= PAGE_SIZE_1MB; | 
					
						
							| 
									
										
										
										
											2008-04-23 07:26:23 +08:00
										 |  |  | 		} else | 
					
						
							|  |  |  | 			return CPLB_PROT_VIOL; | 
					
						
							| 
									
										
										
										
											2008-04-24 02:58:26 +08:00
										 |  |  | 	} else if (addr >= _ramend) { | 
					
						
							| 
									
										
										
										
											2009-12-09 07:01:50 +00:00
										 |  |  | 		d_data |= CPLB_USER_RD | CPLB_USER_WR; | 
					
						
							|  |  |  | 		if (reserved_mem_dcache_on) | 
					
						
							|  |  |  | 			d_data |= CPLB_L1_CHBL; | 
					
						
							| 
									
										
										
										
											2008-04-23 07:26:23 +08:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2008-11-18 17:48:22 +08:00
										 |  |  | 		mask = current_rwx_mask[cpu]; | 
					
						
							| 
									
										
										
										
											2008-04-23 07:26:23 +08:00
										 |  |  | 		if (mask) { | 
					
						
							|  |  |  | 			int page = addr >> PAGE_SHIFT; | 
					
						
							| 
									
										
										
										
											2008-11-18 17:48:22 +08:00
										 |  |  | 			int idx = page >> 5; | 
					
						
							| 
									
										
										
										
											2008-04-23 07:26:23 +08:00
										 |  |  | 			int bit = 1 << (page & 31); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-11-18 17:48:22 +08:00
										 |  |  | 			if (mask[idx] & bit) | 
					
						
							| 
									
										
										
										
											2008-04-23 07:26:23 +08:00
										 |  |  | 				d_data |= CPLB_USER_RD; | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-04-23 07:26:23 +08:00
										 |  |  | 			mask += page_mask_nelts; | 
					
						
							| 
									
										
										
										
											2008-11-18 17:48:22 +08:00
										 |  |  | 			if (mask[idx] & bit) | 
					
						
							| 
									
										
										
										
											2008-04-23 07:26:23 +08:00
										 |  |  | 				d_data |= CPLB_USER_WR; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-11-18 17:48:22 +08:00
										 |  |  | 	idx = evict_one_dcplb(cpu); | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	addr &= PAGE_MASK; | 
					
						
							| 
									
										
										
										
											2008-11-18 17:48:22 +08:00
										 |  |  | 	dcplb_tbl[cpu][idx].addr = addr; | 
					
						
							|  |  |  | 	dcplb_tbl[cpu][idx].data = d_data; | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-08-07 01:20:58 +00:00
										 |  |  | 	_disable_dcplb(); | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 	bfin_write32(DCPLB_DATA0 + idx * 4, d_data); | 
					
						
							|  |  |  | 	bfin_write32(DCPLB_ADDR0 + idx * 4, addr); | 
					
						
							| 
									
										
										
										
											2009-08-07 01:20:58 +00:00
										 |  |  | 	_enable_dcplb(); | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-01-20 07:25:31 +00:00
										 |  |  | MGR_ATTR static noinline int icplb_miss(unsigned int cpu) | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | { | 
					
						
							|  |  |  | 	unsigned long addr = bfin_read_ICPLB_FAULT_ADDR(); | 
					
						
							|  |  |  | 	int status = bfin_read_ICPLB_STATUS(); | 
					
						
							|  |  |  | 	int idx; | 
					
						
							|  |  |  | 	unsigned long i_data; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-11-18 17:48:22 +08:00
										 |  |  | 	nr_icplb_miss[cpu]++; | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-04-24 02:58:26 +08:00
										 |  |  | 	/* If inside the uncached DMA region, fault.  */ | 
					
						
							|  |  |  | 	if (addr >= _ramend - DMA_UNCACHED_REGION && addr < _ramend) | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 		return CPLB_PROT_VIOL; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-04-24 02:58:26 +08:00
										 |  |  | 	if (status & FAULT_USERSUPV) | 
					
						
							| 
									
										
										
										
											2008-11-18 17:48:22 +08:00
										 |  |  | 		nr_icplb_supv_miss[cpu]++; | 
					
						
							| 
									
										
										
										
											2008-04-24 02:58:26 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * First, try to find a CPLB that matches this address.  If we | 
					
						
							|  |  |  | 	 * find one, then the fact that we're in the miss handler means | 
					
						
							|  |  |  | 	 * that the instruction crosses a page boundary. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	for (idx = first_switched_icplb; idx < MAX_CPLBS; idx++) { | 
					
						
							| 
									
										
										
										
											2008-11-18 17:48:22 +08:00
										 |  |  | 		if (icplb_tbl[cpu][idx].data & CPLB_VALID) { | 
					
						
							|  |  |  | 			unsigned long this_addr = icplb_tbl[cpu][idx].addr; | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 			if (this_addr <= addr && this_addr + PAGE_SIZE > addr) { | 
					
						
							|  |  |  | 				addr += PAGE_SIZE; | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	i_data = CPLB_VALID | CPLB_PORTPRIO | PAGE_SIZE_4KB; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-06-16 09:48:33 +00:00
										 |  |  | #ifdef CONFIG_BFIN_EXTMEM_ICACHEABLE
 | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 	/*
 | 
					
						
							| 
									
										
										
										
											2008-04-24 02:58:26 +08:00
										 |  |  | 	 * Normal RAM, and possibly the reserved memory area, are | 
					
						
							|  |  |  | 	 * cacheable. | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2008-04-24 02:58:26 +08:00
										 |  |  | 	if (addr < _ramend || | 
					
						
							|  |  |  | 	    (addr < physical_mem_end && reserved_mem_icache_on)) | 
					
						
							|  |  |  | 		i_data |= CPLB_L1_CHBL | ANOMALY_05000158_WORKAROUND; | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-06-16 09:48:33 +00:00
										 |  |  | 	if (L2_LENGTH && addr >= L2_START && addr < L2_START + L2_LENGTH) { | 
					
						
							|  |  |  | 		addr = L2_START; | 
					
						
							|  |  |  | 		i_data = L2_IMEMORY; | 
					
						
							|  |  |  | 	} else if (addr >= physical_mem_end) { | 
					
						
							| 
									
										
										
										
											2009-12-02 02:50:43 +00:00
										 |  |  | 		if (addr >= ASYNC_BANK0_BASE && addr < ASYNC_BANK3_BASE + ASYNC_BANK3_SIZE) { | 
					
						
							| 
									
										
										
										
											2009-12-07 10:05:58 +00:00
										 |  |  | 			if (!(status & FAULT_USERSUPV)) { | 
					
						
							|  |  |  | 				unsigned long *mask = current_rwx_mask[cpu]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				if (mask) { | 
					
						
							|  |  |  | 					int page = (addr - (ASYNC_BANK0_BASE - _ramend)) >> PAGE_SHIFT; | 
					
						
							|  |  |  | 					int idx = page >> 5; | 
					
						
							|  |  |  | 					int bit = 1 << (page & 31); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					mask += 2 * page_mask_nelts; | 
					
						
							|  |  |  | 					if (mask[idx] & bit) | 
					
						
							|  |  |  | 						i_data |= CPLB_USER_RD; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2009-12-02 02:50:43 +00:00
										 |  |  | 		} else if (addr >= BOOT_ROM_START && addr < BOOT_ROM_START + BOOT_ROM_LENGTH | 
					
						
							| 
									
										
										
										
											2008-04-24 07:23:36 +08:00
										 |  |  | 		    && (status & FAULT_USERSUPV)) { | 
					
						
							|  |  |  | 			addr &= ~(1 * 1024 * 1024 - 1); | 
					
						
							|  |  |  | 			i_data &= ~PAGE_SIZE_4KB; | 
					
						
							|  |  |  | 			i_data |= PAGE_SIZE_1MB; | 
					
						
							|  |  |  | 		} else | 
					
						
							|  |  |  | 		    return CPLB_PROT_VIOL; | 
					
						
							| 
									
										
										
										
											2008-04-24 02:58:26 +08:00
										 |  |  | 	} else if (addr >= _ramend) { | 
					
						
							|  |  |  | 		i_data |= CPLB_USER_RD; | 
					
						
							| 
									
										
										
										
											2009-12-09 07:01:50 +00:00
										 |  |  | 		if (reserved_mem_icache_on) | 
					
						
							|  |  |  | 			i_data |= CPLB_L1_CHBL; | 
					
						
							| 
									
										
										
										
											2008-04-24 02:58:26 +08:00
										 |  |  | 	} else { | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * Two cases to distinguish - a supervisor access must | 
					
						
							|  |  |  | 		 * necessarily be for a module page; we grant it | 
					
						
							|  |  |  | 		 * unconditionally (could do better here in the future). | 
					
						
							|  |  |  | 		 * Otherwise, check the x bitmap of the current process. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		if (!(status & FAULT_USERSUPV)) { | 
					
						
							| 
									
										
										
										
											2008-11-18 17:48:22 +08:00
										 |  |  | 			unsigned long *mask = current_rwx_mask[cpu]; | 
					
						
							| 
									
										
										
										
											2008-04-24 02:58:26 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			if (mask) { | 
					
						
							|  |  |  | 				int page = addr >> PAGE_SHIFT; | 
					
						
							| 
									
										
										
										
											2008-11-18 17:48:22 +08:00
										 |  |  | 				int idx = page >> 5; | 
					
						
							| 
									
										
										
										
											2008-04-24 02:58:26 +08:00
										 |  |  | 				int bit = 1 << (page & 31); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				mask += 2 * page_mask_nelts; | 
					
						
							| 
									
										
										
										
											2008-11-18 17:48:22 +08:00
										 |  |  | 				if (mask[idx] & bit) | 
					
						
							| 
									
										
										
										
											2008-04-24 02:58:26 +08:00
										 |  |  | 					i_data |= CPLB_USER_RD; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-11-18 17:48:22 +08:00
										 |  |  | 	idx = evict_one_icplb(cpu); | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 	addr &= PAGE_MASK; | 
					
						
							| 
									
										
										
										
											2008-11-18 17:48:22 +08:00
										 |  |  | 	icplb_tbl[cpu][idx].addr = addr; | 
					
						
							|  |  |  | 	icplb_tbl[cpu][idx].data = i_data; | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-08-07 01:20:58 +00:00
										 |  |  | 	_disable_icplb(); | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 	bfin_write32(ICPLB_DATA0 + idx * 4, i_data); | 
					
						
							|  |  |  | 	bfin_write32(ICPLB_ADDR0 + idx * 4, addr); | 
					
						
							| 
									
										
										
										
											2009-08-07 01:20:58 +00:00
										 |  |  | 	_enable_icplb(); | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-01-20 07:25:31 +00:00
										 |  |  | MGR_ATTR static noinline int dcplb_protection_fault(unsigned int cpu) | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | { | 
					
						
							|  |  |  | 	int status = bfin_read_DCPLB_STATUS(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-11-18 17:48:22 +08:00
										 |  |  | 	nr_dcplb_prot[cpu]++; | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (status & FAULT_RW) { | 
					
						
							|  |  |  | 		int idx = faulting_cplb_index(status); | 
					
						
							| 
									
										
										
										
											2008-11-18 17:48:22 +08:00
										 |  |  | 		unsigned long data = dcplb_tbl[cpu][idx].data; | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 		if (!(data & CPLB_WT) && !(data & CPLB_DIRTY) && | 
					
						
							|  |  |  | 		    write_permitted(status, data)) { | 
					
						
							|  |  |  | 			data |= CPLB_DIRTY; | 
					
						
							| 
									
										
										
										
											2008-11-18 17:48:22 +08:00
										 |  |  | 			dcplb_tbl[cpu][idx].data = data; | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 			bfin_write32(DCPLB_DATA0 + idx * 4, data); | 
					
						
							|  |  |  | 			return 0; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return CPLB_PROT_VIOL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-01-20 07:25:31 +00:00
										 |  |  | MGR_ATTR int cplb_hdr(int seqstat, struct pt_regs *regs) | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | { | 
					
						
							|  |  |  | 	int cause = seqstat & 0x3f; | 
					
						
							| 
									
										
										
										
											2009-08-20 04:17:47 +00:00
										 |  |  | 	unsigned int cpu = raw_smp_processor_id(); | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 	switch (cause) { | 
					
						
							|  |  |  | 	case 0x23: | 
					
						
							| 
									
										
										
										
											2008-11-18 17:48:22 +08:00
										 |  |  | 		return dcplb_protection_fault(cpu); | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 	case 0x2C: | 
					
						
							| 
									
										
										
										
											2008-11-18 17:48:22 +08:00
										 |  |  | 		return icplb_miss(cpu); | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 	case 0x26: | 
					
						
							| 
									
										
										
										
											2008-11-18 17:48:22 +08:00
										 |  |  | 		return dcplb_miss(cpu); | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 	default: | 
					
						
							| 
									
										
										
										
											2008-04-23 07:26:23 +08:00
										 |  |  | 		return 1; | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-11-18 17:48:22 +08:00
										 |  |  | void flush_switched_cplbs(unsigned int cpu) | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | { | 
					
						
							|  |  |  | 	int i; | 
					
						
							| 
									
										
										
										
											2008-10-07 16:27:01 +08:00
										 |  |  | 	unsigned long flags; | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-11-18 17:48:22 +08:00
										 |  |  | 	nr_cplb_flush[cpu]++; | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-01-07 23:14:39 +08:00
										 |  |  | 	local_irq_save_hw(flags); | 
					
						
							| 
									
										
										
										
											2009-08-07 01:20:58 +00:00
										 |  |  | 	_disable_icplb(); | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 	for (i = first_switched_icplb; i < MAX_CPLBS; i++) { | 
					
						
							| 
									
										
										
										
											2008-11-18 17:48:22 +08:00
										 |  |  | 		icplb_tbl[cpu][i].data = 0; | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 		bfin_write32(ICPLB_DATA0 + i * 4, 0); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-08-07 01:20:58 +00:00
										 |  |  | 	_enable_icplb(); | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-08-07 01:20:58 +00:00
										 |  |  | 	_disable_dcplb(); | 
					
						
							| 
									
										
										
										
											2008-04-24 02:56:36 +08:00
										 |  |  | 	for (i = first_switched_dcplb; i < MAX_CPLBS; i++) { | 
					
						
							| 
									
										
										
										
											2008-11-18 17:48:22 +08:00
										 |  |  | 		dcplb_tbl[cpu][i].data = 0; | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 		bfin_write32(DCPLB_DATA0 + i * 4, 0); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-08-07 01:20:58 +00:00
										 |  |  | 	_enable_dcplb(); | 
					
						
							| 
									
										
										
										
											2009-01-07 23:14:39 +08:00
										 |  |  | 	local_irq_restore_hw(flags); | 
					
						
							| 
									
										
										
										
											2008-10-07 16:27:01 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-11-18 17:48:22 +08:00
										 |  |  | void set_mask_dcplbs(unsigned long *masks, unsigned int cpu) | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | { | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 	unsigned long addr = (unsigned long)masks; | 
					
						
							|  |  |  | 	unsigned long d_data; | 
					
						
							| 
									
										
										
										
											2008-10-07 16:27:01 +08:00
										 |  |  | 	unsigned long flags; | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-10-07 16:27:01 +08:00
										 |  |  | 	if (!masks) { | 
					
						
							| 
									
										
										
										
											2008-11-18 17:48:22 +08:00
										 |  |  | 		current_rwx_mask[cpu] = masks; | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 		return; | 
					
						
							| 
									
										
										
										
											2008-10-07 16:27:01 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-01-07 23:14:39 +08:00
										 |  |  | 	local_irq_save_hw(flags); | 
					
						
							| 
									
										
										
										
											2008-11-18 17:48:22 +08:00
										 |  |  | 	current_rwx_mask[cpu] = masks; | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-06-16 09:48:33 +00:00
										 |  |  | 	if (L2_LENGTH && addr >= L2_START && addr < L2_START + L2_LENGTH) { | 
					
						
							|  |  |  | 		addr = L2_START; | 
					
						
							|  |  |  | 		d_data = L2_DMEMORY; | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		d_data = CPLB_SUPV_WR | CPLB_VALID | CPLB_DIRTY | PAGE_SIZE_4KB; | 
					
						
							|  |  |  | #ifdef CONFIG_BFIN_EXTMEM_DCACHEABLE
 | 
					
						
							|  |  |  | 		d_data |= CPLB_L1_CHBL; | 
					
						
							|  |  |  | # ifdef CONFIG_BFIN_EXTMEM_WRITETHROUGH
 | 
					
						
							|  |  |  | 		d_data |= CPLB_L1_AOW | CPLB_WT; | 
					
						
							|  |  |  | # endif
 | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2009-06-16 09:48:33 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-08-07 01:20:58 +00:00
										 |  |  | 	_disable_dcplb(); | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 	for (i = first_mask_dcplb; i < first_switched_dcplb; i++) { | 
					
						
							| 
									
										
										
										
											2008-11-18 17:48:22 +08:00
										 |  |  | 		dcplb_tbl[cpu][i].addr = addr; | 
					
						
							|  |  |  | 		dcplb_tbl[cpu][i].data = d_data; | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | 		bfin_write32(DCPLB_DATA0 + i * 4, d_data); | 
					
						
							|  |  |  | 		bfin_write32(DCPLB_ADDR0 + i * 4, addr); | 
					
						
							|  |  |  | 		addr += PAGE_SIZE; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-08-07 01:20:58 +00:00
										 |  |  | 	_enable_dcplb(); | 
					
						
							| 
									
										
										
										
											2009-01-07 23:14:39 +08:00
										 |  |  | 	local_irq_restore_hw(flags); | 
					
						
							| 
									
										
										
										
											2008-01-27 18:39:16 +08:00
										 |  |  | } |