Commit | Line | Data |
---|---|---|
1da177e4 LT |
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 | ||
42442ed5 AM |
36 | /* This code runs before the PCI subsystem is initialized, so just |
37 | access the northbridge directly. */ | |
1da177e4 | 38 | |
42442ed5 | 39 | #define NB_ID_3 (PCI_VENDOR_ID_AMD | (0x1103<<16)) |
ece90303 | 40 | |
1da177e4 LT |
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 | /* | |
42442ed5 AM |
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. | |
1da177e4 LT |
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 | } | |
42442ed5 | 69 | printk("Mapping aperture over %d KB of RAM @ %lx\n", |
1da177e4 LT |
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 | ||
42442ed5 | 93 | /* Find a PCI capability */ |
1da177e4 LT |
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"); | |
42442ed5 AM |
258 | printk("This costs you %d MB of RAM\n", |
259 | 32 << fallback_aper_order); | |
1da177e4 LT |
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 | } |