aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86_64/kernel/aperture.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /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.c286
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
27int iommu_aperture;
28int iommu_aperture_disabled __initdata = 0;
29int iommu_aperture_allowed __initdata = 0;
30
31int fallback_aper_order __initdata = 1; /* 64MB */
32int fallback_aper_force __initdata = 0;
33
34int 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
41static 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
74static 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 */
94static __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 */
115static __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. */
162static __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
200void __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}