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>
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
40 int nr_dcplb_miss
[NR_CPUS
], nr_icplb_miss
[NR_CPUS
];
41 int nr_dcplb_supv_miss
[NR_CPUS
], nr_icplb_supv_miss
[NR_CPUS
];
42 int nr_cplb_flush
[NR_CPUS
], nr_dcplb_prot
[NR_CPUS
];
44 #ifdef CONFIG_EXCPT_IRQ_SYSC_L1
45 #define MGR_ATTR __attribute__((l1_text))
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.
55 #define NOWA_SSYNC __asm__ __volatile__ ("ssync;")
57 /* Anomaly handlers provide SSYNCs, so avoid extra if anomaly is present */
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)
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)
72 static inline void write_dcplb_data(int cpu
, int idx
, unsigned long data
,
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
);
81 #ifdef CONFIG_CPLB_INFO
82 dcplb_tbl
[cpu
][idx
].addr
= addr
;
83 dcplb_tbl
[cpu
][idx
].data
= data
;
87 static inline void write_icplb_data(int cpu
, int idx
, unsigned long data
,
90 unsigned long ctrl
= bfin_read_IMEM_CONTROL();
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
);
97 #ifdef CONFIG_CPLB_INFO
98 icplb_tbl
[cpu
][idx
].addr
= addr
;
99 icplb_tbl
[cpu
][idx
].data
= data
;
104 * Given the contents of the status register, return the index of the
105 * CPLB that caused the fault.
107 static inline int faulting_cplb_index(int status
)
109 int signbits
= __builtin_bfin_norm_fr1x32(status
& 0xFFFF);
110 return 30 - signbits
;
114 * Given the contents of the status register and the DCPLB_DATA contents,
115 * return true if a write access should be permitted.
117 static inline int write_permitted(int status
, unsigned long data
)
119 if (status
& FAULT_USERSUPV
)
120 return !!(data
& CPLB_SUPV_WR
);
122 return !!(data
& CPLB_USER_WR
);
125 /* Counters to implement round-robin replacement. */
126 static int icplb_rr_index
[NR_CPUS
] PDT_ATTR
;
127 static int dcplb_rr_index
[NR_CPUS
] PDT_ATTR
;
130 * Find an ICPLB entry to be evicted and return its index.
132 static int evict_one_icplb(int cpu
)
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
;
139 icplb_rr_index
[cpu
]++;
143 static int evict_one_dcplb(int cpu
)
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
;
150 dcplb_rr_index
[cpu
]++;
154 MGR_ATTR
static int icplb_miss(int cpu
)
156 unsigned long addr
= bfin_read_ICPLB_FAULT_ADDR();
157 int status
= bfin_read_ICPLB_STATUS();
159 unsigned long i_data
, base
, addr1
, eaddr
;
161 nr_icplb_miss
[cpu
]++;
162 if (unlikely(status
& FAULT_USERSUPV
))
163 nr_icplb_supv_miss
[cpu
]++;
168 eaddr
= icplb_bounds
[idx
].eaddr
;
172 } while (++idx
< icplb_nr_bounds
);
174 if (unlikely(idx
== icplb_nr_bounds
))
175 return CPLB_NO_ADDR_MATCH
;
177 i_data
= icplb_bounds
[idx
].data
;
178 if (unlikely(i_data
== 0))
179 return CPLB_NO_ADDR_MATCH
;
181 addr1
= addr
& ~(SIZE_4M
- 1);
182 addr
&= ~(SIZE_1M
- 1);
183 i_data
|= PAGE_SIZE_1MB
;
184 if (addr1
>= base
&& (addr1
+ SIZE_4M
) <= eaddr
) {
187 * (PAGE_SIZE_4MB & PAGE_SIZE_1MB) == PAGE_SIZE_1MB.
189 i_data
|= PAGE_SIZE_4MB
;
193 /* Pick entry to evict */
194 idx
= evict_one_icplb(cpu
);
196 write_icplb_data(cpu
, idx
, i_data
, addr
);
198 return CPLB_RELOADED
;
201 MGR_ATTR
static int dcplb_miss(int cpu
)
203 unsigned long addr
= bfin_read_DCPLB_FAULT_ADDR();
204 int status
= bfin_read_DCPLB_STATUS();
206 unsigned long d_data
, base
, addr1
, eaddr
;
208 nr_dcplb_miss
[cpu
]++;
209 if (unlikely(status
& FAULT_USERSUPV
))
210 nr_dcplb_supv_miss
[cpu
]++;
215 eaddr
= dcplb_bounds
[idx
].eaddr
;
219 } while (++idx
< dcplb_nr_bounds
);
221 if (unlikely(idx
== dcplb_nr_bounds
))
222 return CPLB_NO_ADDR_MATCH
;
224 d_data
= dcplb_bounds
[idx
].data
;
225 if (unlikely(d_data
== 0))
226 return CPLB_NO_ADDR_MATCH
;
228 addr1
= addr
& ~(SIZE_4M
- 1);
229 addr
&= ~(SIZE_1M
- 1);
230 d_data
|= PAGE_SIZE_1MB
;
231 if (addr1
>= base
&& (addr1
+ SIZE_4M
) <= eaddr
) {
234 * (PAGE_SIZE_4MB & PAGE_SIZE_1MB) == PAGE_SIZE_1MB.
236 d_data
|= PAGE_SIZE_4MB
;
240 /* Pick entry to evict */
241 idx
= evict_one_dcplb(cpu
);
243 write_dcplb_data(cpu
, idx
, d_data
, addr
);
245 return CPLB_RELOADED
;
248 MGR_ATTR
static noinline
int dcplb_protection_fault(int cpu
)
250 int status
= bfin_read_DCPLB_STATUS();
252 nr_dcplb_prot
[cpu
]++;
254 if (likely(status
& FAULT_RW
)) {
255 int idx
= faulting_cplb_index(status
);
256 unsigned long regaddr
= DCPLB_DATA0
+ idx
* 4;
257 unsigned long data
= bfin_read32(regaddr
);
259 /* Check if fault is to dirty a clean page */
260 if (!(data
& CPLB_WT
) && !(data
& CPLB_DIRTY
) &&
261 write_permitted(status
, data
)) {
263 dcplb_tbl
[cpu
][idx
].data
= data
;
264 bfin_write32(regaddr
, data
);
265 return CPLB_RELOADED
;
269 return CPLB_PROT_VIOL
;
272 MGR_ATTR
int cplb_hdr(int seqstat
, struct pt_regs
*regs
)
274 int cause
= seqstat
& 0x3f;
275 unsigned int cpu
= smp_processor_id();
278 return icplb_miss(cpu
);
280 return dcplb_miss(cpu
);
282 if (unlikely(cause
== 0x23))
283 return dcplb_protection_fault(cpu
);
285 return CPLB_UNKNOWN_ERR
;