]> nv-tegra.nvidia Code Review - linux-2.6.git/blob - arch/blackfin/kernel/cplb-nompu/cplbmgr.c
Merge branch 'ioat' into dmaengine
[linux-2.6.git] / arch / blackfin / kernel / cplb-nompu / cplbmgr.c
1 /*
2  * File:         arch/blackfin/kernel/cplb-nompu-c/cplbmgr.c
3  * Based on:     arch/blackfin/kernel/cplb-mpu/cplbmgr.c
4  * Author:       Michael McTernan <mmcternan@airvana.com>
5  *
6  * Created:      01Nov2008
7  * Description:  CPLB miss handler.
8  *
9  * Modified:
10  *               Copyright 2008 Airvana Inc.
11  *               Copyright 2004-2007 Analog Devices Inc.
12  *
13  * Bugs:         Enter bugs at http://blackfin.uclinux.org/
14  *
15  * This program is free software; you can redistribute it and/or modify
16  * it under the terms of the GNU General Public License as published by
17  * the Free Software Foundation; either version 2 of the License, or
18  * (at your option) any later version.
19  *
20  * This program is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23  * GNU General Public License for more details.
24  */
25
26 #include <linux/kernel.h>
27 #include <asm/blackfin.h>
28 #include <asm/cplbinit.h>
29 #include <asm/cplb.h>
30 #include <asm/mmu_context.h>
31 #include <asm/traps.h>
32
33 /*
34  * WARNING
35  *
36  * This file is compiled with certain -ffixed-reg options.  We have to
37  * make sure not to call any functions here that could clobber these
38  * registers.
39  */
40
41 int nr_dcplb_miss[NR_CPUS], nr_icplb_miss[NR_CPUS];
42 int nr_dcplb_supv_miss[NR_CPUS], nr_icplb_supv_miss[NR_CPUS];
43 int nr_cplb_flush[NR_CPUS], nr_dcplb_prot[NR_CPUS];
44
45 #ifdef CONFIG_EXCPT_IRQ_SYSC_L1
46 #define MGR_ATTR __attribute__((l1_text))
47 #else
48 #define MGR_ATTR
49 #endif
50
51 /*
52  * We're in an exception handler.  The normal cli nop nop workaround
53  * isn't going to do very much, as the only thing that can interrupt
54  * us is an NMI, and the cli isn't going to stop that.
55  */
56 #define NOWA_SSYNC __asm__ __volatile__ ("ssync;")
57
58 /* Anomaly handlers provide SSYNCs, so avoid extra if anomaly is present */
59 #if ANOMALY_05000125
60
61 #define bfin_write_DMEM_CONTROL_SSYNC(v)    bfin_write_DMEM_CONTROL(v)
62 #define bfin_write_IMEM_CONTROL_SSYNC(v)    bfin_write_IMEM_CONTROL(v)
63
64 #else
65
66 #define bfin_write_DMEM_CONTROL_SSYNC(v) \
67     do { NOWA_SSYNC; bfin_write_DMEM_CONTROL(v); NOWA_SSYNC; } while (0)
68 #define bfin_write_IMEM_CONTROL_SSYNC(v) \
69     do { NOWA_SSYNC; bfin_write_IMEM_CONTROL(v); NOWA_SSYNC; } while (0)
70
71 #endif
72
73 static inline void write_dcplb_data(int cpu, int idx, unsigned long data,
74                                     unsigned long addr)
75 {
76         unsigned long ctrl = bfin_read_DMEM_CONTROL();
77         bfin_write_DMEM_CONTROL_SSYNC(ctrl & ~ENDCPLB);
78         bfin_write32(DCPLB_DATA0 + idx * 4, data);
79         bfin_write32(DCPLB_ADDR0 + idx * 4, addr);
80         bfin_write_DMEM_CONTROL_SSYNC(ctrl);
81
82 #ifdef CONFIG_CPLB_INFO
83         dcplb_tbl[cpu][idx].addr = addr;
84         dcplb_tbl[cpu][idx].data = data;
85 #endif
86 }
87
88 static inline void write_icplb_data(int cpu, int idx, unsigned long data,
89                                     unsigned long addr)
90 {
91         unsigned long ctrl = bfin_read_IMEM_CONTROL();
92
93         bfin_write_IMEM_CONTROL_SSYNC(ctrl & ~ENICPLB);
94         bfin_write32(ICPLB_DATA0 + idx * 4, data);
95         bfin_write32(ICPLB_ADDR0 + idx * 4, addr);
96         bfin_write_IMEM_CONTROL_SSYNC(ctrl);
97
98 #ifdef CONFIG_CPLB_INFO
99         icplb_tbl[cpu][idx].addr = addr;
100         icplb_tbl[cpu][idx].data = data;
101 #endif
102 }
103
104 /* Counters to implement round-robin replacement.  */
105 static int icplb_rr_index[NR_CPUS] PDT_ATTR;
106 static int dcplb_rr_index[NR_CPUS] PDT_ATTR;
107
108 /*
109  * Find an ICPLB entry to be evicted and return its index.
110  */
111 static int evict_one_icplb(int cpu)
112 {
113         int i = first_switched_icplb + icplb_rr_index[cpu];
114         if (i >= MAX_CPLBS) {
115                 i -= MAX_CPLBS - first_switched_icplb;
116                 icplb_rr_index[cpu] -= MAX_CPLBS - first_switched_icplb;
117         }
118         icplb_rr_index[cpu]++;
119         return i;
120 }
121
122 static int evict_one_dcplb(int cpu)
123 {
124         int i = first_switched_dcplb + dcplb_rr_index[cpu];
125         if (i >= MAX_CPLBS) {
126                 i -= MAX_CPLBS - first_switched_dcplb;
127                 dcplb_rr_index[cpu] -= MAX_CPLBS - first_switched_dcplb;
128         }
129         dcplb_rr_index[cpu]++;
130         return i;
131 }
132
133 MGR_ATTR static int icplb_miss(int cpu)
134 {
135         unsigned long addr = bfin_read_ICPLB_FAULT_ADDR();
136         int status = bfin_read_ICPLB_STATUS();
137         int idx;
138         unsigned long i_data, base, addr1, eaddr;
139
140         nr_icplb_miss[cpu]++;
141         if (unlikely(status & FAULT_USERSUPV))
142                 nr_icplb_supv_miss[cpu]++;
143
144         base = 0;
145         idx = 0;
146         do {
147                 eaddr = icplb_bounds[idx].eaddr;
148                 if (addr < eaddr)
149                         break;
150                 base = eaddr;
151         } while (++idx < icplb_nr_bounds);
152
153         if (unlikely(idx == icplb_nr_bounds))
154                 return CPLB_NO_ADDR_MATCH;
155
156         i_data = icplb_bounds[idx].data;
157         if (unlikely(i_data == 0))
158                 return CPLB_NO_ADDR_MATCH;
159
160         addr1 = addr & ~(SIZE_4M - 1);
161         addr &= ~(SIZE_1M - 1);
162         i_data |= PAGE_SIZE_1MB;
163         if (addr1 >= base && (addr1 + SIZE_4M) <= eaddr) {
164                 /*
165                  * This works because
166                  * (PAGE_SIZE_4MB & PAGE_SIZE_1MB) == PAGE_SIZE_1MB.
167                  */
168                 i_data |= PAGE_SIZE_4MB;
169                 addr = addr1;
170         }
171
172         /* Pick entry to evict */
173         idx = evict_one_icplb(cpu);
174
175         write_icplb_data(cpu, idx, i_data, addr);
176
177         return CPLB_RELOADED;
178 }
179
180 MGR_ATTR static int dcplb_miss(int cpu)
181 {
182         unsigned long addr = bfin_read_DCPLB_FAULT_ADDR();
183         int status = bfin_read_DCPLB_STATUS();
184         int idx;
185         unsigned long d_data, base, addr1, eaddr;
186
187         nr_dcplb_miss[cpu]++;
188         if (unlikely(status & FAULT_USERSUPV))
189                 nr_dcplb_supv_miss[cpu]++;
190
191         base = 0;
192         idx = 0;
193         do {
194                 eaddr = dcplb_bounds[idx].eaddr;
195                 if (addr < eaddr)
196                         break;
197                 base = eaddr;
198         } while (++idx < dcplb_nr_bounds);
199
200         if (unlikely(idx == dcplb_nr_bounds))
201                 return CPLB_NO_ADDR_MATCH;
202
203         d_data = dcplb_bounds[idx].data;
204         if (unlikely(d_data == 0))
205                 return CPLB_NO_ADDR_MATCH;
206
207         addr1 = addr & ~(SIZE_4M - 1);
208         addr &= ~(SIZE_1M - 1);
209         d_data |= PAGE_SIZE_1MB;
210         if (addr1 >= base && (addr1 + SIZE_4M) <= eaddr) {
211                 /*
212                  * This works because
213                  * (PAGE_SIZE_4MB & PAGE_SIZE_1MB) == PAGE_SIZE_1MB.
214                  */
215                 d_data |= PAGE_SIZE_4MB;
216                 addr = addr1;
217         }
218
219         /* Pick entry to evict */
220         idx = evict_one_dcplb(cpu);
221
222         write_dcplb_data(cpu, idx, d_data, addr);
223
224         return CPLB_RELOADED;
225 }
226
227 MGR_ATTR int cplb_hdr(int seqstat, struct pt_regs *regs)
228 {
229         int cause = seqstat & 0x3f;
230         unsigned int cpu = smp_processor_id();
231         switch (cause) {
232         case VEC_CPLB_I_M:
233                 return icplb_miss(cpu);
234         case VEC_CPLB_M:
235                 return dcplb_miss(cpu);
236         default:
237                 return CPLB_UNKNOWN_ERR;
238         }
239 }