blob: c7f4fdd20f05ff78ee731054170de372f9c0cf40 [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.
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
Andrew Morton42442ed2005-06-08 15:49:25 -070036/* This code runs before the PCI subsystem is initialized, so just
37 access the northbridge directly. */
Linus Torvalds1da177e2005-04-16 15:20:36 -070038
Andrew Morton42442ed2005-06-08 15:49:25 -070039#define NB_ID_3 (PCI_VENDOR_ID_AMD | (0x1103<<16))
Andi Kleenece90302005-04-16 15:25:13 -070040
Linus Torvalds1da177e2005-04-16 15:20:36 -070041static u32 __init allocate_aperture(void)
42{
Linus Torvalds1da177e2005-04-16 15:20:36 -070043 pg_data_t *nd0 = NODE_DATA(0);
Linus Torvalds1da177e2005-04-16 15:20:36 -070044 u32 aper_size;
45 void *p;
46
47 if (fallback_aper_order > 7)
48 fallback_aper_order = 7;
49 aper_size = (32 * 1024 * 1024) << fallback_aper_order;
50
51 /*
Andrew Morton42442ed2005-06-08 15:49:25 -070052 * Aperture has to be naturally aligned. This means an 2GB aperture won't
53 * have much chances to find a place in the lower 4GB of memory.
54 * Unfortunately we cannot move it up because that would make the
55 * IOMMU useless.
Linus Torvalds1da177e2005-04-16 15:20:36 -070056 */
57 p = __alloc_bootmem_node(nd0, aper_size, aper_size, 0);
58 if (!p || __pa(p)+aper_size > 0xffffffff) {
59 printk("Cannot allocate aperture memory hole (%p,%uK)\n",
60 p, aper_size>>10);
61 if (p)
62 free_bootmem_node(nd0, (unsigned long)p, aper_size);
63 return 0;
64 }
Andrew Morton42442ed2005-06-08 15:49:25 -070065 printk("Mapping aperture over %d KB of RAM @ %lx\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -070066 aper_size >> 10, __pa(p));
67 return (u32)__pa(p);
68}
69
70static int __init aperture_valid(char *name, u64 aper_base, u32 aper_size)
71{
72 if (!aper_base)
73 return 0;
74 if (aper_size < 64*1024*1024) {
75 printk("Aperture from %s too small (%d MB)\n", name, aper_size>>20);
76 return 0;
77 }
78 if (aper_base + aper_size >= 0xffffffff) {
79 printk("Aperture from %s beyond 4GB. Ignoring.\n",name);
80 return 0;
81 }
82 if (e820_mapped(aper_base, aper_base + aper_size, E820_RAM)) {
83 printk("Aperture from %s pointing to e820 RAM. Ignoring.\n",name);
84 return 0;
85 }
86 return 1;
87}
88
Andrew Morton42442ed2005-06-08 15:49:25 -070089/* Find a PCI capability */
Linus Torvalds1da177e2005-04-16 15:20:36 -070090static __u32 __init find_cap(int num, int slot, int func, int cap)
91{
92 u8 pos;
93 int bytes;
94 if (!(read_pci_config_16(num,slot,func,PCI_STATUS) & PCI_STATUS_CAP_LIST))
95 return 0;
96 pos = read_pci_config_byte(num,slot,func,PCI_CAPABILITY_LIST);
97 for (bytes = 0; bytes < 48 && pos >= 0x40; bytes++) {
98 u8 id;
99 pos &= ~3;
100 id = read_pci_config_byte(num,slot,func,pos+PCI_CAP_LIST_ID);
101 if (id == 0xff)
102 break;
103 if (id == cap)
104 return pos;
105 pos = read_pci_config_byte(num,slot,func,pos+PCI_CAP_LIST_NEXT);
106 }
107 return 0;
108}
109
110/* Read a standard AGPv3 bridge header */
111static __u32 __init read_agp(int num, int slot, int func, int cap, u32 *order)
112{
113 u32 apsize;
114 u32 apsizereg;
115 int nbits;
116 u32 aper_low, aper_hi;
117 u64 aper;
118
119 printk("AGP bridge at %02x:%02x:%02x\n", num, slot, func);
120 apsizereg = read_pci_config_16(num,slot,func, cap + 0x14);
121 if (apsizereg == 0xffffffff) {
122 printk("APSIZE in AGP bridge unreadable\n");
123 return 0;
124 }
125
126 apsize = apsizereg & 0xfff;
127 /* Some BIOS use weird encodings not in the AGPv3 table. */
128 if (apsize & 0xff)
129 apsize |= 0xf00;
130 nbits = hweight16(apsize);
131 *order = 7 - nbits;
132 if ((int)*order < 0) /* < 32MB */
133 *order = 0;
134
135 aper_low = read_pci_config(num,slot,func, 0x10);
136 aper_hi = read_pci_config(num,slot,func,0x14);
137 aper = (aper_low & ~((1<<22)-1)) | ((u64)aper_hi << 32);
138
139 printk("Aperture from AGP @ %Lx size %u MB (APSIZE %x)\n",
140 aper, 32 << *order, apsizereg);
141
142 if (!aperture_valid("AGP bridge", aper, (32*1024*1024) << *order))
143 return 0;
144 return (u32)aper;
145}
146
147/* Look for an AGP bridge. Windows only expects the aperture in the
148 AGP bridge and some BIOS forget to initialize the Northbridge too.
149 Work around this here.
150
151 Do an PCI bus scan by hand because we're running before the PCI
152 subsystem.
153
154 All K8 AGP bridges are AGPv3 compliant, so we can do this scan
155 generically. It's probably overkill to always scan all slots because
156 the AGP bridges should be always an own bus on the HT hierarchy,
157 but do it here for future safety. */
158static __u32 __init search_agp_bridge(u32 *order, int *valid_agp)
159{
160 int num, slot, func;
161
162 /* Poor man's PCI discovery */
163 for (num = 0; num < 32; num++) {
164 for (slot = 0; slot < 32; slot++) {
165 for (func = 0; func < 8; func++) {
166 u32 class, cap;
167 u8 type;
168 class = read_pci_config(num,slot,func,
169 PCI_CLASS_REVISION);
170 if (class == 0xffffffff)
171 break;
172
173 switch (class >> 16) {
174 case PCI_CLASS_BRIDGE_HOST:
175 case PCI_CLASS_BRIDGE_OTHER: /* needed? */
176 /* AGP bridge? */
177 cap = find_cap(num,slot,func,PCI_CAP_ID_AGP);
178 if (!cap)
179 break;
180 *valid_agp = 1;
181 return read_agp(num,slot,func,cap,order);
182 }
183
184 /* No multi-function device? */
185 type = read_pci_config_byte(num,slot,func,
186 PCI_HEADER_TYPE);
187 if (!(type & 0x80))
188 break;
189 }
190 }
191 }
192 printk("No AGP bridge found\n");
193 return 0;
194}
195
196void __init iommu_hole_init(void)
197{
198 int fix, num;
Andi Kleen50895c52005-11-05 17:25:53 +0100199 u32 aper_size, aper_alloc = 0, aper_order = 0, last_aper_order = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200 u64 aper_base, last_aper_base = 0;
201 int valid_agp = 0;
202
203 if (iommu_aperture_disabled || !fix_aperture)
204 return;
205
206 printk("Checking aperture...\n");
207
208 fix = 0;
209 for (num = 24; num < 32; num++) {
210 char name[30];
211 if (read_pci_config(0, num, 3, 0x00) != NB_ID_3)
212 continue;
213
214 iommu_aperture = 1;
215
216 aper_order = (read_pci_config(0, num, 3, 0x90) >> 1) & 7;
217 aper_size = (32 * 1024 * 1024) << aper_order;
218 aper_base = read_pci_config(0, num, 3, 0x94) & 0x7fff;
219 aper_base <<= 25;
220
221 printk("CPU %d: aperture @ %Lx size %u MB\n", num-24,
222 aper_base, aper_size>>20);
223
224 sprintf(name, "northbridge cpu %d", num-24);
225
226 if (!aperture_valid(name, aper_base, aper_size)) {
227 fix = 1;
228 break;
229 }
230
231 if ((last_aper_order && aper_order != last_aper_order) ||
232 (last_aper_base && aper_base != last_aper_base)) {
233 fix = 1;
234 break;
235 }
236 last_aper_order = aper_order;
237 last_aper_base = aper_base;
238 }
239
240 if (!fix && !fallback_aper_force)
241 return;
242
243 if (!fallback_aper_force)
244 aper_alloc = search_agp_bridge(&aper_order, &valid_agp);
245
246 if (aper_alloc) {
247 /* Got the aperture from the AGP bridge */
Andi Kleen63f02fd2005-09-12 18:49:24 +0200248 } else if (swiotlb && !valid_agp) {
249 /* Do nothing */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250 } else if ((!no_iommu && end_pfn >= 0xffffffff>>PAGE_SHIFT) ||
251 force_iommu ||
252 valid_agp ||
253 fallback_aper_force) {
254 printk("Your BIOS doesn't leave a aperture memory hole\n");
255 printk("Please enable the IOMMU option in the BIOS setup\n");
Andrew Morton42442ed2005-06-08 15:49:25 -0700256 printk("This costs you %d MB of RAM\n",
257 32 << fallback_aper_order);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258
259 aper_order = fallback_aper_order;
260 aper_alloc = allocate_aperture();
261 if (!aper_alloc) {
262 /* Could disable AGP and IOMMU here, but it's probably
263 not worth it. But the later users cannot deal with
264 bad apertures and turning on the aperture over memory
265 causes very strange problems, so it's better to
266 panic early. */
267 panic("Not enough memory for aperture");
268 }
269 } else {
270 return;
271 }
272
273 /* Fix up the north bridges */
274 for (num = 24; num < 32; num++) {
275 if (read_pci_config(0, num, 3, 0x00) != NB_ID_3)
276 continue;
277
278 /* Don't enable translation yet. That is done later.
279 Assume this BIOS didn't initialise the GART so
280 just overwrite all previous bits */
281 write_pci_config(0, num, 3, 0x90, aper_order<<1);
282 write_pci_config(0, num, 3, 0x94, aper_alloc>>25);
283 }
284}