blob: 8f681cae7bf7df3c8a6749799d1d5b2f14682170 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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.
Linus Torvalds1da177e2005-04-16 15:20:36 -070011 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070012#include <linux/kernel.h>
13#include <linux/types.h>
14#include <linux/init.h>
15#include <linux/bootmem.h>
16#include <linux/mmzone.h>
17#include <linux/pci_ids.h>
18#include <linux/pci.h>
19#include <linux/bitops.h>
Aaron Durbin56dd6692006-09-26 10:52:40 +020020#include <linux/ioport.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070021#include <asm/e820.h>
22#include <asm/io.h>
Yinghai Luf2cf8e02007-07-21 17:11:31 +020023#include <asm/iommu.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070024#include <asm/pci-direct.h>
Andi Kleenca8642f2006-01-11 22:44:27 +010025#include <asm/dma.h>
Andi Kleena32073b2006-06-26 13:56:40 +020026#include <asm/k8.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070027
28int iommu_aperture;
29int iommu_aperture_disabled __initdata = 0;
30int iommu_aperture_allowed __initdata = 0;
31
32int fallback_aper_order __initdata = 1; /* 64MB */
33int fallback_aper_force __initdata = 0;
34
35int fix_aperture __initdata = 1;
36
Aaron Durbin56dd6692006-09-26 10:52:40 +020037static struct resource gart_resource = {
38 .name = "GART",
39 .flags = IORESOURCE_MEM,
40};
41
42static void __init insert_aperture_resource(u32 aper_base, u32 aper_size)
43{
44 gart_resource.start = aper_base;
45 gart_resource.end = aper_base + aper_size - 1;
46 insert_resource(&iomem_resource, &gart_resource);
47}
48
Andrew Morton42442ed2005-06-08 15:49:25 -070049/* This code runs before the PCI subsystem is initialized, so just
50 access the northbridge directly. */
Linus Torvalds1da177e2005-04-16 15:20:36 -070051
52static u32 __init allocate_aperture(void)
53{
Linus Torvalds1da177e2005-04-16 15:20:36 -070054 u32 aper_size;
55 void *p;
56
57 if (fallback_aper_order > 7)
58 fallback_aper_order = 7;
59 aper_size = (32 * 1024 * 1024) << fallback_aper_order;
60
61 /*
Andrew Morton42442ed2005-06-08 15:49:25 -070062 * Aperture has to be naturally aligned. This means an 2GB aperture won't
Adam Henleyd5d9ca62006-09-26 10:52:28 +020063 * have much chance of finding a place in the lower 4GB of memory.
Andrew Morton42442ed2005-06-08 15:49:25 -070064 * Unfortunately we cannot move it up because that would make the
65 * IOMMU useless.
Linus Torvalds1da177e2005-04-16 15:20:36 -070066 */
James Puthukattukaran82d1bb72007-05-02 19:27:13 +020067 p = __alloc_bootmem_nopanic(aper_size, aper_size, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -070068 if (!p || __pa(p)+aper_size > 0xffffffff) {
69 printk("Cannot allocate aperture memory hole (%p,%uK)\n",
70 p, aper_size>>10);
71 if (p)
James Puthukattukaran82d1bb72007-05-02 19:27:13 +020072 free_bootmem(__pa(p), aper_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -070073 return 0;
74 }
Andrew Morton42442ed2005-06-08 15:49:25 -070075 printk("Mapping aperture over %d KB of RAM @ %lx\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -070076 aper_size >> 10, __pa(p));
Aaron Durbin56dd6692006-09-26 10:52:40 +020077 insert_aperture_resource((u32)__pa(p), aper_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -070078 return (u32)__pa(p);
79}
80
Andi Kleena32073b2006-06-26 13:56:40 +020081static int __init aperture_valid(u64 aper_base, u32 aper_size)
Linus Torvalds1da177e2005-04-16 15:20:36 -070082{
83 if (!aper_base)
84 return 0;
85 if (aper_size < 64*1024*1024) {
Andi Kleena32073b2006-06-26 13:56:40 +020086 printk("Aperture too small (%d MB)\n", aper_size>>20);
Linus Torvalds1da177e2005-04-16 15:20:36 -070087 return 0;
88 }
Andrew Hastings547c5352007-05-11 11:23:19 +020089 if (aper_base + aper_size > 0x100000000UL) {
Andi Kleena32073b2006-06-26 13:56:40 +020090 printk("Aperture beyond 4GB. Ignoring.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -070091 return 0;
92 }
Arjan van de Veneee5a9f2006-04-07 19:49:24 +020093 if (e820_any_mapped(aper_base, aper_base + aper_size, E820_RAM)) {
Andi Kleena32073b2006-06-26 13:56:40 +020094 printk("Aperture pointing to e820 RAM. Ignoring.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -070095 return 0;
96 }
97 return 1;
98}
99
Andrew Morton42442ed2005-06-08 15:49:25 -0700100/* Find a PCI capability */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101static __u32 __init find_cap(int num, int slot, int func, int cap)
102{
103 u8 pos;
104 int bytes;
105 if (!(read_pci_config_16(num,slot,func,PCI_STATUS) & PCI_STATUS_CAP_LIST))
106 return 0;
107 pos = read_pci_config_byte(num,slot,func,PCI_CAPABILITY_LIST);
108 for (bytes = 0; bytes < 48 && pos >= 0x40; bytes++) {
109 u8 id;
110 pos &= ~3;
111 id = read_pci_config_byte(num,slot,func,pos+PCI_CAP_LIST_ID);
112 if (id == 0xff)
113 break;
114 if (id == cap)
115 return pos;
116 pos = read_pci_config_byte(num,slot,func,pos+PCI_CAP_LIST_NEXT);
117 }
118 return 0;
119}
120
121/* Read a standard AGPv3 bridge header */
122static __u32 __init read_agp(int num, int slot, int func, int cap, u32 *order)
123{
124 u32 apsize;
125 u32 apsizereg;
126 int nbits;
127 u32 aper_low, aper_hi;
128 u64 aper;
129
130 printk("AGP bridge at %02x:%02x:%02x\n", num, slot, func);
131 apsizereg = read_pci_config_16(num,slot,func, cap + 0x14);
132 if (apsizereg == 0xffffffff) {
133 printk("APSIZE in AGP bridge unreadable\n");
134 return 0;
135 }
136
137 apsize = apsizereg & 0xfff;
138 /* Some BIOS use weird encodings not in the AGPv3 table. */
139 if (apsize & 0xff)
140 apsize |= 0xf00;
141 nbits = hweight16(apsize);
142 *order = 7 - nbits;
143 if ((int)*order < 0) /* < 32MB */
144 *order = 0;
145
146 aper_low = read_pci_config(num,slot,func, 0x10);
147 aper_hi = read_pci_config(num,slot,func,0x14);
148 aper = (aper_low & ~((1<<22)-1)) | ((u64)aper_hi << 32);
149
150 printk("Aperture from AGP @ %Lx size %u MB (APSIZE %x)\n",
151 aper, 32 << *order, apsizereg);
152
Andi Kleena32073b2006-06-26 13:56:40 +0200153 if (!aperture_valid(aper, (32*1024*1024) << *order))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154 return 0;
155 return (u32)aper;
156}
157
158/* Look for an AGP bridge. Windows only expects the aperture in the
159 AGP bridge and some BIOS forget to initialize the Northbridge too.
160 Work around this here.
161
162 Do an PCI bus scan by hand because we're running before the PCI
163 subsystem.
164
165 All K8 AGP bridges are AGPv3 compliant, so we can do this scan
166 generically. It's probably overkill to always scan all slots because
167 the AGP bridges should be always an own bus on the HT hierarchy,
168 but do it here for future safety. */
169static __u32 __init search_agp_bridge(u32 *order, int *valid_agp)
170{
171 int num, slot, func;
172
173 /* Poor man's PCI discovery */
Navin Boppuri9c01dda2006-03-25 16:31:40 +0100174 for (num = 0; num < 256; num++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175 for (slot = 0; slot < 32; slot++) {
176 for (func = 0; func < 8; func++) {
177 u32 class, cap;
178 u8 type;
179 class = read_pci_config(num,slot,func,
180 PCI_CLASS_REVISION);
181 if (class == 0xffffffff)
182 break;
183
184 switch (class >> 16) {
185 case PCI_CLASS_BRIDGE_HOST:
186 case PCI_CLASS_BRIDGE_OTHER: /* needed? */
187 /* AGP bridge? */
188 cap = find_cap(num,slot,func,PCI_CAP_ID_AGP);
189 if (!cap)
190 break;
191 *valid_agp = 1;
192 return read_agp(num,slot,func,cap,order);
193 }
194
195 /* No multi-function device? */
196 type = read_pci_config_byte(num,slot,func,
197 PCI_HEADER_TYPE);
198 if (!(type & 0x80))
199 break;
200 }
201 }
202 }
203 printk("No AGP bridge found\n");
204 return 0;
205}
206
207void __init iommu_hole_init(void)
208{
209 int fix, num;
Andi Kleen50895c52005-11-05 17:25:53 +0100210 u32 aper_size, aper_alloc = 0, aper_order = 0, last_aper_order = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211 u64 aper_base, last_aper_base = 0;
212 int valid_agp = 0;
213
Andi Kleen0637a702006-09-26 10:52:41 +0200214 if (iommu_aperture_disabled || !fix_aperture || !early_pci_allowed())
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215 return;
216
Dan Aloni753811d2007-07-21 17:11:36 +0200217 printk(KERN_INFO "Checking aperture...\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218
219 fix = 0;
220 for (num = 24; num < 32; num++) {
Andi Kleena32073b2006-06-26 13:56:40 +0200221 if (!early_is_k8_nb(read_pci_config(0, num, 3, 0x00)))
222 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223
Jon Mason8d4f6b92006-06-26 13:58:05 +0200224 iommu_detected = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225 iommu_aperture = 1;
226
227 aper_order = (read_pci_config(0, num, 3, 0x90) >> 1) & 7;
228 aper_size = (32 * 1024 * 1024) << aper_order;
229 aper_base = read_pci_config(0, num, 3, 0x94) & 0x7fff;
230 aper_base <<= 25;
231
232 printk("CPU %d: aperture @ %Lx size %u MB\n", num-24,
233 aper_base, aper_size>>20);
234
Andi Kleena32073b2006-06-26 13:56:40 +0200235 if (!aperture_valid(aper_base, aper_size)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236 fix = 1;
237 break;
238 }
239
240 if ((last_aper_order && aper_order != last_aper_order) ||
241 (last_aper_base && aper_base != last_aper_base)) {
242 fix = 1;
243 break;
244 }
245 last_aper_order = aper_order;
246 last_aper_base = aper_base;
247 }
248
Aaron Durbin56dd6692006-09-26 10:52:40 +0200249 if (!fix && !fallback_aper_force) {
250 if (last_aper_base) {
251 unsigned long n = (32 * 1024 * 1024) << last_aper_order;
252 insert_aperture_resource((u32)last_aper_base, n);
253 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254 return;
Aaron Durbin56dd6692006-09-26 10:52:40 +0200255 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256
257 if (!fallback_aper_force)
258 aper_alloc = search_agp_bridge(&aper_order, &valid_agp);
259
260 if (aper_alloc) {
261 /* Got the aperture from the AGP bridge */
Andi Kleen63f02fd2005-09-12 18:49:24 +0200262 } else if (swiotlb && !valid_agp) {
263 /* Do nothing */
Jon Mason60b08c62006-02-26 04:18:22 +0100264 } else if ((!no_iommu && end_pfn > MAX_DMA32_PFN) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265 force_iommu ||
266 valid_agp ||
267 fallback_aper_force) {
268 printk("Your BIOS doesn't leave a aperture memory hole\n");
269 printk("Please enable the IOMMU option in the BIOS setup\n");
Andrew Morton42442ed2005-06-08 15:49:25 -0700270 printk("This costs you %d MB of RAM\n",
271 32 << fallback_aper_order);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272
273 aper_order = fallback_aper_order;
274 aper_alloc = allocate_aperture();
275 if (!aper_alloc) {
276 /* Could disable AGP and IOMMU here, but it's probably
277 not worth it. But the later users cannot deal with
278 bad apertures and turning on the aperture over memory
279 causes very strange problems, so it's better to
280 panic early. */
281 panic("Not enough memory for aperture");
282 }
283 } else {
284 return;
285 }
286
287 /* Fix up the north bridges */
288 for (num = 24; num < 32; num++) {
Andi Kleena32073b2006-06-26 13:56:40 +0200289 if (!early_is_k8_nb(read_pci_config(0, num, 3, 0x00)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290 continue;
291
292 /* Don't enable translation yet. That is done later.
293 Assume this BIOS didn't initialise the GART so
294 just overwrite all previous bits */
295 write_pci_config(0, num, 3, 0x90, aper_order<<1);
296 write_pci_config(0, num, 3, 0x94, aper_alloc>>25);
297 }
298}