Path: blob/master/arch/blackfin/kernel/cplb-nompu/cplbmgr.c
15133 views
/*1* Based on: arch/blackfin/kernel/cplb-mpu/cplbmgr.c2* Author: Michael McTernan <[email protected]>3*4* Description: CPLB miss handler.5*6* Modified:7* Copyright 2008 Airvana Inc.8* Copyright 2008-2009 Analog Devices Inc.9*10* Licensed under the GPL-2 or later11*/1213#include <linux/kernel.h>14#include <asm/blackfin.h>15#include <asm/cplbinit.h>16#include <asm/cplb.h>17#include <asm/mmu_context.h>18#include <asm/traps.h>1920/*21* WARNING22*23* This file is compiled with certain -ffixed-reg options. We have to24* make sure not to call any functions here that could clobber these25* registers.26*/2728int nr_dcplb_miss[NR_CPUS], nr_icplb_miss[NR_CPUS];29int nr_dcplb_supv_miss[NR_CPUS], nr_icplb_supv_miss[NR_CPUS];30int nr_cplb_flush[NR_CPUS], nr_dcplb_prot[NR_CPUS];3132#ifdef CONFIG_EXCPT_IRQ_SYSC_L133#define MGR_ATTR __attribute__((l1_text))34#else35#define MGR_ATTR36#endif3738static inline void write_dcplb_data(int cpu, int idx, unsigned long data,39unsigned long addr)40{41_disable_dcplb();42bfin_write32(DCPLB_DATA0 + idx * 4, data);43bfin_write32(DCPLB_ADDR0 + idx * 4, addr);44_enable_dcplb();4546#ifdef CONFIG_CPLB_INFO47dcplb_tbl[cpu][idx].addr = addr;48dcplb_tbl[cpu][idx].data = data;49#endif50}5152static inline void write_icplb_data(int cpu, int idx, unsigned long data,53unsigned long addr)54{55_disable_icplb();56bfin_write32(ICPLB_DATA0 + idx * 4, data);57bfin_write32(ICPLB_ADDR0 + idx * 4, addr);58_enable_icplb();5960#ifdef CONFIG_CPLB_INFO61icplb_tbl[cpu][idx].addr = addr;62icplb_tbl[cpu][idx].data = data;63#endif64}6566/* Counters to implement round-robin replacement. */67static int icplb_rr_index[NR_CPUS] PDT_ATTR;68static int dcplb_rr_index[NR_CPUS] PDT_ATTR;6970/*71* Find an ICPLB entry to be evicted and return its index.72*/73static int evict_one_icplb(int cpu)74{75int i = first_switched_icplb + icplb_rr_index[cpu];76if (i >= MAX_CPLBS) {77i -= MAX_CPLBS - first_switched_icplb;78icplb_rr_index[cpu] -= MAX_CPLBS - first_switched_icplb;79}80icplb_rr_index[cpu]++;81return i;82}8384static int evict_one_dcplb(int cpu)85{86int i = first_switched_dcplb + dcplb_rr_index[cpu];87if (i >= MAX_CPLBS) {88i -= MAX_CPLBS - first_switched_dcplb;89dcplb_rr_index[cpu] -= MAX_CPLBS - first_switched_dcplb;90}91dcplb_rr_index[cpu]++;92return i;93}9495MGR_ATTR static int icplb_miss(int cpu)96{97unsigned long addr = bfin_read_ICPLB_FAULT_ADDR();98int status = bfin_read_ICPLB_STATUS();99int idx;100unsigned long i_data, base, addr1, eaddr;101102nr_icplb_miss[cpu]++;103if (unlikely(status & FAULT_USERSUPV))104nr_icplb_supv_miss[cpu]++;105106base = 0;107idx = 0;108do {109eaddr = icplb_bounds[idx].eaddr;110if (addr < eaddr)111break;112base = eaddr;113} while (++idx < icplb_nr_bounds);114115if (unlikely(idx == icplb_nr_bounds))116return CPLB_NO_ADDR_MATCH;117118i_data = icplb_bounds[idx].data;119if (unlikely(i_data == 0))120return CPLB_NO_ADDR_MATCH;121122addr1 = addr & ~(SIZE_4M - 1);123addr &= ~(SIZE_1M - 1);124i_data |= PAGE_SIZE_1MB;125if (addr1 >= base && (addr1 + SIZE_4M) <= eaddr) {126/*127* This works because128* (PAGE_SIZE_4MB & PAGE_SIZE_1MB) == PAGE_SIZE_1MB.129*/130i_data |= PAGE_SIZE_4MB;131addr = addr1;132}133134/* Pick entry to evict */135idx = evict_one_icplb(cpu);136137write_icplb_data(cpu, idx, i_data, addr);138139return CPLB_RELOADED;140}141142MGR_ATTR static int dcplb_miss(int cpu)143{144unsigned long addr = bfin_read_DCPLB_FAULT_ADDR();145int status = bfin_read_DCPLB_STATUS();146int idx;147unsigned long d_data, base, addr1, eaddr;148149nr_dcplb_miss[cpu]++;150if (unlikely(status & FAULT_USERSUPV))151nr_dcplb_supv_miss[cpu]++;152153base = 0;154idx = 0;155do {156eaddr = dcplb_bounds[idx].eaddr;157if (addr < eaddr)158break;159base = eaddr;160} while (++idx < dcplb_nr_bounds);161162if (unlikely(idx == dcplb_nr_bounds))163return CPLB_NO_ADDR_MATCH;164165d_data = dcplb_bounds[idx].data;166if (unlikely(d_data == 0))167return CPLB_NO_ADDR_MATCH;168169addr1 = addr & ~(SIZE_4M - 1);170addr &= ~(SIZE_1M - 1);171d_data |= PAGE_SIZE_1MB;172if (addr1 >= base && (addr1 + SIZE_4M) <= eaddr) {173/*174* This works because175* (PAGE_SIZE_4MB & PAGE_SIZE_1MB) == PAGE_SIZE_1MB.176*/177d_data |= PAGE_SIZE_4MB;178addr = addr1;179}180181/* Pick entry to evict */182idx = evict_one_dcplb(cpu);183184write_dcplb_data(cpu, idx, d_data, addr);185186return CPLB_RELOADED;187}188189MGR_ATTR int cplb_hdr(int seqstat, struct pt_regs *regs)190{191int cause = seqstat & 0x3f;192unsigned int cpu = raw_smp_processor_id();193switch (cause) {194case VEC_CPLB_I_M:195return icplb_miss(cpu);196case VEC_CPLB_M:197return dcplb_miss(cpu);198default:199return CPLB_UNKNOWN_ERR;200}201}202203204