Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/arch/x86/kernel/amd_nb.c
10817 views
1
/*
2
* Shared support code for AMD K8 northbridges and derivates.
3
* Copyright 2006 Andi Kleen, SUSE Labs. Subject to GPLv2.
4
*/
5
#include <linux/types.h>
6
#include <linux/slab.h>
7
#include <linux/init.h>
8
#include <linux/errno.h>
9
#include <linux/module.h>
10
#include <linux/spinlock.h>
11
#include <asm/amd_nb.h>
12
13
static u32 *flush_words;
14
15
const struct pci_device_id amd_nb_misc_ids[] = {
16
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_K8_NB_MISC) },
17
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_10H_NB_MISC) },
18
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_NB_F3) },
19
{}
20
};
21
EXPORT_SYMBOL(amd_nb_misc_ids);
22
23
static struct pci_device_id amd_nb_link_ids[] = {
24
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_NB_F4) },
25
{}
26
};
27
28
const struct amd_nb_bus_dev_range amd_nb_bus_dev_ranges[] __initconst = {
29
{ 0x00, 0x18, 0x20 },
30
{ 0xff, 0x00, 0x20 },
31
{ 0xfe, 0x00, 0x20 },
32
{ }
33
};
34
35
struct amd_northbridge_info amd_northbridges;
36
EXPORT_SYMBOL(amd_northbridges);
37
38
static struct pci_dev *next_northbridge(struct pci_dev *dev,
39
const struct pci_device_id *ids)
40
{
41
do {
42
dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev);
43
if (!dev)
44
break;
45
} while (!pci_match_id(ids, dev));
46
return dev;
47
}
48
49
int amd_cache_northbridges(void)
50
{
51
u16 i = 0;
52
struct amd_northbridge *nb;
53
struct pci_dev *misc, *link;
54
55
if (amd_nb_num())
56
return 0;
57
58
misc = NULL;
59
while ((misc = next_northbridge(misc, amd_nb_misc_ids)) != NULL)
60
i++;
61
62
if (i == 0)
63
return 0;
64
65
nb = kzalloc(i * sizeof(struct amd_northbridge), GFP_KERNEL);
66
if (!nb)
67
return -ENOMEM;
68
69
amd_northbridges.nb = nb;
70
amd_northbridges.num = i;
71
72
link = misc = NULL;
73
for (i = 0; i != amd_nb_num(); i++) {
74
node_to_amd_nb(i)->misc = misc =
75
next_northbridge(misc, amd_nb_misc_ids);
76
node_to_amd_nb(i)->link = link =
77
next_northbridge(link, amd_nb_link_ids);
78
}
79
80
/* some CPU families (e.g. family 0x11) do not support GART */
81
if (boot_cpu_data.x86 == 0xf || boot_cpu_data.x86 == 0x10 ||
82
boot_cpu_data.x86 == 0x15)
83
amd_northbridges.flags |= AMD_NB_GART;
84
85
/*
86
* Some CPU families support L3 Cache Index Disable. There are some
87
* limitations because of E382 and E388 on family 0x10.
88
*/
89
if (boot_cpu_data.x86 == 0x10 &&
90
boot_cpu_data.x86_model >= 0x8 &&
91
(boot_cpu_data.x86_model > 0x9 ||
92
boot_cpu_data.x86_mask >= 0x1))
93
amd_northbridges.flags |= AMD_NB_L3_INDEX_DISABLE;
94
95
if (boot_cpu_data.x86 == 0x15)
96
amd_northbridges.flags |= AMD_NB_L3_INDEX_DISABLE;
97
98
/* L3 cache partitioning is supported on family 0x15 */
99
if (boot_cpu_data.x86 == 0x15)
100
amd_northbridges.flags |= AMD_NB_L3_PARTITIONING;
101
102
return 0;
103
}
104
EXPORT_SYMBOL_GPL(amd_cache_northbridges);
105
106
/*
107
* Ignores subdevice/subvendor but as far as I can figure out
108
* they're useless anyways
109
*/
110
bool __init early_is_amd_nb(u32 device)
111
{
112
const struct pci_device_id *id;
113
u32 vendor = device & 0xffff;
114
115
device >>= 16;
116
for (id = amd_nb_misc_ids; id->vendor; id++)
117
if (vendor == id->vendor && device == id->device)
118
return true;
119
return false;
120
}
121
122
int amd_get_subcaches(int cpu)
123
{
124
struct pci_dev *link = node_to_amd_nb(amd_get_nb_id(cpu))->link;
125
unsigned int mask;
126
int cuid = 0;
127
128
if (!amd_nb_has_feature(AMD_NB_L3_PARTITIONING))
129
return 0;
130
131
pci_read_config_dword(link, 0x1d4, &mask);
132
133
#ifdef CONFIG_SMP
134
cuid = cpu_data(cpu).compute_unit_id;
135
#endif
136
return (mask >> (4 * cuid)) & 0xf;
137
}
138
139
int amd_set_subcaches(int cpu, int mask)
140
{
141
static unsigned int reset, ban;
142
struct amd_northbridge *nb = node_to_amd_nb(amd_get_nb_id(cpu));
143
unsigned int reg;
144
int cuid = 0;
145
146
if (!amd_nb_has_feature(AMD_NB_L3_PARTITIONING) || mask > 0xf)
147
return -EINVAL;
148
149
/* if necessary, collect reset state of L3 partitioning and BAN mode */
150
if (reset == 0) {
151
pci_read_config_dword(nb->link, 0x1d4, &reset);
152
pci_read_config_dword(nb->misc, 0x1b8, &ban);
153
ban &= 0x180000;
154
}
155
156
/* deactivate BAN mode if any subcaches are to be disabled */
157
if (mask != 0xf) {
158
pci_read_config_dword(nb->misc, 0x1b8, &reg);
159
pci_write_config_dword(nb->misc, 0x1b8, reg & ~0x180000);
160
}
161
162
#ifdef CONFIG_SMP
163
cuid = cpu_data(cpu).compute_unit_id;
164
#endif
165
mask <<= 4 * cuid;
166
mask |= (0xf ^ (1 << cuid)) << 26;
167
168
pci_write_config_dword(nb->link, 0x1d4, mask);
169
170
/* reset BAN mode if L3 partitioning returned to reset state */
171
pci_read_config_dword(nb->link, 0x1d4, &reg);
172
if (reg == reset) {
173
pci_read_config_dword(nb->misc, 0x1b8, &reg);
174
reg &= ~0x180000;
175
pci_write_config_dword(nb->misc, 0x1b8, reg | ban);
176
}
177
178
return 0;
179
}
180
181
static int amd_cache_gart(void)
182
{
183
u16 i;
184
185
if (!amd_nb_has_feature(AMD_NB_GART))
186
return 0;
187
188
flush_words = kmalloc(amd_nb_num() * sizeof(u32), GFP_KERNEL);
189
if (!flush_words) {
190
amd_northbridges.flags &= ~AMD_NB_GART;
191
return -ENOMEM;
192
}
193
194
for (i = 0; i != amd_nb_num(); i++)
195
pci_read_config_dword(node_to_amd_nb(i)->misc, 0x9c,
196
&flush_words[i]);
197
198
return 0;
199
}
200
201
void amd_flush_garts(void)
202
{
203
int flushed, i;
204
unsigned long flags;
205
static DEFINE_SPINLOCK(gart_lock);
206
207
if (!amd_nb_has_feature(AMD_NB_GART))
208
return;
209
210
/* Avoid races between AGP and IOMMU. In theory it's not needed
211
but I'm not sure if the hardware won't lose flush requests
212
when another is pending. This whole thing is so expensive anyways
213
that it doesn't matter to serialize more. -AK */
214
spin_lock_irqsave(&gart_lock, flags);
215
flushed = 0;
216
for (i = 0; i < amd_nb_num(); i++) {
217
pci_write_config_dword(node_to_amd_nb(i)->misc, 0x9c,
218
flush_words[i] | 1);
219
flushed++;
220
}
221
for (i = 0; i < amd_nb_num(); i++) {
222
u32 w;
223
/* Make sure the hardware actually executed the flush*/
224
for (;;) {
225
pci_read_config_dword(node_to_amd_nb(i)->misc,
226
0x9c, &w);
227
if (!(w & 1))
228
break;
229
cpu_relax();
230
}
231
}
232
spin_unlock_irqrestore(&gart_lock, flags);
233
if (!flushed)
234
printk("nothing to flush?\n");
235
}
236
EXPORT_SYMBOL_GPL(amd_flush_garts);
237
238
static __init int init_amd_nbs(void)
239
{
240
int err = 0;
241
242
err = amd_cache_northbridges();
243
244
if (err < 0)
245
printk(KERN_NOTICE "AMD NB: Cannot enumerate AMD northbridges.\n");
246
247
if (amd_cache_gart() < 0)
248
printk(KERN_NOTICE "AMD NB: Cannot initialize GART flush words, "
249
"GART support disabled.\n");
250
251
return err;
252
}
253
254
/* This has to go after the PCI subsystem */
255
fs_initcall(init_amd_nbs);
256
257