linux/arch/blackfin/kernel/cplb-nompu/cplbmgr.c
<<
>>
Prefs
   1/*
   2 * Based on:     arch/blackfin/kernel/cplb-mpu/cplbmgr.c
   3 * Author:       Michael McTernan <mmcternan@airvana.com>
   4 *
   5 * Description:  CPLB miss handler.
   6 *
   7 * Modified:
   8 *               Copyright 2008 Airvana Inc.
   9 *               Copyright 2008-2009 Analog Devices Inc.
  10 *
  11 * Licensed under the GPL-2 or later
  12 */
  13
  14#include <linux/kernel.h>
  15#include <asm/blackfin.h>
  16#include <asm/cplbinit.h>
  17#include <asm/cplb.h>
  18#include <asm/mmu_context.h>
  19#include <asm/traps.h>
  20
  21/*
  22 * WARNING
  23 *
  24 * This file is compiled with certain -ffixed-reg options.  We have to
  25 * make sure not to call any functions here that could clobber these
  26 * registers.
  27 */
  28
  29int nr_dcplb_miss[NR_CPUS], nr_icplb_miss[NR_CPUS];
  30int nr_dcplb_supv_miss[NR_CPUS], nr_icplb_supv_miss[NR_CPUS];
  31int nr_cplb_flush[NR_CPUS], nr_dcplb_prot[NR_CPUS];
  32
  33#ifdef CONFIG_EXCPT_IRQ_SYSC_L1
  34#define MGR_ATTR __attribute__((l1_text))
  35#else
  36#define MGR_ATTR
  37#endif
  38
  39static inline void write_dcplb_data(int cpu, int idx, unsigned long data,
  40                                    unsigned long addr)
  41{
  42        _disable_dcplb();
  43        bfin_write32(DCPLB_DATA0 + idx * 4, data);
  44        bfin_write32(DCPLB_ADDR0 + idx * 4, addr);
  45        _enable_dcplb();
  46
  47#ifdef CONFIG_CPLB_INFO
  48        dcplb_tbl[cpu][idx].addr = addr;
  49        dcplb_tbl[cpu][idx].data = data;
  50#endif
  51}
  52
  53static inline void write_icplb_data(int cpu, int idx, unsigned long data,
  54                                    unsigned long addr)
  55{
  56        _disable_icplb();
  57        bfin_write32(ICPLB_DATA0 + idx * 4, data);
  58        bfin_write32(ICPLB_ADDR0 + idx * 4, addr);
  59        _enable_icplb();
  60
  61#ifdef CONFIG_CPLB_INFO
  62        icplb_tbl[cpu][idx].addr = addr;
  63        icplb_tbl[cpu][idx].data = data;
  64#endif
  65}
  66
  67/* Counters to implement round-robin replacement.  */
  68static int icplb_rr_index[NR_CPUS] PDT_ATTR;
  69static int dcplb_rr_index[NR_CPUS] PDT_ATTR;
  70
  71/*
  72 * Find an ICPLB entry to be evicted and return its index.
  73 */
  74static int evict_one_icplb(int cpu)
  75{
  76        int i = first_switched_icplb + icplb_rr_index[cpu];
  77        if (i >= MAX_CPLBS) {
  78                i -= MAX_CPLBS - first_switched_icplb;
  79                icplb_rr_index[cpu] -= MAX_CPLBS - first_switched_icplb;
  80        }
  81        icplb_rr_index[cpu]++;
  82        return i;
  83}
  84
  85static int evict_one_dcplb(int cpu)
  86{
  87        int i = first_switched_dcplb + dcplb_rr_index[cpu];
  88        if (i >= MAX_CPLBS) {
  89                i -= MAX_CPLBS - first_switched_dcplb;
  90                dcplb_rr_index[cpu] -= MAX_CPLBS - first_switched_dcplb;
  91        }
  92        dcplb_rr_index[cpu]++;
  93        return i;
  94}
  95
  96MGR_ATTR static int icplb_miss(int cpu)
  97{
  98        unsigned long addr = bfin_read_ICPLB_FAULT_ADDR();
  99        int status = bfin_read_ICPLB_STATUS();
 100        int idx;
 101        unsigned long i_data, base, addr1, eaddr;
 102
 103        nr_icplb_miss[cpu]++;
 104        if (unlikely(status & FAULT_USERSUPV))
 105                nr_icplb_supv_miss[cpu]++;
 106
 107        base = 0;
 108        idx = 0;
 109        do {
 110                eaddr = icplb_bounds[idx].eaddr;
 111                if (addr < eaddr)
 112                        break;
 113                base = eaddr;
 114        } while (++idx < icplb_nr_bounds);
 115
 116        if (unlikely(idx == icplb_nr_bounds))
 117                return CPLB_NO_ADDR_MATCH;
 118
 119        i_data = icplb_bounds[idx].data;
 120        if (unlikely(i_data == 0))
 121                return CPLB_NO_ADDR_MATCH;
 122
 123        addr1 = addr & ~(SIZE_4M - 1);
 124        addr &= ~(SIZE_1M - 1);
 125        i_data |= PAGE_SIZE_1MB;
 126        if (addr1 >= base && (addr1 + SIZE_4M) <= eaddr) {
 127                /*
 128                 * This works because
 129                 * (PAGE_SIZE_4MB & PAGE_SIZE_1MB) == PAGE_SIZE_1MB.
 130                 */
 131                i_data |= PAGE_SIZE_4MB;
 132                addr = addr1;
 133        }
 134
 135        /* Pick entry to evict */
 136        idx = evict_one_icplb(cpu);
 137
 138        write_icplb_data(cpu, idx, i_data, addr);
 139
 140        return CPLB_RELOADED;
 141}
 142
 143MGR_ATTR static int dcplb_miss(int cpu)
 144{
 145        unsigned long addr = bfin_read_DCPLB_FAULT_ADDR();
 146        int status = bfin_read_DCPLB_STATUS();
 147        int idx;
 148        unsigned long d_data, base, addr1, eaddr;
 149
 150        nr_dcplb_miss[cpu]++;
 151        if (unlikely(status & FAULT_USERSUPV))
 152                nr_dcplb_supv_miss[cpu]++;
 153
 154        base = 0;
 155        idx = 0;
 156        do {
 157                eaddr = dcplb_bounds[idx].eaddr;
 158                if (addr < eaddr)
 159                        break;
 160                base = eaddr;
 161        } while (++idx < dcplb_nr_bounds);
 162
 163        if (unlikely(idx == dcplb_nr_bounds))
 164                return CPLB_NO_ADDR_MATCH;
 165
 166        d_data = dcplb_bounds[idx].data;
 167        if (unlikely(d_data == 0))
 168                return CPLB_NO_ADDR_MATCH;
 169
 170        addr1 = addr & ~(SIZE_4M - 1);
 171        addr &= ~(SIZE_1M - 1);
 172        d_data |= PAGE_SIZE_1MB;
 173        if (addr1 >= base && (addr1 + SIZE_4M) <= eaddr) {
 174                /*
 175                 * This works because
 176                 * (PAGE_SIZE_4MB & PAGE_SIZE_1MB) == PAGE_SIZE_1MB.
 177                 */
 178                d_data |= PAGE_SIZE_4MB;
 179                addr = addr1;
 180        }
 181
 182        /* Pick entry to evict */
 183        idx = evict_one_dcplb(cpu);
 184
 185        write_dcplb_data(cpu, idx, d_data, addr);
 186
 187        return CPLB_RELOADED;
 188}
 189
 190MGR_ATTR int cplb_hdr(int seqstat, struct pt_regs *regs)
 191{
 192        int cause = seqstat & 0x3f;
 193        unsigned int cpu = raw_smp_processor_id();
 194        switch (cause) {
 195        case VEC_CPLB_I_M:
 196                return icplb_miss(cpu);
 197        case VEC_CPLB_M:
 198                return dcplb_miss(cpu);
 199        default:
 200                return CPLB_UNKNOWN_ERR;
 201        }
 202}
 203
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.