diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/x86_64/kernel/aperture.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'arch/x86_64/kernel/aperture.c')
-rw-r--r-- | arch/x86_64/kernel/aperture.c | 286 |
1 files changed, 286 insertions, 0 deletions
diff --git a/arch/x86_64/kernel/aperture.c b/arch/x86_64/kernel/aperture.c new file mode 100644 index 000000000000..4baa99fe1e5c --- /dev/null +++ b/arch/x86_64/kernel/aperture.c | |||
@@ -0,0 +1,286 @@ | |||
1 | /* | ||
2 | * Firmware replacement code. | ||
3 | * | ||
4 | * Work around broken BIOSes that don't set an aperture or only set the | ||
5 | * aperture in the AGP bridge. | ||
6 | * If all fails map the aperture over some low memory. This is cheaper than | ||
7 | * doing bounce buffering. The memory is lost. This is done at early boot | ||
8 | * because only the bootmem allocator can allocate 32+MB. | ||
9 | * | ||
10 | * Copyright 2002 Andi Kleen, SuSE Labs. | ||
11 | * $Id: aperture.c,v 1.7 2003/08/01 03:36:18 ak Exp $ | ||
12 | */ | ||
13 | #include <linux/config.h> | ||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/types.h> | ||
16 | #include <linux/init.h> | ||
17 | #include <linux/bootmem.h> | ||
18 | #include <linux/mmzone.h> | ||
19 | #include <linux/pci_ids.h> | ||
20 | #include <linux/pci.h> | ||
21 | #include <linux/bitops.h> | ||
22 | #include <asm/e820.h> | ||
23 | #include <asm/io.h> | ||
24 | #include <asm/proto.h> | ||
25 | #include <asm/pci-direct.h> | ||
26 | |||
27 | int iommu_aperture; | ||
28 | int iommu_aperture_disabled __initdata = 0; | ||
29 | int iommu_aperture_allowed __initdata = 0; | ||
30 | |||
31 | int fallback_aper_order __initdata = 1; /* 64MB */ | ||
32 | int fallback_aper_force __initdata = 0; | ||
33 | |||
34 | int fix_aperture __initdata = 1; | ||
35 | |||
36 | /* This code runs before the PCI subsystem is initialized, so just | ||
37 | access the northbridge directly. */ | ||
38 | |||
39 | #define NB_ID_3 (PCI_VENDOR_ID_AMD | (0x1103<<16)) | ||
40 | |||
41 | static u32 __init allocate_aperture(void) | ||
42 | { | ||
43 | #ifdef CONFIG_DISCONTIGMEM | ||
44 | pg_data_t *nd0 = NODE_DATA(0); | ||
45 | #else | ||
46 | pg_data_t *nd0 = &contig_page_data; | ||
47 | #endif | ||
48 | u32 aper_size; | ||
49 | void *p; | ||
50 | |||
51 | if (fallback_aper_order > 7) | ||
52 | fallback_aper_order = 7; | ||
53 | aper_size = (32 * 1024 * 1024) << fallback_aper_order; | ||
54 | |||
55 | /* | ||
56 | * Aperture has to be naturally aligned. This means an 2GB aperture won't | ||
57 | * have much chances to find a place in the lower 4GB of memory. | ||
58 | * Unfortunately we cannot move it up because that would make the | ||
59 | * IOMMU useless. | ||
60 | */ | ||
61 | p = __alloc_bootmem_node(nd0, aper_size, aper_size, 0); | ||
62 | if (!p || __pa(p)+aper_size > 0xffffffff) { | ||
63 | printk("Cannot allocate aperture memory hole (%p,%uK)\n", | ||
64 | p, aper_size>>10); | ||
65 | if (p) | ||
66 | free_bootmem_node(nd0, (unsigned long)p, aper_size); | ||
67 | return 0; | ||
68 | } | ||
69 | printk("Mapping aperture over %d KB of RAM @ %lx\n", | ||
70 | aper_size >> 10, __pa(p)); | ||
71 | return (u32)__pa(p); | ||
72 | } | ||
73 | |||
74 | static int __init aperture_valid(char *name, u64 aper_base, u32 aper_size) | ||
75 | { | ||
76 | if (!aper_base) | ||
77 | return 0; | ||
78 | if (aper_size < 64*1024*1024) { | ||
79 | printk("Aperture from %s too small (%d MB)\n", name, aper_size>>20); | ||
80 | return 0; | ||
81 | } | ||
82 | if (aper_base + aper_size >= 0xffffffff) { | ||
83 | printk("Aperture from %s beyond 4GB. Ignoring.\n",name); | ||
84 | return 0; | ||
85 | } | ||
86 | if (e820_mapped(aper_base, aper_base + aper_size, E820_RAM)) { | ||
87 | printk("Aperture from %s pointing to e820 RAM. Ignoring.\n",name); | ||
88 | return 0; | ||
89 | } | ||
90 | return 1; | ||
91 | } | ||
92 | |||
93 | /* Find a PCI capability */ | ||
94 | static __u32 __init find_cap(int num, int slot, int func, int cap) | ||
95 | { | ||
96 | u8 pos; | ||
97 | int bytes; | ||
98 | if (!(read_pci_config_16(num,slot,func,PCI_STATUS) & PCI_STATUS_CAP_LIST)) | ||
99 | return 0; | ||
100 | pos = read_pci_config_byte(num,slot,func,PCI_CAPABILITY_LIST); | ||
101 | for (bytes = 0; bytes < 48 && pos >= 0x40; bytes++) { | ||
102 | u8 id; | ||
103 | pos &= ~3; | ||
104 | id = read_pci_config_byte(num,slot,func,pos+PCI_CAP_LIST_ID); | ||
105 | if (id == 0xff) | ||
106 | break; | ||
107 | if (id == cap) | ||
108 | return pos; | ||
109 | pos = read_pci_config_byte(num,slot,func,pos+PCI_CAP_LIST_NEXT); | ||
110 | } | ||
111 | return 0; | ||
112 | } | ||
113 | |||
114 | /* Read a standard AGPv3 bridge header */ | ||
115 | static __u32 __init read_agp(int num, int slot, int func, int cap, u32 *order) | ||
116 | { | ||
117 | u32 apsize; | ||
118 | u32 apsizereg; | ||
119 | int nbits; | ||
120 | u32 aper_low, aper_hi; | ||
121 | u64 aper; | ||
122 | |||
123 | printk("AGP bridge at %02x:%02x:%02x\n", num, slot, func); | ||
124 | apsizereg = read_pci_config_16(num,slot,func, cap + 0x14); | ||
125 | if (apsizereg == 0xffffffff) { | ||
126 | printk("APSIZE in AGP bridge unreadable\n"); | ||
127 | return 0; | ||
128 | } | ||
129 | |||
130 | apsize = apsizereg & 0xfff; | ||
131 | /* Some BIOS use weird encodings not in the AGPv3 table. */ | ||
132 | if (apsize & 0xff) | ||
133 | apsize |= 0xf00; | ||
134 | nbits = hweight16(apsize); | ||
135 | *order = 7 - nbits; | ||
136 | if ((int)*order < 0) /* < 32MB */ | ||
137 | *order = 0; | ||
138 | |||
139 | aper_low = read_pci_config(num,slot,func, 0x10); | ||
140 | aper_hi = read_pci_config(num,slot,func,0x14); | ||
141 | aper = (aper_low & ~((1<<22)-1)) | ((u64)aper_hi << 32); | ||
142 | |||
143 | printk("Aperture from AGP @ %Lx size %u MB (APSIZE %x)\n", | ||
144 | aper, 32 << *order, apsizereg); | ||
145 | |||
146 | if (!aperture_valid("AGP bridge", aper, (32*1024*1024) << *order)) | ||
147 | return 0; | ||
148 | return (u32)aper; | ||
149 | } | ||
150 | |||
151 | /* Look for an AGP bridge. Windows only expects the aperture in the | ||
152 | AGP bridge and some BIOS forget to initialize the Northbridge too. | ||
153 | Work around this here. | ||
154 | |||
155 | Do an PCI bus scan by hand because we're running before the PCI | ||
156 | subsystem. | ||
157 | |||
158 | All K8 AGP bridges are AGPv3 compliant, so we can do this scan | ||
159 | generically. It's probably overkill to always scan all slots because | ||
160 | the AGP bridges should be always an own bus on the HT hierarchy, | ||
161 | but do it here for future safety. */ | ||
162 | static __u32 __init search_agp_bridge(u32 *order, int *valid_agp) | ||
163 | { | ||
164 | int num, slot, func; | ||
165 | |||
166 | /* Poor man's PCI discovery */ | ||
167 | for (num = 0; num < 32; num++) { | ||
168 | for (slot = 0; slot < 32; slot++) { | ||
169 | for (func = 0; func < 8; func++) { | ||
170 | u32 class, cap; | ||
171 | u8 type; | ||
172 | class = read_pci_config(num,slot,func, | ||
173 | PCI_CLASS_REVISION); | ||
174 | if (class == 0xffffffff) | ||
175 | break; | ||
176 | |||
177 | switch (class >> 16) { | ||
178 | case PCI_CLASS_BRIDGE_HOST: | ||
179 | case PCI_CLASS_BRIDGE_OTHER: /* needed? */ | ||
180 | /* AGP bridge? */ | ||
181 | cap = find_cap(num,slot,func,PCI_CAP_ID_AGP); | ||
182 | if (!cap) | ||
183 | break; | ||
184 | *valid_agp = 1; | ||
185 | return read_agp(num,slot,func,cap,order); | ||
186 | } | ||
187 | |||
188 | /* No multi-function device? */ | ||
189 | type = read_pci_config_byte(num,slot,func, | ||
190 | PCI_HEADER_TYPE); | ||
191 | if (!(type & 0x80)) | ||
192 | break; | ||
193 | } | ||
194 | } | ||
195 | } | ||
196 | printk("No AGP bridge found\n"); | ||
197 | return 0; | ||
198 | } | ||
199 | |||
200 | void __init iommu_hole_init(void) | ||
201 | { | ||
202 | int fix, num; | ||
203 | u32 aper_size, aper_alloc = 0, aper_order, last_aper_order = 0; | ||
204 | u64 aper_base, last_aper_base = 0; | ||
205 | int valid_agp = 0; | ||
206 | |||
207 | if (iommu_aperture_disabled || !fix_aperture) | ||
208 | return; | ||
209 | |||
210 | printk("Checking aperture...\n"); | ||
211 | |||
212 | fix = 0; | ||
213 | for (num = 24; num < 32; num++) { | ||
214 | char name[30]; | ||
215 | if (read_pci_config(0, num, 3, 0x00) != NB_ID_3) | ||
216 | continue; | ||
217 | |||
218 | iommu_aperture = 1; | ||
219 | |||
220 | aper_order = (read_pci_config(0, num, 3, 0x90) >> 1) & 7; | ||
221 | aper_size = (32 * 1024 * 1024) << aper_order; | ||
222 | aper_base = read_pci_config(0, num, 3, 0x94) & 0x7fff; | ||
223 | aper_base <<= 25; | ||
224 | |||
225 | printk("CPU %d: aperture @ %Lx size %u MB\n", num-24, | ||
226 | aper_base, aper_size>>20); | ||
227 | |||
228 | sprintf(name, "northbridge cpu %d", num-24); | ||
229 | |||
230 | if (!aperture_valid(name, aper_base, aper_size)) { | ||
231 | fix = 1; | ||
232 | break; | ||
233 | } | ||
234 | |||
235 | if ((last_aper_order && aper_order != last_aper_order) || | ||
236 | (last_aper_base && aper_base != last_aper_base)) { | ||
237 | fix = 1; | ||
238 | break; | ||
239 | } | ||
240 | last_aper_order = aper_order; | ||
241 | last_aper_base = aper_base; | ||
242 | } | ||
243 | |||
244 | if (!fix && !fallback_aper_force) | ||
245 | return; | ||
246 | |||
247 | if (!fallback_aper_force) | ||
248 | aper_alloc = search_agp_bridge(&aper_order, &valid_agp); | ||
249 | |||
250 | if (aper_alloc) { | ||
251 | /* Got the aperture from the AGP bridge */ | ||
252 | } else if ((!no_iommu && end_pfn >= 0xffffffff>>PAGE_SHIFT) || | ||
253 | force_iommu || | ||
254 | valid_agp || | ||
255 | fallback_aper_force) { | ||
256 | printk("Your BIOS doesn't leave a aperture memory hole\n"); | ||
257 | printk("Please enable the IOMMU option in the BIOS setup\n"); | ||
258 | printk("This costs you %d MB of RAM\n", | ||
259 | 32 << fallback_aper_order); | ||
260 | |||
261 | aper_order = fallback_aper_order; | ||
262 | aper_alloc = allocate_aperture(); | ||
263 | if (!aper_alloc) { | ||
264 | /* Could disable AGP and IOMMU here, but it's probably | ||
265 | not worth it. But the later users cannot deal with | ||
266 | bad apertures and turning on the aperture over memory | ||
267 | causes very strange problems, so it's better to | ||
268 | panic early. */ | ||
269 | panic("Not enough memory for aperture"); | ||
270 | } | ||
271 | } else { | ||
272 | return; | ||
273 | } | ||
274 | |||
275 | /* Fix up the north bridges */ | ||
276 | for (num = 24; num < 32; num++) { | ||
277 | if (read_pci_config(0, num, 3, 0x00) != NB_ID_3) | ||
278 | continue; | ||
279 | |||
280 | /* Don't enable translation yet. That is done later. | ||
281 | Assume this BIOS didn't initialise the GART so | ||
282 | just overwrite all previous bits */ | ||
283 | write_pci_config(0, num, 3, 0x90, aper_order<<1); | ||
284 | write_pci_config(0, num, 3, 0x94, aper_alloc>>25); | ||
285 | } | ||
286 | } | ||