Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/arch/blackfin/kernel/cplb-nompu/cplbmgr.c
15133 views
1
/*
2
* Based on: arch/blackfin/kernel/cplb-mpu/cplbmgr.c
3
* Author: Michael McTernan <[email protected]>
4
*
5
* Description: CPLB miss handler.
6
*
7
* Modified:
8
* Copyright 2008 Airvana Inc.
9
* Copyright 2008-2009 Analog Devices Inc.
10
*
11
* Licensed under the GPL-2 or later
12
*/
13
14
#include <linux/kernel.h>
15
#include <asm/blackfin.h>
16
#include <asm/cplbinit.h>
17
#include <asm/cplb.h>
18
#include <asm/mmu_context.h>
19
#include <asm/traps.h>
20
21
/*
22
* WARNING
23
*
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
26
* registers.
27
*/
28
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];
32
33
#ifdef CONFIG_EXCPT_IRQ_SYSC_L1
34
#define MGR_ATTR __attribute__((l1_text))
35
#else
36
#define MGR_ATTR
37
#endif
38
39
static inline void write_dcplb_data(int cpu, int idx, unsigned long data,
40
unsigned long addr)
41
{
42
_disable_dcplb();
43
bfin_write32(DCPLB_DATA0 + idx * 4, data);
44
bfin_write32(DCPLB_ADDR0 + idx * 4, addr);
45
_enable_dcplb();
46
47
#ifdef CONFIG_CPLB_INFO
48
dcplb_tbl[cpu][idx].addr = addr;
49
dcplb_tbl[cpu][idx].data = data;
50
#endif
51
}
52
53
static inline void write_icplb_data(int cpu, int idx, unsigned long data,
54
unsigned long addr)
55
{
56
_disable_icplb();
57
bfin_write32(ICPLB_DATA0 + idx * 4, data);
58
bfin_write32(ICPLB_ADDR0 + idx * 4, addr);
59
_enable_icplb();
60
61
#ifdef CONFIG_CPLB_INFO
62
icplb_tbl[cpu][idx].addr = addr;
63
icplb_tbl[cpu][idx].data = data;
64
#endif
65
}
66
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;
70
71
/*
72
* Find an ICPLB entry to be evicted and return its index.
73
*/
74
static int evict_one_icplb(int cpu)
75
{
76
int i = first_switched_icplb + icplb_rr_index[cpu];
77
if (i >= MAX_CPLBS) {
78
i -= MAX_CPLBS - first_switched_icplb;
79
icplb_rr_index[cpu] -= MAX_CPLBS - first_switched_icplb;
80
}
81
icplb_rr_index[cpu]++;
82
return i;
83
}
84
85
static int evict_one_dcplb(int cpu)
86
{
87
int i = first_switched_dcplb + dcplb_rr_index[cpu];
88
if (i >= MAX_CPLBS) {
89
i -= MAX_CPLBS - first_switched_dcplb;
90
dcplb_rr_index[cpu] -= MAX_CPLBS - first_switched_dcplb;
91
}
92
dcplb_rr_index[cpu]++;
93
return i;
94
}
95
96
MGR_ATTR static int icplb_miss(int cpu)
97
{
98
unsigned long addr = bfin_read_ICPLB_FAULT_ADDR();
99
int status = bfin_read_ICPLB_STATUS();
100
int idx;
101
unsigned long i_data, base, addr1, eaddr;
102
103
nr_icplb_miss[cpu]++;
104
if (unlikely(status & FAULT_USERSUPV))
105
nr_icplb_supv_miss[cpu]++;
106
107
base = 0;
108
idx = 0;
109
do {
110
eaddr = icplb_bounds[idx].eaddr;
111
if (addr < eaddr)
112
break;
113
base = eaddr;
114
} while (++idx < icplb_nr_bounds);
115
116
if (unlikely(idx == icplb_nr_bounds))
117
return CPLB_NO_ADDR_MATCH;
118
119
i_data = icplb_bounds[idx].data;
120
if (unlikely(i_data == 0))
121
return CPLB_NO_ADDR_MATCH;
122
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) {
127
/*
128
* This works because
129
* (PAGE_SIZE_4MB & PAGE_SIZE_1MB) == PAGE_SIZE_1MB.
130
*/
131
i_data |= PAGE_SIZE_4MB;
132
addr = addr1;
133
}
134
135
/* Pick entry to evict */
136
idx = evict_one_icplb(cpu);
137
138
write_icplb_data(cpu, idx, i_data, addr);
139
140
return CPLB_RELOADED;
141
}
142
143
MGR_ATTR static int dcplb_miss(int cpu)
144
{
145
unsigned long addr = bfin_read_DCPLB_FAULT_ADDR();
146
int status = bfin_read_DCPLB_STATUS();
147
int idx;
148
unsigned long d_data, base, addr1, eaddr;
149
150
nr_dcplb_miss[cpu]++;
151
if (unlikely(status & FAULT_USERSUPV))
152
nr_dcplb_supv_miss[cpu]++;
153
154
base = 0;
155
idx = 0;
156
do {
157
eaddr = dcplb_bounds[idx].eaddr;
158
if (addr < eaddr)
159
break;
160
base = eaddr;
161
} while (++idx < dcplb_nr_bounds);
162
163
if (unlikely(idx == dcplb_nr_bounds))
164
return CPLB_NO_ADDR_MATCH;
165
166
d_data = dcplb_bounds[idx].data;
167
if (unlikely(d_data == 0))
168
return CPLB_NO_ADDR_MATCH;
169
170
addr1 = addr & ~(SIZE_4M - 1);
171
addr &= ~(SIZE_1M - 1);
172
d_data |= PAGE_SIZE_1MB;
173
if (addr1 >= base && (addr1 + SIZE_4M) <= eaddr) {
174
/*
175
* This works because
176
* (PAGE_SIZE_4MB & PAGE_SIZE_1MB) == PAGE_SIZE_1MB.
177
*/
178
d_data |= PAGE_SIZE_4MB;
179
addr = addr1;
180
}
181
182
/* Pick entry to evict */
183
idx = evict_one_dcplb(cpu);
184
185
write_dcplb_data(cpu, idx, d_data, addr);
186
187
return CPLB_RELOADED;
188
}
189
190
MGR_ATTR int cplb_hdr(int seqstat, struct pt_regs *regs)
191
{
192
int cause = seqstat & 0x3f;
193
unsigned int cpu = raw_smp_processor_id();
194
switch (cause) {
195
case VEC_CPLB_I_M:
196
return icplb_miss(cpu);
197
case VEC_CPLB_M:
198
return dcplb_miss(cpu);
199
default:
200
return CPLB_UNKNOWN_ERR;
201
}
202
}
203
204