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>
7 * Description: CPLB miss handler.
10 * Copyright 2008 Airvana Inc.
11 * Copyright 2004-2007 Analog Devices Inc.
13 * Bugs: Enter bugs at http://blackfin.uclinux.org/
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.
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.
26 #include <linux/kernel.h>
27 #include <asm/blackfin.h>
28 #include <asm/cplbinit.h>
30 #include <asm/mmu_context.h>
31 #include <asm/traps.h>
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
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
];
45 #ifdef CONFIG_EXCPT_IRQ_SYSC_L1
46 #define MGR_ATTR __attribute__((l1_text))
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.
56 #define NOWA_SSYNC __asm__ __volatile__ ("ssync;")
58 /* Anomaly handlers provide SSYNCs, so avoid extra if anomaly is present */
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)
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)
73 static inline void write_dcplb_data(int cpu
, int idx
, unsigned long data
,
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
);
82 #ifdef CONFIG_CPLB_INFO
83 dcplb_tbl
[cpu
][idx
].addr
= addr
;
84 dcplb_tbl
[cpu
][idx
].data
= data
;
88 static inline void write_icplb_data(int cpu
, int idx
, unsigned long data
,
91 unsigned long ctrl
= bfin_read_IMEM_CONTROL();
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
);
98 #ifdef CONFIG_CPLB_INFO
99 icplb_tbl
[cpu
][idx
].addr
= addr
;
100 icplb_tbl
[cpu
][idx
].data
= data
;
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
;
109 * Find an ICPLB entry to be evicted and return its index.
111 static int evict_one_icplb(int cpu
)
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
;
118 icplb_rr_index
[cpu
]++;
122 static int evict_one_dcplb(int cpu
)
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
;
129 dcplb_rr_index
[cpu
]++;
133 MGR_ATTR
static int icplb_miss(int cpu
)
135 unsigned long addr
= bfin_read_ICPLB_FAULT_ADDR();
136 int status
= bfin_read_ICPLB_STATUS();
138 unsigned long i_data
, base
, addr1
, eaddr
;
140 nr_icplb_miss
[cpu
]++;
141 if (unlikely(status
& FAULT_USERSUPV
))
142 nr_icplb_supv_miss
[cpu
]++;
147 eaddr
= icplb_bounds
[idx
].eaddr
;
151 } while (++idx
< icplb_nr_bounds
);
153 if (unlikely(idx
== icplb_nr_bounds
))
154 return CPLB_NO_ADDR_MATCH
;
156 i_data
= icplb_bounds
[idx
].data
;
157 if (unlikely(i_data
== 0))
158 return CPLB_NO_ADDR_MATCH
;
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
) {
166 * (PAGE_SIZE_4MB & PAGE_SIZE_1MB) == PAGE_SIZE_1MB.
168 i_data
|= PAGE_SIZE_4MB
;
172 /* Pick entry to evict */
173 idx
= evict_one_icplb(cpu
);
175 write_icplb_data(cpu
, idx
, i_data
, addr
);
177 return CPLB_RELOADED
;
180 MGR_ATTR
static int dcplb_miss(int cpu
)
182 unsigned long addr
= bfin_read_DCPLB_FAULT_ADDR();
183 int status
= bfin_read_DCPLB_STATUS();
185 unsigned long d_data
, base
, addr1
, eaddr
;
187 nr_dcplb_miss
[cpu
]++;
188 if (unlikely(status
& FAULT_USERSUPV
))
189 nr_dcplb_supv_miss
[cpu
]++;
194 eaddr
= dcplb_bounds
[idx
].eaddr
;
198 } while (++idx
< dcplb_nr_bounds
);
200 if (unlikely(idx
== dcplb_nr_bounds
))
201 return CPLB_NO_ADDR_MATCH
;
203 d_data
= dcplb_bounds
[idx
].data
;
204 if (unlikely(d_data
== 0))
205 return CPLB_NO_ADDR_MATCH
;
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
) {
213 * (PAGE_SIZE_4MB & PAGE_SIZE_1MB) == PAGE_SIZE_1MB.
215 d_data
|= PAGE_SIZE_4MB
;
219 /* Pick entry to evict */
220 idx
= evict_one_dcplb(cpu
);
222 write_dcplb_data(cpu
, idx
, d_data
, addr
);
224 return CPLB_RELOADED
;
227 MGR_ATTR
int cplb_hdr(int seqstat
, struct pt_regs
*regs
)
229 int cause
= seqstat
& 0x3f;
230 unsigned int cpu
= smp_processor_id();
233 return icplb_miss(cpu
);
235 return dcplb_miss(cpu
);
237 return CPLB_UNKNOWN_ERR
;