aboutsummaryrefslogtreecommitdiffstats
path: root/arch/blackfin/kernel/cplb-nompu
diff options
context:
space:
mode:
Diffstat (limited to 'arch/blackfin/kernel/cplb-nompu')
-rw-r--r--arch/blackfin/kernel/cplb-nompu/Makefile7
-rw-r--r--arch/blackfin/kernel/cplb-nompu/cacheinit.c26
-rw-r--r--arch/blackfin/kernel/cplb-nompu/cplbhdlr.S130
-rw-r--r--arch/blackfin/kernel/cplb-nompu/cplbinit.c498
-rw-r--r--arch/blackfin/kernel/cplb-nompu/cplbmgr.S648
-rw-r--r--arch/blackfin/kernel/cplb-nompu/cplbmgr.c283
6 files changed, 410 insertions, 1182 deletions
diff --git a/arch/blackfin/kernel/cplb-nompu/Makefile b/arch/blackfin/kernel/cplb-nompu/Makefile
index 4010eca1c6c2..7d70d3bf3212 100644
--- a/arch/blackfin/kernel/cplb-nompu/Makefile
+++ b/arch/blackfin/kernel/cplb-nompu/Makefile
@@ -2,4 +2,9 @@
2# arch/blackfin/kernel/cplb-nompu/Makefile 2# arch/blackfin/kernel/cplb-nompu/Makefile
3# 3#
4 4
5obj-y := cplbinit.o cacheinit.o cplbhdlr.o cplbmgr.o 5obj-y := cplbinit.o cacheinit.o cplbmgr.o
6
7CFLAGS_cplbmgr.o := -ffixed-I0 -ffixed-I1 -ffixed-I2 -ffixed-I3 \
8 -ffixed-L0 -ffixed-L1 -ffixed-L2 -ffixed-L3 \
9 -ffixed-M0 -ffixed-M1 -ffixed-M2 -ffixed-M3 \
10 -ffixed-B0 -ffixed-B1 -ffixed-B2 -ffixed-B3
diff --git a/arch/blackfin/kernel/cplb-nompu/cacheinit.c b/arch/blackfin/kernel/cplb-nompu/cacheinit.c
index 3a385aec67d5..c6ff947f9d37 100644
--- a/arch/blackfin/kernel/cplb-nompu/cacheinit.c
+++ b/arch/blackfin/kernel/cplb-nompu/cacheinit.c
@@ -25,19 +25,15 @@
25#include <asm/cplbinit.h> 25#include <asm/cplbinit.h>
26 26
27#if defined(CONFIG_BFIN_ICACHE) 27#if defined(CONFIG_BFIN_ICACHE)
28void __cpuinit bfin_icache_init(u_long icplb[]) 28void __cpuinit bfin_icache_init(struct cplb_entry *icplb_tbl)
29{ 29{
30 unsigned long *table = icplb;
31 unsigned long ctrl; 30 unsigned long ctrl;
32 int i; 31 int i;
33 32
33 SSYNC();
34 for (i = 0; i < MAX_CPLBS; i++) { 34 for (i = 0; i < MAX_CPLBS; i++) {
35 unsigned long addr = *table++; 35 bfin_write32(ICPLB_ADDR0 + i * 4, icplb_tbl[i].addr);
36 unsigned long data = *table++; 36 bfin_write32(ICPLB_DATA0 + i * 4, icplb_tbl[i].data);
37 if (addr == (unsigned long)-1)
38 break;
39 bfin_write32(ICPLB_ADDR0 + i * 4, addr);
40 bfin_write32(ICPLB_DATA0 + i * 4, data);
41 } 37 }
42 ctrl = bfin_read_IMEM_CONTROL(); 38 ctrl = bfin_read_IMEM_CONTROL();
43 ctrl |= IMC | ENICPLB; 39 ctrl |= IMC | ENICPLB;
@@ -47,24 +43,20 @@ void __cpuinit bfin_icache_init(u_long icplb[])
47#endif 43#endif
48 44
49#if defined(CONFIG_BFIN_DCACHE) 45#if defined(CONFIG_BFIN_DCACHE)
50void __cpuinit bfin_dcache_init(u_long dcplb[]) 46void __cpuinit bfin_dcache_init(struct cplb_entry *dcplb_tbl)
51{ 47{
52 unsigned long *table = dcplb;
53 unsigned long ctrl; 48 unsigned long ctrl;
54 int i; 49 int i;
55 50
51 SSYNC();
56 for (i = 0; i < MAX_CPLBS; i++) { 52 for (i = 0; i < MAX_CPLBS; i++) {
57 unsigned long addr = *table++; 53 bfin_write32(DCPLB_ADDR0 + i * 4, dcplb_tbl[i].addr);
58 unsigned long data = *table++; 54 bfin_write32(DCPLB_DATA0 + i * 4, dcplb_tbl[i].data);
59 if (addr == (unsigned long)-1)
60 break;
61 bfin_write32(DCPLB_ADDR0 + i * 4, addr);
62 bfin_write32(DCPLB_DATA0 + i * 4, data);
63 } 55 }
56
64 ctrl = bfin_read_DMEM_CONTROL(); 57 ctrl = bfin_read_DMEM_CONTROL();
65 ctrl |= DMEM_CNTR; 58 ctrl |= DMEM_CNTR;
66 bfin_write_DMEM_CONTROL(ctrl); 59 bfin_write_DMEM_CONTROL(ctrl);
67
68 SSYNC(); 60 SSYNC();
69} 61}
70#endif 62#endif
diff --git a/arch/blackfin/kernel/cplb-nompu/cplbhdlr.S b/arch/blackfin/kernel/cplb-nompu/cplbhdlr.S
deleted file mode 100644
index ecbabc0a1fed..000000000000
--- a/arch/blackfin/kernel/cplb-nompu/cplbhdlr.S
+++ /dev/null
@@ -1,130 +0,0 @@
1/*
2 * File: arch/blackfin/mach-common/cplbhdlr.S
3 * Based on:
4 * Author: LG Soft India
5 *
6 * Created: ?
7 * Description: CPLB exception handler
8 *
9 * Modified:
10 * Copyright 2004-2006 Analog Devices Inc.
11 *
12 * Bugs: Enter bugs at http://blackfin.uclinux.org/
13 *
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, see the file COPYING, or write
26 * to the Free Software Foundation, Inc.,
27 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
28 */
29
30#include <linux/linkage.h>
31#include <asm/cplb.h>
32#include <asm/entry.h>
33
34#ifdef CONFIG_EXCPT_IRQ_SYSC_L1
35.section .l1.text
36#else
37.text
38#endif
39
40.type _cplb_mgr, STT_FUNC;
41.type _panic_cplb_error, STT_FUNC;
42
43.align 2
44
45ENTRY(__cplb_hdr)
46 R2 = SEQSTAT;
47
48 /* Mask the contents of SEQSTAT and leave only EXCAUSE in R2 */
49 R2 <<= 26;
50 R2 >>= 26;
51
52 R1 = 0x23; /* Data access CPLB protection violation */
53 CC = R2 == R1;
54 IF !CC JUMP .Lnot_data_write;
55 R0 = 2; /* is a write to data space*/
56 JUMP .Lis_icplb_miss;
57
58.Lnot_data_write:
59 R1 = 0x2C; /* CPLB miss on an instruction fetch */
60 CC = R2 == R1;
61 R0 = 0; /* is_data_miss == False*/
62 IF CC JUMP .Lis_icplb_miss;
63
64 R1 = 0x26;
65 CC = R2 == R1;
66 IF !CC JUMP .Lunknown;
67
68 R0 = 1; /* is_data_miss == True*/
69
70.Lis_icplb_miss:
71
72#if defined(CONFIG_BFIN_ICACHE) || defined(CONFIG_BFIN_DCACHE)
73# if defined(CONFIG_BFIN_ICACHE) && !defined(CONFIG_BFIN_DCACHE)
74 R1 = CPLB_ENABLE_ICACHE;
75# endif
76# if !defined(CONFIG_BFIN_ICACHE) && defined(CONFIG_BFIN_DCACHE)
77 R1 = CPLB_ENABLE_DCACHE;
78# endif
79# if defined(CONFIG_BFIN_ICACHE) && defined(CONFIG_BFIN_DCACHE)
80 R1 = CPLB_ENABLE_DCACHE | CPLB_ENABLE_ICACHE;
81# endif
82#else
83 R1 = 0;
84#endif
85
86 [--SP] = RETS;
87 CALL _cplb_mgr;
88 RETS = [SP++];
89 CC = R0 == 0;
90 IF !CC JUMP .Lnot_replaced;
91 RTS;
92
93/*
94 * Diagnostic exception handlers
95 */
96.Lunknown:
97 R0 = CPLB_UNKNOWN_ERR;
98 JUMP .Lcplb_error;
99
100.Lnot_replaced:
101 CC = R0 == CPLB_NO_UNLOCKED;
102 IF !CC JUMP .Lnext_check;
103 R0 = CPLB_NO_UNLOCKED;
104 JUMP .Lcplb_error;
105
106.Lnext_check:
107 CC = R0 == CPLB_NO_ADDR_MATCH;
108 IF !CC JUMP .Lnext_check2;
109 R0 = CPLB_NO_ADDR_MATCH;
110 JUMP .Lcplb_error;
111
112.Lnext_check2:
113 CC = R0 == CPLB_PROT_VIOL;
114 IF !CC JUMP .Lstrange_return_from_cplb_mgr;
115 R0 = CPLB_PROT_VIOL;
116 JUMP .Lcplb_error;
117
118.Lstrange_return_from_cplb_mgr:
119 IDLE;
120 CSYNC;
121 JUMP .Lstrange_return_from_cplb_mgr;
122
123.Lcplb_error:
124 R1 = sp;
125 SP += -12;
126 call _panic_cplb_error;
127 SP += 12;
128 JUMP.L _handle_bad_cplb;
129
130ENDPROC(__cplb_hdr)
diff --git a/arch/blackfin/kernel/cplb-nompu/cplbinit.c b/arch/blackfin/kernel/cplb-nompu/cplbinit.c
index 4c010ba50a80..0e28f7595733 100644
--- a/arch/blackfin/kernel/cplb-nompu/cplbinit.c
+++ b/arch/blackfin/kernel/cplb-nompu/cplbinit.c
@@ -29,417 +29,143 @@
29#include <asm/cplbinit.h> 29#include <asm/cplbinit.h>
30#include <asm/mem_map.h> 30#include <asm/mem_map.h>
31 31
32u_long icplb_tables[NR_CPUS][CPLB_TBL_ENTRIES+1]; 32struct cplb_entry icplb_tbl[NR_CPUS][MAX_CPLBS] PDT_ATTR;
33u_long dcplb_tables[NR_CPUS][CPLB_TBL_ENTRIES+1]; 33struct cplb_entry dcplb_tbl[NR_CPUS][MAX_CPLBS] PDT_ATTR;
34 34
35#ifdef CONFIG_CPLB_SWITCH_TAB_L1 35int first_switched_icplb PDT_ATTR;
36#define PDT_ATTR __attribute__((l1_data)) 36int first_switched_dcplb PDT_ATTR;
37#else
38#define PDT_ATTR
39#endif
40
41u_long ipdt_tables[NR_CPUS][MAX_SWITCH_I_CPLBS+1] PDT_ATTR;
42u_long dpdt_tables[NR_CPUS][MAX_SWITCH_D_CPLBS+1] PDT_ATTR;
43#ifdef CONFIG_CPLB_INFO
44u_long ipdt_swapcount_tables[NR_CPUS][MAX_SWITCH_I_CPLBS] PDT_ATTR;
45u_long dpdt_swapcount_tables[NR_CPUS][MAX_SWITCH_D_CPLBS] PDT_ATTR;
46#endif
47 37
48struct s_cplb { 38struct cplb_boundary dcplb_bounds[9] PDT_ATTR;
49 struct cplb_tab init_i; 39struct cplb_boundary icplb_bounds[7] PDT_ATTR;
50 struct cplb_tab init_d;
51 struct cplb_tab switch_i;
52 struct cplb_tab switch_d;
53};
54 40
55#if defined(CONFIG_BFIN_DCACHE) || defined(CONFIG_BFIN_ICACHE) 41int icplb_nr_bounds PDT_ATTR;
56static struct cplb_desc cplb_data[] = { 42int dcplb_nr_bounds PDT_ATTR;
57 {
58 .start = 0,
59 .end = SIZE_1K,
60 .psize = SIZE_1K,
61 .attr = INITIAL_T | SWITCH_T | I_CPLB | D_CPLB,
62 .i_conf = SDRAM_OOPS,
63 .d_conf = SDRAM_OOPS,
64#if defined(CONFIG_DEBUG_HUNT_FOR_ZERO)
65 .valid = 1,
66#else
67 .valid = 0,
68#endif
69 .name = "Zero Pointer Guard Page",
70 },
71 {
72 .start = 0, /* dyanmic */
73 .end = 0, /* dynamic */
74 .psize = SIZE_4M,
75 .attr = INITIAL_T | SWITCH_T | I_CPLB,
76 .i_conf = L1_IMEMORY,
77 .d_conf = 0,
78 .valid = 1,
79 .name = "L1 I-Memory",
80 },
81 {
82 .start = 0, /* dynamic */
83 .end = 0, /* dynamic */
84 .psize = SIZE_4M,
85 .attr = INITIAL_T | SWITCH_T | D_CPLB,
86 .i_conf = 0,
87 .d_conf = L1_DMEMORY,
88#if ((L1_DATA_A_LENGTH > 0) || (L1_DATA_B_LENGTH > 0))
89 .valid = 1,
90#else
91 .valid = 0,
92#endif
93 .name = "L1 D-Memory",
94 },
95 {
96 .start = L2_START,
97 .end = L2_START + L2_LENGTH,
98 .psize = SIZE_1M,
99 .attr = L2_ATTR,
100 .i_conf = L2_IMEMORY,
101 .d_conf = L2_DMEMORY,
102 .valid = (L2_LENGTH > 0),
103 .name = "L2 Memory",
104 },
105 {
106 .start = 0,
107 .end = 0, /* dynamic */
108 .psize = 0,
109 .attr = INITIAL_T | SWITCH_T | I_CPLB | D_CPLB,
110 .i_conf = SDRAM_IGENERIC,
111 .d_conf = SDRAM_DGENERIC,
112 .valid = 1,
113 .name = "Kernel Memory",
114 },
115 {
116 .start = 0, /* dynamic */
117 .end = 0, /* dynamic */
118 .psize = 0,
119 .attr = INITIAL_T | SWITCH_T | D_CPLB,
120 .i_conf = SDRAM_IGENERIC,
121 .d_conf = SDRAM_DNON_CHBL,
122 .valid = 1,
123 .name = "uClinux MTD Memory",
124 },
125 {
126 .start = 0, /* dynamic */
127 .end = 0, /* dynamic */
128 .psize = SIZE_1M,
129 .attr = INITIAL_T | SWITCH_T | D_CPLB,
130 .d_conf = SDRAM_DNON_CHBL,
131 .valid = 1,
132 .name = "Uncached DMA Zone",
133 },
134 {
135 .start = 0, /* dynamic */
136 .end = 0, /* dynamic */
137 .psize = 0,
138 .attr = SWITCH_T | D_CPLB,
139 .i_conf = 0, /* dynamic */
140 .d_conf = 0, /* dynamic */
141 .valid = 1,
142 .name = "Reserved Memory",
143 },
144 {
145 .start = ASYNC_BANK0_BASE,
146 .end = ASYNC_BANK3_BASE + ASYNC_BANK3_SIZE,
147 .psize = 0,
148 .attr = SWITCH_T | D_CPLB,
149 .d_conf = SDRAM_EBIU,
150 .valid = 1,
151 .name = "Asynchronous Memory Banks",
152 },
153 {
154 .start = BOOT_ROM_START,
155 .end = BOOT_ROM_START + BOOT_ROM_LENGTH,
156 .psize = SIZE_1M,
157 .attr = SWITCH_T | I_CPLB | D_CPLB,
158 .i_conf = SDRAM_IGENERIC,
159 .d_conf = SDRAM_DGENERIC,
160 .valid = 1,
161 .name = "On-Chip BootROM",
162 },
163};
164 43
165static bool __init lock_kernel_check(u32 start, u32 end) 44void __init generate_cplb_tables_cpu(unsigned int cpu)
166{ 45{
167 if (start >= (u32)__init_begin || end <= (u32)_stext) 46 int i_d, i_i;
168 return false; 47 unsigned long addr;
169
170 /* This cplb block overlapped with kernel area. */
171 return true;
172}
173 48
174static void __init 49 struct cplb_entry *d_tbl = dcplb_tbl[cpu];
175fill_cplbtab(struct cplb_tab *table, 50 struct cplb_entry *i_tbl = icplb_tbl[cpu];
176 unsigned long start, unsigned long end,
177 unsigned long block_size, unsigned long cplb_data)
178{
179 int i;
180 51
181 switch (block_size) { 52 printk(KERN_INFO "NOMPU: setting up cplb tables\n");
182 case SIZE_4M:
183 i = 3;
184 break;
185 case SIZE_1M:
186 i = 2;
187 break;
188 case SIZE_4K:
189 i = 1;
190 break;
191 case SIZE_1K:
192 default:
193 i = 0;
194 break;
195 }
196 53
197 cplb_data = (cplb_data & ~(3 << 16)) | (i << 16); 54 i_d = i_i = 0;
198 55
199 while ((start < end) && (table->pos < table->size)) { 56 /* Set up the zero page. */
57 d_tbl[i_d].addr = 0;
58 d_tbl[i_d++].data = SDRAM_OOPS | PAGE_SIZE_1KB;
200 59
201 table->tab[table->pos++] = start; 60 /* Cover kernel memory with 4M pages. */
61 addr = 0;
202 62
203 if (lock_kernel_check(start, start + block_size)) 63 for (; addr < memory_start; addr += 4 * 1024 * 1024) {
204 table->tab[table->pos++] = 64 d_tbl[i_d].addr = addr;
205 cplb_data | CPLB_LOCK | CPLB_DIRTY; 65 d_tbl[i_d++].data = SDRAM_DGENERIC | PAGE_SIZE_4MB;
206 else 66 i_tbl[i_i].addr = addr;
207 table->tab[table->pos++] = cplb_data; 67 i_tbl[i_i++].data = SDRAM_IGENERIC | PAGE_SIZE_4MB;
68 }
208 69
209 start += block_size; 70 /* Cover L1 memory. One 4M area for code and data each is enough. */
71 if (L1_DATA_A_LENGTH || L1_DATA_B_LENGTH) {
72 d_tbl[i_d].addr = L1_DATA_A_START;
73 d_tbl[i_d++].data = L1_DMEMORY | PAGE_SIZE_4MB;
210 } 74 }
211} 75 i_tbl[i_i].addr = L1_CODE_START;
76 i_tbl[i_i++].data = L1_IMEMORY | PAGE_SIZE_4MB;
212 77
213static void __init close_cplbtab(struct cplb_tab *table) 78 first_switched_dcplb = i_d;
214{ 79 first_switched_icplb = i_i;
215 while (table->pos < table->size)
216 table->tab[table->pos++] = 0;
217}
218 80
219/* helper function */ 81 BUG_ON(first_switched_dcplb > MAX_CPLBS);
220static void __init 82 BUG_ON(first_switched_icplb > MAX_CPLBS);
221__fill_code_cplbtab(struct cplb_tab *t, int i, u32 a_start, u32 a_end)
222{
223 if (cplb_data[i].psize) {
224 fill_cplbtab(t,
225 cplb_data[i].start,
226 cplb_data[i].end,
227 cplb_data[i].psize,
228 cplb_data[i].i_conf);
229 } else {
230#if defined(CONFIG_BFIN_ICACHE)
231 if (ANOMALY_05000263 && i == SDRAM_KERN) {
232 fill_cplbtab(t,
233 cplb_data[i].start,
234 cplb_data[i].end,
235 SIZE_4M,
236 cplb_data[i].i_conf);
237 } else
238#endif
239 {
240 fill_cplbtab(t,
241 cplb_data[i].start,
242 a_start,
243 SIZE_1M,
244 cplb_data[i].i_conf);
245 fill_cplbtab(t,
246 a_start,
247 a_end,
248 SIZE_4M,
249 cplb_data[i].i_conf);
250 fill_cplbtab(t, a_end,
251 cplb_data[i].end,
252 SIZE_1M,
253 cplb_data[i].i_conf);
254 }
255 }
256}
257 83
258static void __init 84 while (i_d < MAX_CPLBS)
259__fill_data_cplbtab(struct cplb_tab *t, int i, u32 a_start, u32 a_end) 85 d_tbl[i_d++].data = 0;
260{ 86 while (i_i < MAX_CPLBS)
261 if (cplb_data[i].psize) { 87 i_tbl[i_i++].data = 0;
262 fill_cplbtab(t,
263 cplb_data[i].start,
264 cplb_data[i].end,
265 cplb_data[i].psize,
266 cplb_data[i].d_conf);
267 } else {
268 fill_cplbtab(t,
269 cplb_data[i].start,
270 a_start, SIZE_1M,
271 cplb_data[i].d_conf);
272 fill_cplbtab(t, a_start,
273 a_end, SIZE_4M,
274 cplb_data[i].d_conf);
275 fill_cplbtab(t, a_end,
276 cplb_data[i].end,
277 SIZE_1M,
278 cplb_data[i].d_conf);
279 }
280} 88}
281 89
282void __init generate_cplb_tables_cpu(unsigned int cpu) 90void __init generate_cplb_tables_all(void)
283{ 91{
92 int i_d, i_i;
284 93
285 u16 i, j, process; 94 i_d = 0;
286 u32 a_start, a_end, as, ae, as_1m; 95 /* Normal RAM, including MTD FS. */
287
288 struct cplb_tab *t_i = NULL;
289 struct cplb_tab *t_d = NULL;
290 struct s_cplb cplb;
291
292 printk(KERN_INFO "NOMPU: setting up cplb tables for global access\n");
293
294 cplb.init_i.size = CPLB_TBL_ENTRIES;
295 cplb.init_d.size = CPLB_TBL_ENTRIES;
296 cplb.switch_i.size = MAX_SWITCH_I_CPLBS;
297 cplb.switch_d.size = MAX_SWITCH_D_CPLBS;
298
299 cplb.init_i.pos = 0;
300 cplb.init_d.pos = 0;
301 cplb.switch_i.pos = 0;
302 cplb.switch_d.pos = 0;
303
304 cplb.init_i.tab = icplb_tables[cpu];
305 cplb.init_d.tab = dcplb_tables[cpu];
306 cplb.switch_i.tab = ipdt_tables[cpu];
307 cplb.switch_d.tab = dpdt_tables[cpu];
308
309 cplb_data[L1I_MEM].start = get_l1_code_start_cpu(cpu);
310 cplb_data[L1I_MEM].end = cplb_data[L1I_MEM].start + L1_CODE_LENGTH;
311 cplb_data[L1D_MEM].start = get_l1_data_a_start_cpu(cpu);
312 cplb_data[L1D_MEM].end = get_l1_data_b_start_cpu(cpu) + L1_DATA_B_LENGTH;
313 cplb_data[SDRAM_KERN].end = memory_end;
314
315#ifdef CONFIG_MTD_UCLINUX 96#ifdef CONFIG_MTD_UCLINUX
316 cplb_data[SDRAM_RAM_MTD].start = memory_mtd_start; 97 dcplb_bounds[i_d].eaddr = memory_mtd_start + mtd_size;
317 cplb_data[SDRAM_RAM_MTD].end = memory_mtd_start + mtd_size;
318 cplb_data[SDRAM_RAM_MTD].valid = mtd_size > 0;
319# if defined(CONFIG_ROMFS_FS)
320 cplb_data[SDRAM_RAM_MTD].attr |= I_CPLB;
321
322 /*
323 * The ROMFS_FS size is often not multiple of 1MB.
324 * This can cause multiple CPLB sets covering the same memory area.
325 * This will then cause multiple CPLB hit exceptions.
326 * Workaround: We ensure a contiguous memory area by extending the kernel
327 * memory section over the mtd section.
328 * For ROMFS_FS memory must be covered with ICPLBs anyways.
329 * So there is no difference between kernel and mtd memory setup.
330 */
331
332 cplb_data[SDRAM_KERN].end = memory_mtd_start + mtd_size;;
333 cplb_data[SDRAM_RAM_MTD].valid = 0;
334
335# endif
336#else 98#else
337 cplb_data[SDRAM_RAM_MTD].valid = 0; 99 dcplb_bounds[i_d].eaddr = memory_end;
338#endif 100#endif
101 dcplb_bounds[i_d++].data = SDRAM_DGENERIC;
102 /* DMA uncached region. */
103 if (DMA_UNCACHED_REGION) {
104 dcplb_bounds[i_d].eaddr = _ramend;
105 dcplb_bounds[i_d++].data = SDRAM_DNON_CHBL;
106 }
107 if (_ramend != physical_mem_end) {
108 /* Reserved memory. */
109 dcplb_bounds[i_d].eaddr = physical_mem_end;
110 dcplb_bounds[i_d++].data = (reserved_mem_dcache_on ?
111 SDRAM_DGENERIC : SDRAM_DNON_CHBL);
112 }
113 /* Addressing hole up to the async bank. */
114 dcplb_bounds[i_d].eaddr = ASYNC_BANK0_BASE;
115 dcplb_bounds[i_d++].data = 0;
116 /* ASYNC banks. */
117 dcplb_bounds[i_d].eaddr = ASYNC_BANK3_BASE + ASYNC_BANK3_SIZE;
118 dcplb_bounds[i_d++].data = SDRAM_EBIU;
119 /* Addressing hole up to BootROM. */
120 dcplb_bounds[i_d].eaddr = BOOT_ROM_START;
121 dcplb_bounds[i_d++].data = 0;
122 /* BootROM -- largest one should be less than 1 meg. */
123 dcplb_bounds[i_d].eaddr = BOOT_ROM_START + (1 * 1024 * 1024);
124 dcplb_bounds[i_d++].data = SDRAM_DGENERIC;
125 if (L2_LENGTH) {
126 /* Addressing hole up to L2 SRAM. */
127 dcplb_bounds[i_d].eaddr = L2_START;
128 dcplb_bounds[i_d++].data = 0;
129 /* L2 SRAM. */
130 dcplb_bounds[i_d].eaddr = L2_START + L2_LENGTH;
131 dcplb_bounds[i_d++].data = L2_DMEMORY;
132 }
133 dcplb_nr_bounds = i_d;
134 BUG_ON(dcplb_nr_bounds > ARRAY_SIZE(dcplb_bounds));
339 135
340 cplb_data[SDRAM_DMAZ].start = _ramend - DMA_UNCACHED_REGION; 136 i_i = 0;
341 cplb_data[SDRAM_DMAZ].end = _ramend; 137 /* Normal RAM, including MTD FS. */
342
343 cplb_data[RES_MEM].start = _ramend;
344 cplb_data[RES_MEM].end = physical_mem_end;
345
346 if (reserved_mem_dcache_on)
347 cplb_data[RES_MEM].d_conf = SDRAM_DGENERIC;
348 else
349 cplb_data[RES_MEM].d_conf = SDRAM_DNON_CHBL;
350
351 if (reserved_mem_icache_on)
352 cplb_data[RES_MEM].i_conf = SDRAM_IGENERIC;
353 else
354 cplb_data[RES_MEM].i_conf = SDRAM_INON_CHBL;
355
356 for (i = ZERO_P; i < ARRAY_SIZE(cplb_data); ++i) {
357 if (!cplb_data[i].valid)
358 continue;
359
360 as_1m = cplb_data[i].start % SIZE_1M;
361
362 /* We need to make sure all sections are properly 1M aligned
363 * However between Kernel Memory and the Kernel mtd section, depending on the
364 * rootfs size, there can be overlapping memory areas.
365 */
366
367 if (as_1m && i != L1I_MEM && i != L1D_MEM) {
368#ifdef CONFIG_MTD_UCLINUX 138#ifdef CONFIG_MTD_UCLINUX
369 if (i == SDRAM_RAM_MTD) { 139 icplb_bounds[i_i].eaddr = memory_mtd_start + mtd_size;
370 if ((cplb_data[SDRAM_KERN].end + 1) > cplb_data[SDRAM_RAM_MTD].start) 140#else
371 cplb_data[SDRAM_RAM_MTD].start = (cplb_data[i].start & (-2*SIZE_1M)) + SIZE_1M; 141 icplb_bounds[i_i].eaddr = memory_end;
372 else
373 cplb_data[SDRAM_RAM_MTD].start = (cplb_data[i].start & (-2*SIZE_1M));
374 } else
375#endif 142#endif
376 printk(KERN_WARNING "Unaligned Start of %s at 0x%X\n", 143 icplb_bounds[i_i++].data = SDRAM_IGENERIC;
377 cplb_data[i].name, cplb_data[i].start); 144 /* DMA uncached region. */
378 } 145 if (DMA_UNCACHED_REGION) {
379 146 icplb_bounds[i_i].eaddr = _ramend;
380 as = cplb_data[i].start % SIZE_4M; 147 icplb_bounds[i_i++].data = 0;
381 ae = cplb_data[i].end % SIZE_4M;
382
383 if (as)
384 a_start = cplb_data[i].start + (SIZE_4M - (as));
385 else
386 a_start = cplb_data[i].start;
387
388 a_end = cplb_data[i].end - ae;
389
390 for (j = INITIAL_T; j <= SWITCH_T; j++) {
391
392 switch (j) {
393 case INITIAL_T:
394 if (cplb_data[i].attr & INITIAL_T) {
395 t_i = &cplb.init_i;
396 t_d = &cplb.init_d;
397 process = 1;
398 } else
399 process = 0;
400 break;
401 case SWITCH_T:
402 if (cplb_data[i].attr & SWITCH_T) {
403 t_i = &cplb.switch_i;
404 t_d = &cplb.switch_d;
405 process = 1;
406 } else
407 process = 0;
408 break;
409 default:
410 process = 0;
411 break;
412 }
413
414 if (!process)
415 continue;
416 if (cplb_data[i].attr & I_CPLB)
417 __fill_code_cplbtab(t_i, i, a_start, a_end);
418
419 if (cplb_data[i].attr & D_CPLB)
420 __fill_data_cplbtab(t_d, i, a_start, a_end);
421 }
422 } 148 }
423 149 if (_ramend != physical_mem_end) {
424 /* make sure we locked the kernel start */ 150 /* Reserved memory. */
425 BUG_ON(cplb.init_i.pos < 2 + cplb_data[ZERO_P].valid); 151 icplb_bounds[i_i].eaddr = physical_mem_end;
426 BUG_ON(cplb.init_d.pos < 1 + cplb_data[ZERO_P].valid + cplb_data[L1D_MEM].valid); 152 icplb_bounds[i_i++].data = (reserved_mem_icache_on ?
427 153 SDRAM_IGENERIC : SDRAM_INON_CHBL);
428 /* make sure we didnt overflow the table */ 154 }
429 BUG_ON(cplb.init_i.size < cplb.init_i.pos); 155 /* Addressing hole up to BootROM. */
430 BUG_ON(cplb.init_d.size < cplb.init_d.pos); 156 icplb_bounds[i_i].eaddr = BOOT_ROM_START;
431 BUG_ON(cplb.switch_i.size < cplb.switch_i.pos); 157 icplb_bounds[i_i++].data = 0;
432 BUG_ON(cplb.switch_d.size < cplb.switch_d.pos); 158 /* BootROM -- largest one should be less than 1 meg. */
433 159 icplb_bounds[i_i].eaddr = BOOT_ROM_START + (1 * 1024 * 1024);
434 /* close tables */ 160 icplb_bounds[i_i++].data = SDRAM_IGENERIC;
435 close_cplbtab(&cplb.init_i); 161 if (L2_LENGTH) {
436 close_cplbtab(&cplb.init_d); 162 /* Addressing hole up to L2 SRAM, including the async bank. */
437 163 icplb_bounds[i_i].eaddr = L2_START;
438 cplb.init_i.tab[cplb.init_i.pos] = -1; 164 icplb_bounds[i_i++].data = 0;
439 cplb.init_d.tab[cplb.init_d.pos] = -1; 165 /* L2 SRAM. */
440 cplb.switch_i.tab[cplb.switch_i.pos] = -1; 166 icplb_bounds[i_i].eaddr = L2_START + L2_LENGTH;
441 cplb.switch_d.tab[cplb.switch_d.pos] = -1; 167 icplb_bounds[i_i++].data = L2_IMEMORY;
442 168 }
169 icplb_nr_bounds = i_i;
170 BUG_ON(icplb_nr_bounds > ARRAY_SIZE(icplb_bounds));
443} 171}
444
445#endif
diff --git a/arch/blackfin/kernel/cplb-nompu/cplbmgr.S b/arch/blackfin/kernel/cplb-nompu/cplbmgr.S
deleted file mode 100644
index f4ca76c72394..000000000000
--- a/arch/blackfin/kernel/cplb-nompu/cplbmgr.S
+++ /dev/null
@@ -1,648 +0,0 @@
1/*
2 * File: arch/blackfin/mach-common/cplbmgtr.S
3 * Based on:
4 * Author: LG Soft India
5 *
6 * Created: ?
7 * Description: CPLB replacement routine for CPLB mismatch
8 *
9 * Modified:
10 * Copyright 2004-2006 Analog Devices Inc.
11 *
12 * Bugs: Enter bugs at http://blackfin.uclinux.org/
13 *
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, see the file COPYING, or write
26 * to the Free Software Foundation, Inc.,
27 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
28 */
29
30/* Usage: int _cplb_mgr(is_data_miss,int enable_cache)
31 * is_data_miss==2 => Mark as Dirty, write to the clean data page
32 * is_data_miss==1 => Replace a data CPLB.
33 * is_data_miss==0 => Replace an instruction CPLB.
34 *
35 * Returns:
36 * CPLB_RELOADED => Successfully updated CPLB table.
37 * CPLB_NO_UNLOCKED => All CPLBs are locked, so cannot be evicted.
38 * This indicates that the CPLBs in the configuration
39 * tablei are badly configured, as this should never
40 * occur.
41 * CPLB_NO_ADDR_MATCH => The address being accessed, that triggered the
42 * exception, is not covered by any of the CPLBs in
43 * the configuration table. The application is
44 * presumably misbehaving.
45 * CPLB_PROT_VIOL => The address being accessed, that triggered the
46 * exception, was not a first-write to a clean Write
47 * Back Data page, and so presumably is a genuine
48 * violation of the page's protection attributes.
49 * The application is misbehaving.
50 */
51
52#include <linux/linkage.h>
53#include <asm/blackfin.h>
54#include <asm/cplb.h>
55#include <asm/asm-offsets.h>
56
57#ifdef CONFIG_EXCPT_IRQ_SYSC_L1
58.section .l1.text
59#else
60.text
61#endif
62
63.align 2;
64ENTRY(_cplb_mgr)
65
66 [--SP]=( R7:4,P5:3 );
67
68 CC = R0 == 2;
69 IF CC JUMP .Ldcplb_write;
70
71 CC = R0 == 0;
72 IF !CC JUMP .Ldcplb_miss_compare;
73
74 /* ICPLB Miss Exception. We need to choose one of the
75 * currently-installed CPLBs, and replace it with one
76 * from the configuration table.
77 */
78
79 /* A multi-word instruction can cross a page boundary. This means the
80 * first part of the instruction can be in a valid page, but the
81 * second part is not, and hence generates the instruction miss.
82 * However, the fault address is for the start of the instruction,
83 * not the part that's in the bad page. Therefore, we have to check
84 * whether the fault address applies to a page that is already present
85 * in the table.
86 */
87
88 P4.L = LO(ICPLB_FAULT_ADDR);
89 P4.H = HI(ICPLB_FAULT_ADDR);
90
91 P1 = 16;
92 P5.L = _page_size_table;
93 P5.H = _page_size_table;
94
95 P0.L = LO(ICPLB_DATA0);
96 P0.H = HI(ICPLB_DATA0);
97 R4 = [P4]; /* Get faulting address*/
98 R6 = 64; /* Advance past the fault address, which*/
99 R6 = R6 + R4; /* we'll use if we find a match*/
100 R3 = ((16 << 8) | 2); /* Extract mask, two bits at posn 16 */
101
102 R5 = 0;
103.Lisearch:
104
105 R1 = [P0-0x100]; /* Address for this CPLB */
106
107 R0 = [P0++]; /* Info for this CPLB*/
108 CC = BITTST(R0,0); /* Is the CPLB valid?*/
109 IF !CC JUMP .Lnomatch; /* Skip it, if not.*/
110 CC = R4 < R1(IU); /* If fault address less than page start*/
111 IF CC JUMP .Lnomatch; /* then skip this one.*/
112 R2 = EXTRACT(R0,R3.L) (Z); /* Get page size*/
113 P1 = R2;
114 P1 = P5 + (P1<<2); /* index into page-size table*/
115 R2 = [P1]; /* Get the page size*/
116 R1 = R1 + R2; /* and add to page start, to get page end*/
117 CC = R4 < R1(IU); /* and see whether fault addr is in page.*/
118 IF !CC R4 = R6; /* If so, advance the address and finish loop.*/
119 IF !CC JUMP .Lisearch_done;
120.Lnomatch:
121 /* Go around again*/
122 R5 += 1;
123 CC = BITTST(R5, 4); /* i.e CC = R5 >= 16*/
124 IF !CC JUMP .Lisearch;
125
126.Lisearch_done:
127 I0 = R4; /* Fault address we'll search for*/
128
129 /* set up pointers */
130 P0.L = LO(ICPLB_DATA0);
131 P0.H = HI(ICPLB_DATA0);
132
133 /* The replacement procedure for ICPLBs */
134
135 P4.L = LO(IMEM_CONTROL);
136 P4.H = HI(IMEM_CONTROL);
137
138 /* Turn off CPLBs while we work, necessary according to HRM before
139 * modifying CPLB descriptors
140 */
141 R5 = [P4]; /* Control Register*/
142 BITCLR(R5,ENICPLB_P);
143 CLI R1;
144 SSYNC; /* SSYNC required before writing to IMEM_CONTROL. */
145 .align 8;
146 [P4] = R5;
147 SSYNC;
148 STI R1;
149
150 R1 = -1; /* end point comparison */
151 R3 = 16; /* counter */
152
153 /* Search through CPLBs for first non-locked entry */
154 /* Overwrite it by moving everyone else up by 1 */
155.Licheck_lock:
156 R0 = [P0++];
157 R3 = R3 + R1;
158 CC = R3 == R1;
159 IF CC JUMP .Lall_locked;
160 CC = BITTST(R0, 0); /* an invalid entry is good */
161 IF !CC JUMP .Lifound_victim;
162 CC = BITTST(R0,1); /* but a locked entry isn't */
163 IF CC JUMP .Licheck_lock;
164
165.Lifound_victim:
166#ifdef CONFIG_CPLB_INFO
167 R7 = [P0 - 0x104];
168 GET_PDA(P2, R2);
169 P3 = [P2 + PDA_IPDT_SWAPCOUNT];
170 P2 = [P2 + PDA_IPDT];
171 P3 += -4;
172.Licount:
173 R2 = [P2]; /* address from config table */
174 P2 += 8;
175 P3 += 8;
176 CC = R2==-1;
177 IF CC JUMP .Licount_done;
178 CC = R7==R2;
179 IF !CC JUMP .Licount;
180 R7 = [P3];
181 R7 += 1;
182 [P3] = R7;
183 CSYNC;
184.Licount_done:
185#endif
186 LC0=R3;
187 LSETUP(.Lis_move,.Lie_move) LC0;
188.Lis_move:
189 R0 = [P0];
190 [P0 - 4] = R0;
191 R0 = [P0 - 0x100];
192 [P0-0x104] = R0;
193.Lie_move:
194 P0+=4;
195
196 /* Clear ICPLB_DATA15, in case we don't find a replacement
197 * otherwise, we would have a duplicate entry, and will crash
198 */
199 R0 = 0;
200 [P0 - 4] = R0;
201
202 /* We've made space in the ICPLB table, so that ICPLB15
203 * is now free to be overwritten. Next, we have to determine
204 * which CPLB we need to install, from the configuration
205 * table. This is a matter of getting the start-of-page
206 * addresses and page-lengths from the config table, and
207 * determining whether the fault address falls within that
208 * range.
209 */
210
211 GET_PDA(P3, R0);
212 P2 = [P3 + PDA_IPDT];
213#ifdef CONFIG_CPLB_INFO
214 P3 = [P3 + PDA_IPDT_SWAPCOUNT];
215 P3 += -8;
216#endif
217 P0.L = _page_size_table;
218 P0.H = _page_size_table;
219
220 /* Retrieve our fault address (which may have been advanced
221 * because the faulting instruction crossed a page boundary).
222 */
223
224 R0 = I0;
225
226 /* An extraction pattern, to get the page-size bits from
227 * the CPLB data entry. Bits 16-17, so two bits at posn 16.
228 */
229
230 R1 = ((16<<8)|2);
231.Linext: R4 = [P2++]; /* address from config table */
232 R2 = [P2++]; /* data from config table */
233#ifdef CONFIG_CPLB_INFO
234 P3 += 8;
235#endif
236
237 CC = R4 == -1; /* End of config table*/
238 IF CC JUMP .Lno_page_in_table;
239
240 /* See if failed address > start address */
241 CC = R4 <= R0(IU);
242 IF !CC JUMP .Linext;
243
244 /* extract page size (17:16)*/
245 R3 = EXTRACT(R2, R1.L) (Z);
246
247 /* add page size to addr to get range */
248
249 P5 = R3;
250 P5 = P0 + (P5 << 2); /* scaled, for int access*/
251 R3 = [P5];
252 R3 = R3 + R4;
253
254 /* See if failed address < (start address + page size) */
255 CC = R0 < R3(IU);
256 IF !CC JUMP .Linext;
257
258 /* We've found a CPLB in the config table that covers
259 * the faulting address, so install this CPLB into the
260 * last entry of the table.
261 */
262
263 P1.L = LO(ICPLB_DATA15); /* ICPLB_DATA15 */
264 P1.H = HI(ICPLB_DATA15);
265 [P1] = R2;
266 [P1-0x100] = R4;
267#ifdef CONFIG_CPLB_INFO
268 R3 = [P3];
269 R3 += 1;
270 [P3] = R3;
271#endif
272
273 /* P4 points to IMEM_CONTROL, and R5 contains its old
274 * value, after we disabled ICPLBS. Re-enable them.
275 */
276
277 BITSET(R5,ENICPLB_P);
278 CLI R2;
279 SSYNC; /* SSYNC required before writing to IMEM_CONTROL. */
280 .align 8;
281 [P4] = R5;
282 SSYNC;
283 STI R2;
284
285 ( R7:4,P5:3 ) = [SP++];
286 R0 = CPLB_RELOADED;
287 RTS;
288
289/* FAILED CASES*/
290.Lno_page_in_table:
291 R0 = CPLB_NO_ADDR_MATCH;
292 JUMP .Lfail_ret;
293
294.Lall_locked:
295 R0 = CPLB_NO_UNLOCKED;
296 JUMP .Lfail_ret;
297
298.Lprot_violation:
299 R0 = CPLB_PROT_VIOL;
300
301.Lfail_ret:
302 /* Make sure we turn protection/cache back on, even in the failing case */
303 BITSET(R5,ENICPLB_P);
304 CLI R2;
305 SSYNC; /* SSYNC required before writing to IMEM_CONTROL. */
306 .align 8;
307 [P4] = R5;
308 SSYNC;
309 STI R2;
310
311 ( R7:4,P5:3 ) = [SP++];
312 RTS;
313
314.Ldcplb_write:
315
316 /* if a DCPLB is marked as write-back (CPLB_WT==0), and
317 * it is clean (CPLB_DIRTY==0), then a write to the
318 * CPLB's page triggers a protection violation. We have to
319 * mark the CPLB as dirty, to indicate that there are
320 * pending writes associated with the CPLB.
321 */
322
323 P4.L = LO(DCPLB_STATUS);
324 P4.H = HI(DCPLB_STATUS);
325 P3.L = LO(DCPLB_DATA0);
326 P3.H = HI(DCPLB_DATA0);
327 R5 = [P4];
328
329 /* A protection violation can be caused by more than just writes
330 * to a clean WB page, so we have to ensure that:
331 * - It's a write
332 * - to a clean WB page
333 * - and is allowed in the mode the access occurred.
334 */
335
336 CC = BITTST(R5, 16); /* ensure it was a write*/
337 IF !CC JUMP .Lprot_violation;
338
339 /* to check the rest, we have to retrieve the DCPLB.*/
340
341 /* The low half of DCPLB_STATUS is a bit mask*/
342
343 R2 = R5.L (Z); /* indicating which CPLB triggered the event.*/
344 R3 = 30; /* so we can use this to determine the offset*/
345 R2.L = SIGNBITS R2;
346 R2 = R2.L (Z); /* into the DCPLB table.*/
347 R3 = R3 - R2;
348 P4 = R3;
349 P3 = P3 + (P4<<2);
350 R3 = [P3]; /* Retrieve the CPLB*/
351
352 /* Now we can check whether it's a clean WB page*/
353
354 CC = BITTST(R3, 14); /* 0==WB, 1==WT*/
355 IF CC JUMP .Lprot_violation;
356 CC = BITTST(R3, 7); /* 0 == clean, 1 == dirty*/
357 IF CC JUMP .Lprot_violation;
358
359 /* Check whether the write is allowed in the mode that was active.*/
360
361 R2 = 1<<3; /* checking write in user mode*/
362 CC = BITTST(R5, 17); /* 0==was user, 1==was super*/
363 R5 = CC;
364 R2 <<= R5; /* if was super, check write in super mode*/
365 R2 = R3 & R2;
366 CC = R2 == 0;
367 IF CC JUMP .Lprot_violation;
368
369 /* It's a genuine write-to-clean-page.*/
370
371 BITSET(R3, 7); /* mark as dirty*/
372 [P3] = R3; /* and write back.*/
373 NOP;
374 CSYNC;
375 ( R7:4,P5:3 ) = [SP++];
376 R0 = CPLB_RELOADED;
377 RTS;
378
379.Ldcplb_miss_compare:
380
381 /* Data CPLB Miss event. We need to choose a CPLB to
382 * evict, and then locate a new CPLB to install from the
383 * config table, that covers the faulting address.
384 */
385
386 P1.L = LO(DCPLB_DATA15);
387 P1.H = HI(DCPLB_DATA15);
388
389 P4.L = LO(DCPLB_FAULT_ADDR);
390 P4.H = HI(DCPLB_FAULT_ADDR);
391 R4 = [P4];
392 I0 = R4;
393
394 /* The replacement procedure for DCPLBs*/
395
396 R6 = R1; /* Save for later*/
397
398 /* Turn off CPLBs while we work.*/
399 P4.L = LO(DMEM_CONTROL);
400 P4.H = HI(DMEM_CONTROL);
401 R5 = [P4];
402 BITCLR(R5,ENDCPLB_P);
403 CLI R0;
404 SSYNC; /* SSYNC required before writing to DMEM_CONTROL. */
405 .align 8;
406 [P4] = R5;
407 SSYNC;
408 STI R0;
409
410 /* Start looking for a CPLB to evict. Our order of preference
411 * is: invalid CPLBs, clean CPLBs, dirty CPLBs. Locked CPLBs
412 * are no good.
413 */
414
415 I1.L = LO(DCPLB_DATA0);
416 I1.H = HI(DCPLB_DATA0);
417 P1 = 2;
418 P2 = 16;
419 I2.L = _dcplb_preference;
420 I2.H = _dcplb_preference;
421 LSETUP(.Lsdsearch1, .Ledsearch1) LC0 = P1;
422.Lsdsearch1:
423 R0 = [I2++]; /* Get the bits we're interested in*/
424 P0 = I1; /* Go back to start of table*/
425 LSETUP (.Lsdsearch2, .Ledsearch2) LC1 = P2;
426.Lsdsearch2:
427 R1 = [P0++]; /* Fetch each installed CPLB in turn*/
428 R2 = R1 & R0; /* and test for interesting bits.*/
429 CC = R2 == 0; /* If none are set, it'll do.*/
430 IF !CC JUMP .Lskip_stack_check;
431
432 R2 = [P0 - 0x104]; /* R2 - PageStart */
433 P3.L = _page_size_table; /* retrieve end address */
434 P3.H = _page_size_table; /* retrieve end address */
435 R3 = 0x1002; /* 16th - position, 2 bits -length */
436#if ANOMALY_05000209
437 nop; /* Anomaly 05000209 */
438#endif
439 R7 = EXTRACT(R1,R3.l);
440 R7 = R7 << 2; /* Page size index offset */
441 P5 = R7;
442 P3 = P3 + P5;
443 R7 = [P3]; /* page size in bytes */
444
445 R7 = R2 + R7; /* R7 - PageEnd */
446 R4 = SP; /* Test SP is in range */
447
448 CC = R7 < R4; /* if PageEnd < SP */
449 IF CC JUMP .Ldfound_victim;
450 R3 = 0x284; /* stack length from start of trap till
451 * the point.
452 * 20 stack locations for future modifications
453 */
454 R4 = R4 + R3;
455 CC = R4 < R2; /* if SP + stacklen < PageStart */
456 IF CC JUMP .Ldfound_victim;
457.Lskip_stack_check:
458
459.Ledsearch2: NOP;
460.Ledsearch1: NOP;
461
462 /* If we got here, we didn't find a DCPLB we considered
463 * replacable, which means all of them were locked.
464 */
465
466 JUMP .Lall_locked;
467.Ldfound_victim:
468
469#ifdef CONFIG_CPLB_INFO
470 R7 = [P0 - 0x104];
471 GET_PDA(P2, R2);
472 P3 = [P2 + PDA_DPDT_SWAPCOUNT];
473 P2 = [P2 + PDA_DPDT];
474 P3 += -4;
475.Ldicount:
476 R2 = [P2];
477 P2 += 8;
478 P3 += 8;
479 CC = R2==-1;
480 IF CC JUMP .Ldicount_done;
481 CC = R7==R2;
482 IF !CC JUMP .Ldicount;
483 R7 = [P3];
484 R7 += 1;
485 [P3] = R7;
486.Ldicount_done:
487#endif
488
489 /* Clean down the hardware loops*/
490 R2 = 0;
491 LC1 = R2;
492 LC0 = R2;
493
494 /* There's a suitable victim in [P0-4] (because we've
495 * advanced already).
496 */
497
498.LDdoverwrite:
499
500 /* [P0-4] is a suitable victim CPLB, so we want to
501 * overwrite it by moving all the following CPLBs
502 * one space closer to the start.
503 */
504
505 R1.L = LO(DCPLB_DATA16); /* DCPLB_DATA15 + 4 */
506 R1.H = HI(DCPLB_DATA16);
507 R0 = P0;
508
509 /* If the victim happens to be in DCPLB15,
510 * we don't need to move anything.
511 */
512
513 CC = R1 == R0;
514 IF CC JUMP .Lde_moved;
515 R1 = R1 - R0;
516 R1 >>= 2;
517 P1 = R1;
518 LSETUP(.Lds_move, .Lde_move) LC0=P1;
519.Lds_move:
520 R0 = [P0++]; /* move data */
521 [P0 - 8] = R0;
522 R0 = [P0-0x104] /* move address */
523.Lde_move:
524 [P0-0x108] = R0;
525
526.Lde_moved:
527 NOP;
528
529 /* Clear DCPLB_DATA15, in case we don't find a replacement
530 * otherwise, we would have a duplicate entry, and will crash
531 */
532 R0 = 0;
533 [P0 - 0x4] = R0;
534
535 /* We've now made space in DCPLB15 for the new CPLB to be
536 * installed. The next stage is to locate a CPLB in the
537 * config table that covers the faulting address.
538 */
539
540 R0 = I0; /* Our faulting address */
541
542 GET_PDA(P3, R1);
543 P2 = [P3 + PDA_DPDT];
544#ifdef CONFIG_CPLB_INFO
545 P3 = [P3 + PDA_DPDT_SWAPCOUNT];
546 P3 += -8;
547#endif
548
549 P1.L = _page_size_table;
550 P1.H = _page_size_table;
551
552 /* An extraction pattern, to retrieve bits 17:16.*/
553
554 R1 = (16<<8)|2;
555.Ldnext: R4 = [P2++]; /* address */
556 R2 = [P2++]; /* data */
557#ifdef CONFIG_CPLB_INFO
558 P3 += 8;
559#endif
560
561 CC = R4 == -1;
562 IF CC JUMP .Lno_page_in_table;
563
564 /* See if failed address > start address */
565 CC = R4 <= R0(IU);
566 IF !CC JUMP .Ldnext;
567
568 /* extract page size (17:16)*/
569 R3 = EXTRACT(R2, R1.L) (Z);
570
571 /* add page size to addr to get range */
572
573 P5 = R3;
574 P5 = P1 + (P5 << 2);
575 R3 = [P5];
576 R3 = R3 + R4;
577
578 /* See if failed address < (start address + page size) */
579 CC = R0 < R3(IU);
580 IF !CC JUMP .Ldnext;
581
582 /* We've found the CPLB that should be installed, so
583 * write it into CPLB15, masking off any caching bits
584 * if necessary.
585 */
586
587 P1.L = LO(DCPLB_DATA15);
588 P1.H = HI(DCPLB_DATA15);
589
590 /* If the DCPLB has cache bits set, but caching hasn't
591 * been enabled, then we want to mask off the cache-in-L1
592 * bit before installing. Moreover, if caching is off, we
593 * also want to ensure that the DCPLB has WT mode set, rather
594 * than WB, since WB pages still trigger first-write exceptions
595 * even when not caching is off, and the page isn't marked as
596 * cachable. Finally, we could mark the page as clean, not dirty,
597 * but we choose to leave that decision to the user; if the user
598 * chooses to have a CPLB pre-defined as dirty, then they always
599 * pay the cost of flushing during eviction, but don't pay the
600 * cost of first-write exceptions to mark the page as dirty.
601 */
602
603#ifdef CONFIG_BFIN_WT
604 BITSET(R6, 14); /* Set WT*/
605#endif
606
607 [P1] = R2;
608 [P1-0x100] = R4;
609#ifdef CONFIG_CPLB_INFO
610 R3 = [P3];
611 R3 += 1;
612 [P3] = R3;
613#endif
614
615 /* We've installed the CPLB, so re-enable CPLBs. P4
616 * points to DMEM_CONTROL, and R5 is the value we
617 * last wrote to it, when we were disabling CPLBs.
618 */
619
620 BITSET(R5,ENDCPLB_P);
621 CLI R2;
622 .align 8;
623 [P4] = R5;
624 SSYNC;
625 STI R2;
626
627 ( R7:4,P5:3 ) = [SP++];
628 R0 = CPLB_RELOADED;
629 RTS;
630ENDPROC(_cplb_mgr)
631
632#ifdef CONFIG_CPLB_SWITCH_TAB_L1
633.section .l1.data
634#else
635.data
636#endif
637
638ENTRY(_page_size_table)
639.byte4 0x00000400; /* 1K */
640.byte4 0x00001000; /* 4K */
641.byte4 0x00100000; /* 1M */
642.byte4 0x00400000; /* 4M */
643END(_page_size_table)
644
645ENTRY(_dcplb_preference)
646.byte4 0x00000001; /* valid bit */
647.byte4 0x00000002; /* lock bit */
648END(_dcplb_preference)
diff --git a/arch/blackfin/kernel/cplb-nompu/cplbmgr.c b/arch/blackfin/kernel/cplb-nompu/cplbmgr.c
new file mode 100644
index 000000000000..376249ab2694
--- /dev/null
+++ b/arch/blackfin/kernel/cplb-nompu/cplbmgr.c
@@ -0,0 +1,283 @@
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
32/*
33 * WARNING
34 *
35 * This file is compiled with certain -ffixed-reg options. We have to
36 * make sure not to call any functions here that could clobber these
37 * registers.
38 */
39
40int nr_dcplb_miss[NR_CPUS], nr_icplb_miss[NR_CPUS];
41int nr_dcplb_supv_miss[NR_CPUS], nr_icplb_supv_miss[NR_CPUS];
42int nr_cplb_flush[NR_CPUS], nr_dcplb_prot[NR_CPUS];
43
44#ifdef CONFIG_EXCPT_IRQ_SYSC_L1
45#define MGR_ATTR __attribute__((l1_text))
46#else
47#define MGR_ATTR
48#endif
49
50/*
51 * We're in an exception handler. The normal cli nop nop workaround
52 * isn't going to do very much, as the only thing that can interrupt
53 * us is an NMI, and the cli isn't going to stop that.
54 */
55#define NOWA_SSYNC __asm__ __volatile__ ("ssync;")
56
57/* Anomaly handlers provide SSYNCs, so avoid extra if anomaly is present */
58#if ANOMALY_05000125
59
60#define bfin_write_DMEM_CONTROL_SSYNC(v) bfin_write_DMEM_CONTROL(v)
61#define bfin_write_IMEM_CONTROL_SSYNC(v) bfin_write_IMEM_CONTROL(v)
62
63#else
64
65#define bfin_write_DMEM_CONTROL_SSYNC(v) \
66 do { NOWA_SSYNC; bfin_write_DMEM_CONTROL(v); NOWA_SSYNC; } while (0)
67#define bfin_write_IMEM_CONTROL_SSYNC(v) \
68 do { NOWA_SSYNC; bfin_write_IMEM_CONTROL(v); NOWA_SSYNC; } while (0)
69
70#endif
71
72static inline void write_dcplb_data(int cpu, int idx, unsigned long data,
73 unsigned long addr)
74{
75 unsigned long ctrl = bfin_read_DMEM_CONTROL();
76 bfin_write_DMEM_CONTROL_SSYNC(ctrl & ~ENDCPLB);
77 bfin_write32(DCPLB_DATA0 + idx * 4, data);
78 bfin_write32(DCPLB_ADDR0 + idx * 4, addr);
79 bfin_write_DMEM_CONTROL_SSYNC(ctrl);
80
81#ifdef CONFIG_CPLB_INFO
82 dcplb_tbl[cpu][idx].addr = addr;
83 dcplb_tbl[cpu][idx].data = data;
84#endif
85}
86
87static inline void write_icplb_data(int cpu, int idx, unsigned long data,
88 unsigned long addr)
89{
90 unsigned long ctrl = bfin_read_IMEM_CONTROL();
91
92 bfin_write_IMEM_CONTROL_SSYNC(ctrl & ~ENICPLB);
93 bfin_write32(ICPLB_DATA0 + idx * 4, data);
94 bfin_write32(ICPLB_ADDR0 + idx * 4, addr);
95 bfin_write_IMEM_CONTROL_SSYNC(ctrl);
96
97#ifdef CONFIG_CPLB_INFO
98 icplb_tbl[cpu][idx].addr = addr;
99 icplb_tbl[cpu][idx].data = data;
100#endif
101}
102
103/*
104 * Given the contents of the status register, return the index of the
105 * CPLB that caused the fault.
106 */
107static inline int faulting_cplb_index(int status)
108{
109 int signbits = __builtin_bfin_norm_fr1x32(status & 0xFFFF);
110 return 30 - signbits;
111}
112
113/*
114 * Given the contents of the status register and the DCPLB_DATA contents,
115 * return true if a write access should be permitted.
116 */
117static inline int write_permitted(int status, unsigned long data)
118{
119 if (status & FAULT_USERSUPV)
120 return !!(data & CPLB_SUPV_WR);
121 else
122 return !!(data & CPLB_USER_WR);
123}
124
125/* Counters to implement round-robin replacement. */
126static int icplb_rr_index[NR_CPUS] PDT_ATTR;
127static int dcplb_rr_index[NR_CPUS] PDT_ATTR;
128
129/*
130 * Find an ICPLB entry to be evicted and return its index.
131 */
132static int evict_one_icplb(int cpu)
133{
134 int i = first_switched_icplb + icplb_rr_index[cpu];
135 if (i >= MAX_CPLBS) {
136 i -= MAX_CPLBS - first_switched_icplb;
137 icplb_rr_index[cpu] -= MAX_CPLBS - first_switched_icplb;
138 }
139 icplb_rr_index[cpu]++;
140 return i;
141}
142
143static int evict_one_dcplb(int cpu)
144{
145 int i = first_switched_dcplb + dcplb_rr_index[cpu];
146 if (i >= MAX_CPLBS) {
147 i -= MAX_CPLBS - first_switched_dcplb;
148 dcplb_rr_index[cpu] -= MAX_CPLBS - first_switched_dcplb;
149 }
150 dcplb_rr_index[cpu]++;
151 return i;
152}
153
154MGR_ATTR static int icplb_miss(int cpu)
155{
156 unsigned long addr = bfin_read_ICPLB_FAULT_ADDR();
157 int status = bfin_read_ICPLB_STATUS();
158 int idx;
159 unsigned long i_data, base, addr1, eaddr;
160
161 nr_icplb_miss[cpu]++;
162 if (unlikely(status & FAULT_USERSUPV))
163 nr_icplb_supv_miss[cpu]++;
164
165 base = 0;
166 for (idx = 0; idx < icplb_nr_bounds; idx++) {
167 eaddr = icplb_bounds[idx].eaddr;
168 if (addr < eaddr)
169 break;
170 base = eaddr;
171 }
172 if (unlikely(idx == icplb_nr_bounds))
173 return CPLB_NO_ADDR_MATCH;
174
175 i_data = icplb_bounds[idx].data;
176 if (unlikely(i_data == 0))
177 return CPLB_NO_ADDR_MATCH;
178
179 addr1 = addr & ~(SIZE_4M - 1);
180 addr &= ~(SIZE_1M - 1);
181 i_data |= PAGE_SIZE_1MB;
182 if (addr1 >= base && (addr1 + SIZE_4M) <= eaddr) {
183 /*
184 * This works because
185 * (PAGE_SIZE_4MB & PAGE_SIZE_1MB) == PAGE_SIZE_1MB.
186 */
187 i_data |= PAGE_SIZE_4MB;
188 addr = addr1;
189 }
190
191 /* Pick entry to evict */
192 idx = evict_one_icplb(cpu);
193
194 write_icplb_data(cpu, idx, i_data, addr);
195
196 return CPLB_RELOADED;
197}
198
199MGR_ATTR static int dcplb_miss(int cpu)
200{
201 unsigned long addr = bfin_read_DCPLB_FAULT_ADDR();
202 int status = bfin_read_DCPLB_STATUS();
203 int idx;
204 unsigned long d_data, base, addr1, eaddr;
205
206 nr_dcplb_miss[cpu]++;
207 if (unlikely(status & FAULT_USERSUPV))
208 nr_dcplb_supv_miss[cpu]++;
209
210 base = 0;
211 for (idx = 0; idx < dcplb_nr_bounds; idx++) {
212 eaddr = dcplb_bounds[idx].eaddr;
213 if (addr < eaddr)
214 break;
215 base = eaddr;
216 }
217 if (unlikely(idx == dcplb_nr_bounds))
218 return CPLB_NO_ADDR_MATCH;
219
220 d_data = dcplb_bounds[idx].data;
221 if (unlikely(d_data == 0))
222 return CPLB_NO_ADDR_MATCH;
223
224 addr1 = addr & ~(SIZE_4M - 1);
225 addr &= ~(SIZE_1M - 1);
226 d_data |= PAGE_SIZE_1MB;
227 if (addr1 >= base && (addr1 + SIZE_4M) <= eaddr) {
228 /*
229 * This works because
230 * (PAGE_SIZE_4MB & PAGE_SIZE_1MB) == PAGE_SIZE_1MB.
231 */
232 d_data |= PAGE_SIZE_4MB;
233 addr = addr1;
234 }
235
236 /* Pick entry to evict */
237 idx = evict_one_dcplb(cpu);
238
239 write_dcplb_data(cpu, idx, d_data, addr);
240
241 return CPLB_RELOADED;
242}
243
244MGR_ATTR static noinline int dcplb_protection_fault(int cpu)
245{
246 int status = bfin_read_DCPLB_STATUS();
247
248 nr_dcplb_prot[cpu]++;
249
250 if (likely(status & FAULT_RW)) {
251 int idx = faulting_cplb_index(status);
252 unsigned long regaddr = DCPLB_DATA0 + idx * 4;
253 unsigned long data = bfin_read32(regaddr);
254
255 /* Check if fault is to dirty a clean page */
256 if (!(data & CPLB_WT) && !(data & CPLB_DIRTY) &&
257 write_permitted(status, data)) {
258
259 dcplb_tbl[cpu][idx].data = data;
260 bfin_write32(regaddr, data);
261 return CPLB_RELOADED;
262 }
263 }
264
265 return CPLB_PROT_VIOL;
266}
267
268MGR_ATTR int cplb_hdr(int seqstat, struct pt_regs *regs)
269{
270 int cause = seqstat & 0x3f;
271 unsigned int cpu = smp_processor_id();
272 switch (cause) {
273 case 0x2C:
274 return icplb_miss(cpu);
275 case 0x26:
276 return dcplb_miss(cpu);
277 default:
278 if (unlikely(cause == 0x23))
279 return dcplb_protection_fault(cpu);
280
281 return CPLB_UNKNOWN_ERR;
282 }
283}