diff options
Diffstat (limited to 'arch/blackfin/kernel/cplb-mpu/cplbmgr.c')
-rw-r--r-- | arch/blackfin/kernel/cplb-mpu/cplbmgr.c | 119 |
1 files changed, 63 insertions, 56 deletions
diff --git a/arch/blackfin/kernel/cplb-mpu/cplbmgr.c b/arch/blackfin/kernel/cplb-mpu/cplbmgr.c index baa52e261f0d..87463ce87f5a 100644 --- a/arch/blackfin/kernel/cplb-mpu/cplbmgr.c +++ b/arch/blackfin/kernel/cplb-mpu/cplbmgr.c @@ -25,15 +25,21 @@ #include <asm/cplbinit.h> #include <asm/mmu_context.h> -#define FAULT_RW (1 << 16) -#define FAULT_USERSUPV (1 << 17) +/* + * 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. + */ int page_mask_nelts; int page_mask_order; -unsigned long *current_rwx_mask; +unsigned long *current_rwx_mask[NR_CPUS]; -int nr_dcplb_miss, nr_icplb_miss, nr_icplb_supv_miss, nr_dcplb_prot; -int nr_cplb_flush; +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]; static inline void disable_dcplb(void) { @@ -98,42 +104,42 @@ static inline int write_permitted(int status, unsigned long data) } /* Counters to implement round-robin replacement. */ -static int icplb_rr_index, dcplb_rr_index; +static int icplb_rr_index[NR_CPUS], dcplb_rr_index[NR_CPUS]; /* * Find an ICPLB entry to be evicted and return its index. */ -static int evict_one_icplb(void) +static int evict_one_icplb(unsigned int cpu) { int i; for (i = first_switched_icplb; i < MAX_CPLBS; i++) - if ((icplb_tbl[i].data & CPLB_VALID) == 0) + if ((icplb_tbl[cpu][i].data & CPLB_VALID) == 0) return i; - i = first_switched_icplb + icplb_rr_index; + i = first_switched_icplb + icplb_rr_index[cpu]; if (i >= MAX_CPLBS) { i -= MAX_CPLBS - first_switched_icplb; - icplb_rr_index -= MAX_CPLBS - first_switched_icplb; + icplb_rr_index[cpu] -= MAX_CPLBS - first_switched_icplb; } - icplb_rr_index++; + icplb_rr_index[cpu]++; return i; } -static int evict_one_dcplb(void) +static int evict_one_dcplb(unsigned int cpu) { int i; for (i = first_switched_dcplb; i < MAX_CPLBS; i++) - if ((dcplb_tbl[i].data & CPLB_VALID) == 0) + if ((dcplb_tbl[cpu][i].data & CPLB_VALID) == 0) return i; - i = first_switched_dcplb + dcplb_rr_index; + i = first_switched_dcplb + dcplb_rr_index[cpu]; if (i >= MAX_CPLBS) { i -= MAX_CPLBS - first_switched_dcplb; - dcplb_rr_index -= MAX_CPLBS - first_switched_dcplb; + dcplb_rr_index[cpu] -= MAX_CPLBS - first_switched_dcplb; } - dcplb_rr_index++; + dcplb_rr_index[cpu]++; return i; } -static noinline int dcplb_miss(void) +static noinline int dcplb_miss(unsigned int cpu) { unsigned long addr = bfin_read_DCPLB_FAULT_ADDR(); int status = bfin_read_DCPLB_STATUS(); @@ -141,7 +147,7 @@ static noinline int dcplb_miss(void) int idx; unsigned long d_data; - nr_dcplb_miss++; + nr_dcplb_miss[cpu]++; d_data = CPLB_SUPV_WR | CPLB_VALID | CPLB_DIRTY | PAGE_SIZE_4KB; #ifdef CONFIG_BFIN_DCACHE @@ -168,25 +174,25 @@ static noinline int dcplb_miss(void) } else if (addr >= _ramend) { d_data |= CPLB_USER_RD | CPLB_USER_WR; } else { - mask = current_rwx_mask; + mask = current_rwx_mask[cpu]; if (mask) { int page = addr >> PAGE_SHIFT; - int offs = page >> 5; + int idx = page >> 5; int bit = 1 << (page & 31); - if (mask[offs] & bit) + if (mask[idx] & bit) d_data |= CPLB_USER_RD; mask += page_mask_nelts; - if (mask[offs] & bit) + if (mask[idx] & bit) d_data |= CPLB_USER_WR; } } - idx = evict_one_dcplb(); + idx = evict_one_dcplb(cpu); addr &= PAGE_MASK; - dcplb_tbl[idx].addr = addr; - dcplb_tbl[idx].data = d_data; + dcplb_tbl[cpu][idx].addr = addr; + dcplb_tbl[cpu][idx].data = d_data; disable_dcplb(); bfin_write32(DCPLB_DATA0 + idx * 4, d_data); @@ -196,21 +202,21 @@ static noinline int dcplb_miss(void) return 0; } -static noinline int icplb_miss(void) +static noinline int icplb_miss(unsigned int cpu) { unsigned long addr = bfin_read_ICPLB_FAULT_ADDR(); int status = bfin_read_ICPLB_STATUS(); int idx; unsigned long i_data; - nr_icplb_miss++; + nr_icplb_miss[cpu]++; /* If inside the uncached DMA region, fault. */ if (addr >= _ramend - DMA_UNCACHED_REGION && addr < _ramend) return CPLB_PROT_VIOL; if (status & FAULT_USERSUPV) - nr_icplb_supv_miss++; + nr_icplb_supv_miss[cpu]++; /* * First, try to find a CPLB that matches this address. If we @@ -218,8 +224,8 @@ static noinline int icplb_miss(void) * that the instruction crosses a page boundary. */ for (idx = first_switched_icplb; idx < MAX_CPLBS; idx++) { - if (icplb_tbl[idx].data & CPLB_VALID) { - unsigned long this_addr = icplb_tbl[idx].addr; + if (icplb_tbl[cpu][idx].data & CPLB_VALID) { + unsigned long this_addr = icplb_tbl[cpu][idx].addr; if (this_addr <= addr && this_addr + PAGE_SIZE > addr) { addr += PAGE_SIZE; break; @@ -257,23 +263,23 @@ static noinline int icplb_miss(void) * Otherwise, check the x bitmap of the current process. */ if (!(status & FAULT_USERSUPV)) { - unsigned long *mask = current_rwx_mask; + unsigned long *mask = current_rwx_mask[cpu]; if (mask) { int page = addr >> PAGE_SHIFT; - int offs = page >> 5; + int idx = page >> 5; int bit = 1 << (page & 31); mask += 2 * page_mask_nelts; - if (mask[offs] & bit) + if (mask[idx] & bit) i_data |= CPLB_USER_RD; } } } - idx = evict_one_icplb(); + idx = evict_one_icplb(cpu); addr &= PAGE_MASK; - icplb_tbl[idx].addr = addr; - icplb_tbl[idx].data = i_data; + icplb_tbl[cpu][idx].addr = addr; + icplb_tbl[cpu][idx].data = i_data; disable_icplb(); bfin_write32(ICPLB_DATA0 + idx * 4, i_data); @@ -283,19 +289,19 @@ static noinline int icplb_miss(void) return 0; } -static noinline int dcplb_protection_fault(void) +static noinline int dcplb_protection_fault(unsigned int cpu) { int status = bfin_read_DCPLB_STATUS(); - nr_dcplb_prot++; + nr_dcplb_prot[cpu]++; if (status & FAULT_RW) { int idx = faulting_cplb_index(status); - unsigned long data = dcplb_tbl[idx].data; + unsigned long data = dcplb_tbl[cpu][idx].data; if (!(data & CPLB_WT) && !(data & CPLB_DIRTY) && write_permitted(status, data)) { data |= CPLB_DIRTY; - dcplb_tbl[idx].data = data; + dcplb_tbl[cpu][idx].data = data; bfin_write32(DCPLB_DATA0 + idx * 4, data); return 0; } @@ -306,44 +312,45 @@ static noinline int dcplb_protection_fault(void) int cplb_hdr(int seqstat, struct pt_regs *regs) { int cause = seqstat & 0x3f; + unsigned int cpu = smp_processor_id(); switch (cause) { case 0x23: - return dcplb_protection_fault(); + return dcplb_protection_fault(cpu); case 0x2C: - return icplb_miss(); + return icplb_miss(cpu); case 0x26: - return dcplb_miss(); + return dcplb_miss(cpu); default: return 1; } } -void flush_switched_cplbs(void) +void flush_switched_cplbs(unsigned int cpu) { int i; unsigned long flags; - nr_cplb_flush++; + nr_cplb_flush[cpu]++; - local_irq_save(flags); + local_irq_save_hw(flags); disable_icplb(); for (i = first_switched_icplb; i < MAX_CPLBS; i++) { - icplb_tbl[i].data = 0; + icplb_tbl[cpu][i].data = 0; bfin_write32(ICPLB_DATA0 + i * 4, 0); } enable_icplb(); disable_dcplb(); for (i = first_switched_dcplb; i < MAX_CPLBS; i++) { - dcplb_tbl[i].data = 0; + dcplb_tbl[cpu][i].data = 0; bfin_write32(DCPLB_DATA0 + i * 4, 0); } enable_dcplb(); - local_irq_restore(flags); + local_irq_restore_hw(flags); } -void set_mask_dcplbs(unsigned long *masks) +void set_mask_dcplbs(unsigned long *masks, unsigned int cpu) { int i; unsigned long addr = (unsigned long)masks; @@ -351,12 +358,12 @@ void set_mask_dcplbs(unsigned long *masks) unsigned long flags; if (!masks) { - current_rwx_mask = masks; + current_rwx_mask[cpu] = masks; return; } - local_irq_save(flags); - current_rwx_mask = masks; + local_irq_save_hw(flags); + current_rwx_mask[cpu] = masks; d_data = CPLB_SUPV_WR | CPLB_VALID | CPLB_DIRTY | PAGE_SIZE_4KB; #ifdef CONFIG_BFIN_DCACHE @@ -368,12 +375,12 @@ void set_mask_dcplbs(unsigned long *masks) disable_dcplb(); for (i = first_mask_dcplb; i < first_switched_dcplb; i++) { - dcplb_tbl[i].addr = addr; - dcplb_tbl[i].data = d_data; + dcplb_tbl[cpu][i].addr = addr; + dcplb_tbl[cpu][i].data = d_data; bfin_write32(DCPLB_DATA0 + i * 4, d_data); bfin_write32(DCPLB_ADDR0 + i * 4, addr); addr += PAGE_SIZE; } enable_dcplb(); - local_irq_restore(flags); + local_irq_restore_hw(flags); } |