2 * Based on: arch/blackfin/kernel/cplb-mpu/cplbmgr.c
3 * Author: Michael McTernan <mmcternan@airvana.com>
5 * Description: CPLB miss handler.
8 * Copyright 2008 Airvana Inc.
9 * Copyright 2008-2009 Analog Devices Inc.
11 * Licensed under the GPL-2 or later
14 #include <linux/kernel.h>
15 #include <asm/blackfin.h>
16 #include <asm/cplbinit.h>
18 #include <asm/mmu_context.h>
19 #include <asm/traps.h>
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
29 int nr_dcplb_miss
[NR_CPUS
], nr_icplb_miss
[NR_CPUS
];
30 int nr_dcplb_supv_miss
[NR_CPUS
], nr_icplb_supv_miss
[NR_CPUS
];
31 int nr_cplb_flush
[NR_CPUS
], nr_dcplb_prot
[NR_CPUS
];
33 #ifdef CONFIG_EXCPT_IRQ_SYSC_L1
34 #define MGR_ATTR __attribute__((l1_text))
39 static inline void write_dcplb_data(int cpu
, int idx
, unsigned long data
,
43 bfin_write32(DCPLB_DATA0
+ idx
* 4, data
);
44 bfin_write32(DCPLB_ADDR0
+ idx
* 4, addr
);
47 #ifdef CONFIG_CPLB_INFO
48 dcplb_tbl
[cpu
][idx
].addr
= addr
;
49 dcplb_tbl
[cpu
][idx
].data
= data
;
53 static inline void write_icplb_data(int cpu
, int idx
, unsigned long data
,
57 bfin_write32(ICPLB_DATA0
+ idx
* 4, data
);
58 bfin_write32(ICPLB_ADDR0
+ idx
* 4, addr
);
61 #ifdef CONFIG_CPLB_INFO
62 icplb_tbl
[cpu
][idx
].addr
= addr
;
63 icplb_tbl
[cpu
][idx
].data
= data
;
67 /* Counters to implement round-robin replacement. */
68 static int icplb_rr_index
[NR_CPUS
] PDT_ATTR
;
69 static int dcplb_rr_index
[NR_CPUS
] PDT_ATTR
;
72 * Find an ICPLB entry to be evicted and return its index.
74 static int evict_one_icplb(int cpu
)
76 int i
= first_switched_icplb
+ icplb_rr_index
[cpu
];
78 i
-= MAX_CPLBS
- first_switched_icplb
;
79 icplb_rr_index
[cpu
] -= MAX_CPLBS
- first_switched_icplb
;
81 icplb_rr_index
[cpu
]++;
85 static int evict_one_dcplb(int cpu
)
87 int i
= first_switched_dcplb
+ dcplb_rr_index
[cpu
];
89 i
-= MAX_CPLBS
- first_switched_dcplb
;
90 dcplb_rr_index
[cpu
] -= MAX_CPLBS
- first_switched_dcplb
;
92 dcplb_rr_index
[cpu
]++;
96 MGR_ATTR
static int icplb_miss(int cpu
)
98 unsigned long addr
= bfin_read_ICPLB_FAULT_ADDR();
99 int status
= bfin_read_ICPLB_STATUS();
101 unsigned long i_data
, base
, addr1
, eaddr
;
103 nr_icplb_miss
[cpu
]++;
104 if (unlikely(status
& FAULT_USERSUPV
))
105 nr_icplb_supv_miss
[cpu
]++;
110 eaddr
= icplb_bounds
[idx
].eaddr
;
114 } while (++idx
< icplb_nr_bounds
);
116 if (unlikely(idx
== icplb_nr_bounds
))
117 return CPLB_NO_ADDR_MATCH
;
119 i_data
= icplb_bounds
[idx
].data
;
120 if (unlikely(i_data
== 0))
121 return CPLB_NO_ADDR_MATCH
;
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
) {
129 * (PAGE_SIZE_4MB & PAGE_SIZE_1MB) == PAGE_SIZE_1MB.
131 i_data
|= PAGE_SIZE_4MB
;
135 /* Pick entry to evict */
136 idx
= evict_one_icplb(cpu
);
138 write_icplb_data(cpu
, idx
, i_data
, addr
);
140 return CPLB_RELOADED
;
143 MGR_ATTR
static int dcplb_miss(int cpu
)
145 unsigned long addr
= bfin_read_DCPLB_FAULT_ADDR();
146 int status
= bfin_read_DCPLB_STATUS();
148 unsigned long d_data
, base
, addr1
, eaddr
, cplb_pagesize
, cplb_pageflags
;
150 nr_dcplb_miss
[cpu
]++;
151 if (unlikely(status
& FAULT_USERSUPV
))
152 nr_dcplb_supv_miss
[cpu
]++;
157 eaddr
= dcplb_bounds
[idx
].eaddr
;
161 } while (++idx
< dcplb_nr_bounds
);
163 if (unlikely(idx
== dcplb_nr_bounds
))
164 return CPLB_NO_ADDR_MATCH
;
166 d_data
= dcplb_bounds
[idx
].data
;
167 if (unlikely(d_data
== 0))
168 return CPLB_NO_ADDR_MATCH
;
170 addr
&= ~(SIZE_1M
- 1);
171 d_data
|= PAGE_SIZE_1MB
;
173 /* BF60x support large than 4M CPLB page size */
174 #ifdef PAGE_SIZE_16MB
175 cplb_pageflags
= PAGE_SIZE_16MB
;
176 cplb_pagesize
= SIZE_16M
;
178 cplb_pageflags
= PAGE_SIZE_4MB
;
179 cplb_pagesize
= SIZE_4M
;
183 addr1
= addr
& ~(cplb_pagesize
- 1);
184 if (addr1
>= base
&& (addr1
+ cplb_pagesize
) <= eaddr
) {
187 * (PAGE_SIZE_4MB & PAGE_SIZE_1MB) == PAGE_SIZE_1MB.
189 d_data
|= cplb_pageflags
;
193 if (cplb_pagesize
> SIZE_4M
) {
194 cplb_pageflags
= PAGE_SIZE_4MB
;
195 cplb_pagesize
= SIZE_4M
;
202 if ((addr
>= ASYNC_BANK0_BASE
)
203 && (addr
< ASYNC_BANK3_BASE
+ ASYNC_BANK3_SIZE
))
204 d_data
|= PAGE_SIZE_64MB
;
207 /* Pick entry to evict */
208 idx
= evict_one_dcplb(cpu
);
210 write_dcplb_data(cpu
, idx
, d_data
, addr
);
212 return CPLB_RELOADED
;
215 MGR_ATTR
int cplb_hdr(int seqstat
, struct pt_regs
*regs
)
217 int cause
= seqstat
& 0x3f;
218 unsigned int cpu
= raw_smp_processor_id();
221 return icplb_miss(cpu
);
223 return dcplb_miss(cpu
);
225 return CPLB_UNKNOWN_ERR
;