xref: /linux/arch/x86/kernel/aperture_64.c (revision a5d3244a0b1c16963fd7ceadf76da843df27c3c5)
1250c2277SThomas Gleixner /*
2250c2277SThomas Gleixner  * Firmware replacement code.
3250c2277SThomas Gleixner  *
48caac563SPavel Machek  * Work around broken BIOSes that don't set an aperture, only set the
58caac563SPavel Machek  * aperture in the AGP bridge, or set too small aperture.
68caac563SPavel Machek  *
7250c2277SThomas Gleixner  * If all fails map the aperture over some low memory.  This is cheaper than
8250c2277SThomas Gleixner  * doing bounce buffering. The memory is lost. This is done at early boot
9250c2277SThomas Gleixner  * because only the bootmem allocator can allocate 32+MB.
10250c2277SThomas Gleixner  *
11250c2277SThomas Gleixner  * Copyright 2002 Andi Kleen, SuSE Labs.
12250c2277SThomas Gleixner  */
13*a5d3244aSBjorn Helgaas #define pr_fmt(fmt) "AGP: " fmt
14*a5d3244aSBjorn Helgaas 
15250c2277SThomas Gleixner #include <linux/kernel.h>
16250c2277SThomas Gleixner #include <linux/types.h>
17250c2277SThomas Gleixner #include <linux/init.h>
1832e3f2b0SYinghai Lu #include <linux/memblock.h>
19250c2277SThomas Gleixner #include <linux/mmzone.h>
20250c2277SThomas Gleixner #include <linux/pci_ids.h>
21250c2277SThomas Gleixner #include <linux/pci.h>
22250c2277SThomas Gleixner #include <linux/bitops.h>
232050d45dSPavel Machek #include <linux/suspend.h>
24250c2277SThomas Gleixner #include <asm/e820.h>
25250c2277SThomas Gleixner #include <asm/io.h>
2646a7fa27SFUJITA Tomonori #include <asm/iommu.h>
27395624fcSJoerg Roedel #include <asm/gart.h>
28250c2277SThomas Gleixner #include <asm/pci-direct.h>
29250c2277SThomas Gleixner #include <asm/dma.h>
3023ac4ae8SAndreas Herrmann #include <asm/amd_nb.h>
31de957628SFUJITA Tomonori #include <asm/x86_init.h>
32250c2277SThomas Gleixner 
33c387aa3aSJoerg Roedel /*
34c387aa3aSJoerg Roedel  * Using 512M as goal, in case kexec will load kernel_big
35c387aa3aSJoerg Roedel  * that will do the on-position decompress, and could overlap with
36c387aa3aSJoerg Roedel  * with the gart aperture that is used.
37c387aa3aSJoerg Roedel  * Sequence:
38c387aa3aSJoerg Roedel  * kernel_small
39c387aa3aSJoerg Roedel  * ==> kexec (with kdump trigger path or gart still enabled)
40c387aa3aSJoerg Roedel  * ==> kernel_small (gart area become e820_reserved)
41c387aa3aSJoerg Roedel  * ==> kexec (with kdump trigger path or gart still enabled)
42c387aa3aSJoerg Roedel  * ==> kerne_big (uncompressed size will be big than 64M or 128M)
43c387aa3aSJoerg Roedel  * So don't use 512M below as gart iommu, leave the space for kernel
44c387aa3aSJoerg Roedel  * code for safe.
45c387aa3aSJoerg Roedel  */
46c387aa3aSJoerg Roedel #define GART_MIN_ADDR	(512ULL << 20)
47c387aa3aSJoerg Roedel #define GART_MAX_ADDR	(1ULL   << 32)
48c387aa3aSJoerg Roedel 
490440d4c0SJoerg Roedel int gart_iommu_aperture;
507de6a4cdSPavel Machek int gart_iommu_aperture_disabled __initdata;
517de6a4cdSPavel Machek int gart_iommu_aperture_allowed __initdata;
52250c2277SThomas Gleixner 
53250c2277SThomas Gleixner int fallback_aper_order __initdata = 1; /* 64MB */
547de6a4cdSPavel Machek int fallback_aper_force __initdata;
55250c2277SThomas Gleixner 
56250c2277SThomas Gleixner int fix_aperture __initdata = 1;
57250c2277SThomas Gleixner 
58250c2277SThomas Gleixner /* This code runs before the PCI subsystem is initialized, so just
59250c2277SThomas Gleixner    access the northbridge directly. */
60250c2277SThomas Gleixner 
61250c2277SThomas Gleixner static u32 __init allocate_aperture(void)
62250c2277SThomas Gleixner {
63250c2277SThomas Gleixner 	u32 aper_size;
6432e3f2b0SYinghai Lu 	unsigned long addr;
65250c2277SThomas Gleixner 
667677b2efSYinghai Lu 	/* aper_size should <= 1G */
677677b2efSYinghai Lu 	if (fallback_aper_order > 5)
687677b2efSYinghai Lu 		fallback_aper_order = 5;
69250c2277SThomas Gleixner 	aper_size = (32 * 1024 * 1024) << fallback_aper_order;
70250c2277SThomas Gleixner 
71250c2277SThomas Gleixner 	/*
72c140df97SIngo Molnar 	 * Aperture has to be naturally aligned. This means a 2GB aperture
73c140df97SIngo Molnar 	 * won't have much chance of finding a place in the lower 4GB of
74c140df97SIngo Molnar 	 * memory. Unfortunately we cannot move it up because that would
75c140df97SIngo Molnar 	 * make the IOMMU useless.
76250c2277SThomas Gleixner 	 */
77c387aa3aSJoerg Roedel 	addr = memblock_find_in_range(GART_MIN_ADDR, GART_MAX_ADDR,
78c387aa3aSJoerg Roedel 				      aper_size, aper_size);
7926bfc540SWang YanQing 	if (!addr) {
80*a5d3244aSBjorn Helgaas 		pr_err("Cannot allocate aperture memory hole (%lx,%uK)\n",
8132e3f2b0SYinghai Lu 		       addr, aper_size>>10);
8232e3f2b0SYinghai Lu 		return 0;
8332e3f2b0SYinghai Lu 	}
8424aa0788STejun Heo 	memblock_reserve(addr, aper_size);
85*a5d3244aSBjorn Helgaas 	pr_info("Mapping aperture over %d KB of RAM @ %lx\n", aper_size >> 10,
86*a5d3244aSBjorn Helgaas 		addr);
8732e3f2b0SYinghai Lu 	register_nosave_region(addr >> PAGE_SHIFT,
8832e3f2b0SYinghai Lu 			       (addr+aper_size) >> PAGE_SHIFT);
89c140df97SIngo Molnar 
9032e3f2b0SYinghai Lu 	return (u32)addr;
91250c2277SThomas Gleixner }
92250c2277SThomas Gleixner 
93250c2277SThomas Gleixner 
94250c2277SThomas Gleixner /* Find a PCI capability */
95dd564d0cSPavel Machek static u32 __init find_cap(int bus, int slot, int func, int cap)
96250c2277SThomas Gleixner {
97250c2277SThomas Gleixner 	int bytes;
98c140df97SIngo Molnar 	u8 pos;
99c140df97SIngo Molnar 
10055c0d721SYinghai Lu 	if (!(read_pci_config_16(bus, slot, func, PCI_STATUS) &
101c140df97SIngo Molnar 						PCI_STATUS_CAP_LIST))
102250c2277SThomas Gleixner 		return 0;
103c140df97SIngo Molnar 
10455c0d721SYinghai Lu 	pos = read_pci_config_byte(bus, slot, func, PCI_CAPABILITY_LIST);
105250c2277SThomas Gleixner 	for (bytes = 0; bytes < 48 && pos >= 0x40; bytes++) {
106250c2277SThomas Gleixner 		u8 id;
107c140df97SIngo Molnar 
108250c2277SThomas Gleixner 		pos &= ~3;
10955c0d721SYinghai Lu 		id = read_pci_config_byte(bus, slot, func, pos+PCI_CAP_LIST_ID);
110250c2277SThomas Gleixner 		if (id == 0xff)
111250c2277SThomas Gleixner 			break;
112250c2277SThomas Gleixner 		if (id == cap)
113250c2277SThomas Gleixner 			return pos;
11455c0d721SYinghai Lu 		pos = read_pci_config_byte(bus, slot, func,
115c140df97SIngo Molnar 						pos+PCI_CAP_LIST_NEXT);
116250c2277SThomas Gleixner 	}
117250c2277SThomas Gleixner 	return 0;
118250c2277SThomas Gleixner }
119250c2277SThomas Gleixner 
120250c2277SThomas Gleixner /* Read a standard AGPv3 bridge header */
121dd564d0cSPavel Machek static u32 __init read_agp(int bus, int slot, int func, int cap, u32 *order)
122250c2277SThomas Gleixner {
123250c2277SThomas Gleixner 	u32 apsize;
124250c2277SThomas Gleixner 	u32 apsizereg;
125250c2277SThomas Gleixner 	int nbits;
126250c2277SThomas Gleixner 	u32 aper_low, aper_hi;
127250c2277SThomas Gleixner 	u64 aper;
1281edc1ab3SYinghai Lu 	u32 old_order;
129250c2277SThomas Gleixner 
130*a5d3244aSBjorn Helgaas 	pr_info("AGP bridge at %02x:%02x:%02x\n", bus, slot, func);
13155c0d721SYinghai Lu 	apsizereg = read_pci_config_16(bus, slot, func, cap + 0x14);
132250c2277SThomas Gleixner 	if (apsizereg == 0xffffffff) {
133*a5d3244aSBjorn Helgaas 		pr_err("APSIZE in AGP bridge unreadable\n");
134250c2277SThomas Gleixner 		return 0;
135250c2277SThomas Gleixner 	}
136250c2277SThomas Gleixner 
1371edc1ab3SYinghai Lu 	/* old_order could be the value from NB gart setting */
1381edc1ab3SYinghai Lu 	old_order = *order;
1391edc1ab3SYinghai Lu 
140250c2277SThomas Gleixner 	apsize = apsizereg & 0xfff;
141250c2277SThomas Gleixner 	/* Some BIOS use weird encodings not in the AGPv3 table. */
142250c2277SThomas Gleixner 	if (apsize & 0xff)
143250c2277SThomas Gleixner 		apsize |= 0xf00;
144250c2277SThomas Gleixner 	nbits = hweight16(apsize);
145250c2277SThomas Gleixner 	*order = 7 - nbits;
146250c2277SThomas Gleixner 	if ((int)*order < 0) /* < 32MB */
147250c2277SThomas Gleixner 		*order = 0;
148250c2277SThomas Gleixner 
14955c0d721SYinghai Lu 	aper_low = read_pci_config(bus, slot, func, 0x10);
15055c0d721SYinghai Lu 	aper_hi = read_pci_config(bus, slot, func, 0x14);
151250c2277SThomas Gleixner 	aper = (aper_low & ~((1<<22)-1)) | ((u64)aper_hi << 32);
152250c2277SThomas Gleixner 
1531edc1ab3SYinghai Lu 	/*
1541edc1ab3SYinghai Lu 	 * On some sick chips, APSIZE is 0. It means it wants 4G
1551edc1ab3SYinghai Lu 	 * so let double check that order, and lets trust AMD NB settings:
1561edc1ab3SYinghai Lu 	 */
157*a5d3244aSBjorn Helgaas 	pr_info("Aperture from AGP @ %Lx old size %u MB\n",
1588c9fd91aSYinghai Lu 		aper, 32 << old_order);
1598c9fd91aSYinghai Lu 	if (aper + (32ULL<<(20 + *order)) > 0x100000000ULL) {
160*a5d3244aSBjorn Helgaas 		pr_info("Aperture size %u MB (APSIZE %x) is not right, using settings from NB\n",
1611edc1ab3SYinghai Lu 			32 << *order, apsizereg);
1621edc1ab3SYinghai Lu 		*order = old_order;
1631edc1ab3SYinghai Lu 	}
1641edc1ab3SYinghai Lu 
165*a5d3244aSBjorn Helgaas 	pr_info("Aperture from AGP @ %Lx size %u MB (APSIZE %x)\n", aper,
166*a5d3244aSBjorn Helgaas 		32 << *order, apsizereg);
167250c2277SThomas Gleixner 
1688c9fd91aSYinghai Lu 	if (!aperture_valid(aper, (32*1024*1024) << *order, 32<<20))
169250c2277SThomas Gleixner 		return 0;
170250c2277SThomas Gleixner 	return (u32)aper;
171250c2277SThomas Gleixner }
172250c2277SThomas Gleixner 
173c140df97SIngo Molnar /*
174c140df97SIngo Molnar  * Look for an AGP bridge. Windows only expects the aperture in the
175c140df97SIngo Molnar  * AGP bridge and some BIOS forget to initialize the Northbridge too.
176c140df97SIngo Molnar  * Work around this here.
177c140df97SIngo Molnar  *
178c140df97SIngo Molnar  * Do an PCI bus scan by hand because we're running before the PCI
179c140df97SIngo Molnar  * subsystem.
180c140df97SIngo Molnar  *
181eec1d4faSHans Rosenfeld  * All AMD AGP bridges are AGPv3 compliant, so we can do this scan
182c140df97SIngo Molnar  * generically. It's probably overkill to always scan all slots because
183c140df97SIngo Molnar  * the AGP bridges should be always an own bus on the HT hierarchy,
184c140df97SIngo Molnar  * but do it here for future safety.
185c140df97SIngo Molnar  */
186dd564d0cSPavel Machek static u32 __init search_agp_bridge(u32 *order, int *valid_agp)
187250c2277SThomas Gleixner {
18855c0d721SYinghai Lu 	int bus, slot, func;
189250c2277SThomas Gleixner 
190250c2277SThomas Gleixner 	/* Poor man's PCI discovery */
19155c0d721SYinghai Lu 	for (bus = 0; bus < 256; bus++) {
192250c2277SThomas Gleixner 		for (slot = 0; slot < 32; slot++) {
193250c2277SThomas Gleixner 			for (func = 0; func < 8; func++) {
194250c2277SThomas Gleixner 				u32 class, cap;
195250c2277SThomas Gleixner 				u8 type;
19655c0d721SYinghai Lu 				class = read_pci_config(bus, slot, func,
197250c2277SThomas Gleixner 							PCI_CLASS_REVISION);
198250c2277SThomas Gleixner 				if (class == 0xffffffff)
199250c2277SThomas Gleixner 					break;
200250c2277SThomas Gleixner 
201250c2277SThomas Gleixner 				switch (class >> 16) {
202250c2277SThomas Gleixner 				case PCI_CLASS_BRIDGE_HOST:
203250c2277SThomas Gleixner 				case PCI_CLASS_BRIDGE_OTHER: /* needed? */
204250c2277SThomas Gleixner 					/* AGP bridge? */
20555c0d721SYinghai Lu 					cap = find_cap(bus, slot, func,
206c140df97SIngo Molnar 							PCI_CAP_ID_AGP);
207250c2277SThomas Gleixner 					if (!cap)
208250c2277SThomas Gleixner 						break;
209250c2277SThomas Gleixner 					*valid_agp = 1;
21055c0d721SYinghai Lu 					return read_agp(bus, slot, func, cap,
211c140df97SIngo Molnar 							order);
212250c2277SThomas Gleixner 				}
213250c2277SThomas Gleixner 
214250c2277SThomas Gleixner 				/* No multi-function device? */
21555c0d721SYinghai Lu 				type = read_pci_config_byte(bus, slot, func,
216250c2277SThomas Gleixner 							       PCI_HEADER_TYPE);
217250c2277SThomas Gleixner 				if (!(type & 0x80))
218250c2277SThomas Gleixner 					break;
219250c2277SThomas Gleixner 			}
220250c2277SThomas Gleixner 		}
221250c2277SThomas Gleixner 	}
222*a5d3244aSBjorn Helgaas 	pr_info("No AGP bridge found\n");
223c140df97SIngo Molnar 
224250c2277SThomas Gleixner 	return 0;
225250c2277SThomas Gleixner }
226250c2277SThomas Gleixner 
227aaf23042SYinghai Lu static int gart_fix_e820 __initdata = 1;
228aaf23042SYinghai Lu 
229aaf23042SYinghai Lu static int __init parse_gart_mem(char *p)
230aaf23042SYinghai Lu {
231aaf23042SYinghai Lu 	if (!p)
232aaf23042SYinghai Lu 		return -EINVAL;
233aaf23042SYinghai Lu 
234aaf23042SYinghai Lu 	if (!strncmp(p, "off", 3))
235aaf23042SYinghai Lu 		gart_fix_e820 = 0;
236aaf23042SYinghai Lu 	else if (!strncmp(p, "on", 2))
237aaf23042SYinghai Lu 		gart_fix_e820 = 1;
238aaf23042SYinghai Lu 
239aaf23042SYinghai Lu 	return 0;
240aaf23042SYinghai Lu }
241aaf23042SYinghai Lu early_param("gart_fix_e820", parse_gart_mem);
242aaf23042SYinghai Lu 
243aaf23042SYinghai Lu void __init early_gart_iommu_check(void)
244aaf23042SYinghai Lu {
245aaf23042SYinghai Lu 	/*
246aaf23042SYinghai Lu 	 * in case it is enabled before, esp for kexec/kdump,
247aaf23042SYinghai Lu 	 * previous kernel already enable that. memset called
248aaf23042SYinghai Lu 	 * by allocate_aperture/__alloc_bootmem_nopanic cause restart.
249aaf23042SYinghai Lu 	 * or second kernel have different position for GART hole. and new
250aaf23042SYinghai Lu 	 * kernel could use hole as RAM that is still used by GART set by
251aaf23042SYinghai Lu 	 * first kernel
252aaf23042SYinghai Lu 	 * or BIOS forget to put that in reserved.
253aaf23042SYinghai Lu 	 * try to update e820 to make that region as reserved.
254aaf23042SYinghai Lu 	 */
255fa10ba64SAndi Kleen 	u32 agp_aper_order = 0;
256f3eee542SYinghai Lu 	int i, fix, slot, valid_agp = 0;
257aaf23042SYinghai Lu 	u32 ctl;
258aaf23042SYinghai Lu 	u32 aper_size = 0, aper_order = 0, last_aper_order = 0;
259aaf23042SYinghai Lu 	u64 aper_base = 0, last_aper_base = 0;
260fa5b8a30SPavel Machek 	int aper_enabled = 0, last_aper_enabled = 0, last_valid = 0;
261aaf23042SYinghai Lu 
262aaf23042SYinghai Lu 	if (!early_pci_allowed())
263aaf23042SYinghai Lu 		return;
264aaf23042SYinghai Lu 
265fa5b8a30SPavel Machek 	/* This is mostly duplicate of iommu_hole_init */
266fa10ba64SAndi Kleen 	search_agp_bridge(&agp_aper_order, &valid_agp);
267f3eee542SYinghai Lu 
268aaf23042SYinghai Lu 	fix = 0;
26924d9b70bSJan Beulich 	for (i = 0; amd_nb_bus_dev_ranges[i].dev_limit; i++) {
27055c0d721SYinghai Lu 		int bus;
27155c0d721SYinghai Lu 		int dev_base, dev_limit;
27255c0d721SYinghai Lu 
27324d9b70bSJan Beulich 		bus = amd_nb_bus_dev_ranges[i].bus;
27424d9b70bSJan Beulich 		dev_base = amd_nb_bus_dev_ranges[i].dev_base;
27524d9b70bSJan Beulich 		dev_limit = amd_nb_bus_dev_ranges[i].dev_limit;
27655c0d721SYinghai Lu 
27755c0d721SYinghai Lu 		for (slot = dev_base; slot < dev_limit; slot++) {
278eec1d4faSHans Rosenfeld 			if (!early_is_amd_nb(read_pci_config(bus, slot, 3, 0x00)))
279aaf23042SYinghai Lu 				continue;
280aaf23042SYinghai Lu 
28155c0d721SYinghai Lu 			ctl = read_pci_config(bus, slot, 3, AMD64_GARTAPERTURECTL);
28257ab43e3SBorislav Petkov 			aper_enabled = ctl & GARTEN;
283aaf23042SYinghai Lu 			aper_order = (ctl >> 1) & 7;
284aaf23042SYinghai Lu 			aper_size = (32 * 1024 * 1024) << aper_order;
28555c0d721SYinghai Lu 			aper_base = read_pci_config(bus, slot, 3, AMD64_GARTAPERTUREBASE) & 0x7fff;
286aaf23042SYinghai Lu 			aper_base <<= 25;
287aaf23042SYinghai Lu 
288fa5b8a30SPavel Machek 			if (last_valid) {
289fa5b8a30SPavel Machek 				if ((aper_order != last_aper_order) ||
290fa5b8a30SPavel Machek 				    (aper_base != last_aper_base) ||
291fa5b8a30SPavel Machek 				    (aper_enabled != last_aper_enabled)) {
292aaf23042SYinghai Lu 					fix = 1;
293fa5b8a30SPavel Machek 					break;
294aaf23042SYinghai Lu 				}
29555c0d721SYinghai Lu 			}
296aaf23042SYinghai Lu 
297fa5b8a30SPavel Machek 			last_aper_order = aper_order;
298fa5b8a30SPavel Machek 			last_aper_base = aper_base;
299fa5b8a30SPavel Machek 			last_aper_enabled = aper_enabled;
300fa5b8a30SPavel Machek 			last_valid = 1;
301fa5b8a30SPavel Machek 		}
302fa5b8a30SPavel Machek 	}
303fa5b8a30SPavel Machek 
304aaf23042SYinghai Lu 	if (!fix && !aper_enabled)
305aaf23042SYinghai Lu 		return;
306aaf23042SYinghai Lu 
307aaf23042SYinghai Lu 	if (!aper_base || !aper_size || aper_base + aper_size > 0x100000000UL)
308aaf23042SYinghai Lu 		fix = 1;
309aaf23042SYinghai Lu 
310aaf23042SYinghai Lu 	if (gart_fix_e820 && !fix && aper_enabled) {
3110754557dSYinghai Lu 		if (e820_any_mapped(aper_base, aper_base + aper_size,
3120754557dSYinghai Lu 				    E820_RAM)) {
3130abbc78aSPavel Machek 			/* reserve it, so we can reuse it in second kernel */
314*a5d3244aSBjorn Helgaas 			pr_info("update e820 for GART\n");
315d0be6bdeSYinghai Lu 			e820_add_region(aper_base, aper_size, E820_RESERVED);
316aaf23042SYinghai Lu 			update_e820();
317aaf23042SYinghai Lu 		}
318aaf23042SYinghai Lu 	}
319aaf23042SYinghai Lu 
320f3eee542SYinghai Lu 	if (valid_agp)
3214f384f8bSPavel Machek 		return;
3224f384f8bSPavel Machek 
323f3eee542SYinghai Lu 	/* disable them all at first */
32424d9b70bSJan Beulich 	for (i = 0; i < amd_nb_bus_dev_ranges[i].dev_limit; i++) {
32555c0d721SYinghai Lu 		int bus;
32655c0d721SYinghai Lu 		int dev_base, dev_limit;
32755c0d721SYinghai Lu 
32824d9b70bSJan Beulich 		bus = amd_nb_bus_dev_ranges[i].bus;
32924d9b70bSJan Beulich 		dev_base = amd_nb_bus_dev_ranges[i].dev_base;
33024d9b70bSJan Beulich 		dev_limit = amd_nb_bus_dev_ranges[i].dev_limit;
33155c0d721SYinghai Lu 
33255c0d721SYinghai Lu 		for (slot = dev_base; slot < dev_limit; slot++) {
333eec1d4faSHans Rosenfeld 			if (!early_is_amd_nb(read_pci_config(bus, slot, 3, 0x00)))
334aaf23042SYinghai Lu 				continue;
335aaf23042SYinghai Lu 
33655c0d721SYinghai Lu 			ctl = read_pci_config(bus, slot, 3, AMD64_GARTAPERTURECTL);
33757ab43e3SBorislav Petkov 			ctl &= ~GARTEN;
33855c0d721SYinghai Lu 			write_pci_config(bus, slot, 3, AMD64_GARTAPERTURECTL, ctl);
33955c0d721SYinghai Lu 		}
340aaf23042SYinghai Lu 	}
341aaf23042SYinghai Lu 
342aaf23042SYinghai Lu }
343aaf23042SYinghai Lu 
3448c9fd91aSYinghai Lu static int __initdata printed_gart_size_msg;
3458c9fd91aSYinghai Lu 
346480125baSKonrad Rzeszutek Wilk int __init gart_iommu_hole_init(void)
347250c2277SThomas Gleixner {
3488c9fd91aSYinghai Lu 	u32 agp_aper_base = 0, agp_aper_order = 0;
349250c2277SThomas Gleixner 	u32 aper_size, aper_alloc = 0, aper_order = 0, last_aper_order = 0;
350250c2277SThomas Gleixner 	u64 aper_base, last_aper_base = 0;
35155c0d721SYinghai Lu 	int fix, slot, valid_agp = 0;
35255c0d721SYinghai Lu 	int i, node;
353250c2277SThomas Gleixner 
3540440d4c0SJoerg Roedel 	if (gart_iommu_aperture_disabled || !fix_aperture ||
3550440d4c0SJoerg Roedel 	    !early_pci_allowed())
356480125baSKonrad Rzeszutek Wilk 		return -ENODEV;
357250c2277SThomas Gleixner 
358*a5d3244aSBjorn Helgaas 	pr_info("Checking aperture...\n");
359250c2277SThomas Gleixner 
3608c9fd91aSYinghai Lu 	if (!fallback_aper_force)
3618c9fd91aSYinghai Lu 		agp_aper_base = search_agp_bridge(&agp_aper_order, &valid_agp);
3628c9fd91aSYinghai Lu 
363250c2277SThomas Gleixner 	fix = 0;
36447db4c3eSYinghai Lu 	node = 0;
36524d9b70bSJan Beulich 	for (i = 0; i < amd_nb_bus_dev_ranges[i].dev_limit; i++) {
36655c0d721SYinghai Lu 		int bus;
36755c0d721SYinghai Lu 		int dev_base, dev_limit;
3684b83873dSJoerg Roedel 		u32 ctl;
36955c0d721SYinghai Lu 
37024d9b70bSJan Beulich 		bus = amd_nb_bus_dev_ranges[i].bus;
37124d9b70bSJan Beulich 		dev_base = amd_nb_bus_dev_ranges[i].dev_base;
37224d9b70bSJan Beulich 		dev_limit = amd_nb_bus_dev_ranges[i].dev_limit;
37355c0d721SYinghai Lu 
37455c0d721SYinghai Lu 		for (slot = dev_base; slot < dev_limit; slot++) {
375eec1d4faSHans Rosenfeld 			if (!early_is_amd_nb(read_pci_config(bus, slot, 3, 0x00)))
376250c2277SThomas Gleixner 				continue;
377250c2277SThomas Gleixner 
378250c2277SThomas Gleixner 			iommu_detected = 1;
3790440d4c0SJoerg Roedel 			gart_iommu_aperture = 1;
380de957628SFUJITA Tomonori 			x86_init.iommu.iommu_init = gart_iommu_init;
381250c2277SThomas Gleixner 
3824b83873dSJoerg Roedel 			ctl = read_pci_config(bus, slot, 3,
3834b83873dSJoerg Roedel 					      AMD64_GARTAPERTURECTL);
3844b83873dSJoerg Roedel 
3854b83873dSJoerg Roedel 			/*
3864b83873dSJoerg Roedel 			 * Before we do anything else disable the GART. It may
3874b83873dSJoerg Roedel 			 * still be enabled if we boot into a crash-kernel here.
3884b83873dSJoerg Roedel 			 * Reconfiguring the GART while it is enabled could have
3894b83873dSJoerg Roedel 			 * unknown side-effects.
3904b83873dSJoerg Roedel 			 */
3914b83873dSJoerg Roedel 			ctl &= ~GARTEN;
3924b83873dSJoerg Roedel 			write_pci_config(bus, slot, 3, AMD64_GARTAPERTURECTL, ctl);
3934b83873dSJoerg Roedel 
3944b83873dSJoerg Roedel 			aper_order = (ctl >> 1) & 7;
395250c2277SThomas Gleixner 			aper_size = (32 * 1024 * 1024) << aper_order;
39655c0d721SYinghai Lu 			aper_base = read_pci_config(bus, slot, 3, AMD64_GARTAPERTUREBASE) & 0x7fff;
397250c2277SThomas Gleixner 			aper_base <<= 25;
398250c2277SThomas Gleixner 
399*a5d3244aSBjorn Helgaas 			pr_info("Node %d: aperture @ %Lx size %u MB\n",
40047db4c3eSYinghai Lu 				node, aper_base, aper_size >> 20);
40147db4c3eSYinghai Lu 			node++;
402250c2277SThomas Gleixner 
4038c9fd91aSYinghai Lu 			if (!aperture_valid(aper_base, aper_size, 64<<20)) {
4048c9fd91aSYinghai Lu 				if (valid_agp && agp_aper_base &&
4058c9fd91aSYinghai Lu 				    agp_aper_base == aper_base &&
4068c9fd91aSYinghai Lu 				    agp_aper_order == aper_order) {
4078c9fd91aSYinghai Lu 					/* the same between two setting from NB and agp */
408c987d12fSYinghai Lu 					if (!no_iommu &&
409c987d12fSYinghai Lu 					    max_pfn > MAX_DMA32_PFN &&
410c987d12fSYinghai Lu 					    !printed_gart_size_msg) {
411*a5d3244aSBjorn Helgaas 						pr_err("you are using iommu with agp, but GART size is less than 64M\n");
412*a5d3244aSBjorn Helgaas 						pr_err("please increase GART size in your BIOS setup\n");
413*a5d3244aSBjorn Helgaas 						pr_err("if BIOS doesn't have that option, contact your HW vendor!\n");
4148c9fd91aSYinghai Lu 						printed_gart_size_msg = 1;
4158c9fd91aSYinghai Lu 					}
4168c9fd91aSYinghai Lu 				} else {
417250c2277SThomas Gleixner 					fix = 1;
41855c0d721SYinghai Lu 					goto out;
419250c2277SThomas Gleixner 				}
4208c9fd91aSYinghai Lu 			}
421250c2277SThomas Gleixner 
422250c2277SThomas Gleixner 			if ((last_aper_order && aper_order != last_aper_order) ||
423250c2277SThomas Gleixner 			    (last_aper_base && aper_base != last_aper_base)) {
424250c2277SThomas Gleixner 				fix = 1;
42555c0d721SYinghai Lu 				goto out;
426250c2277SThomas Gleixner 			}
427250c2277SThomas Gleixner 			last_aper_order = aper_order;
428250c2277SThomas Gleixner 			last_aper_base = aper_base;
429250c2277SThomas Gleixner 		}
43055c0d721SYinghai Lu 	}
431250c2277SThomas Gleixner 
43255c0d721SYinghai Lu out:
433250c2277SThomas Gleixner 	if (!fix && !fallback_aper_force) {
434707d4eefSBjorn Helgaas 		if (last_aper_base)
435480125baSKonrad Rzeszutek Wilk 			return 1;
436480125baSKonrad Rzeszutek Wilk 		return 0;
437250c2277SThomas Gleixner 	}
438250c2277SThomas Gleixner 
4398c9fd91aSYinghai Lu 	if (!fallback_aper_force) {
4408c9fd91aSYinghai Lu 		aper_alloc = agp_aper_base;
4418c9fd91aSYinghai Lu 		aper_order = agp_aper_order;
4428c9fd91aSYinghai Lu 	}
443250c2277SThomas Gleixner 
444250c2277SThomas Gleixner 	if (aper_alloc) {
445250c2277SThomas Gleixner 		/* Got the aperture from the AGP bridge */
446c987d12fSYinghai Lu 	} else if ((!no_iommu && max_pfn > MAX_DMA32_PFN) ||
447250c2277SThomas Gleixner 		   force_iommu ||
448250c2277SThomas Gleixner 		   valid_agp ||
449250c2277SThomas Gleixner 		   fallback_aper_force) {
450*a5d3244aSBjorn Helgaas 		pr_info("Your BIOS doesn't leave a aperture memory hole\n");
451*a5d3244aSBjorn Helgaas 		pr_info("Please enable the IOMMU option in the BIOS setup\n");
452*a5d3244aSBjorn Helgaas 		pr_info("This costs you %d MB of RAM\n",
453250c2277SThomas Gleixner 			32 << fallback_aper_order);
454250c2277SThomas Gleixner 
455250c2277SThomas Gleixner 		aper_order = fallback_aper_order;
456250c2277SThomas Gleixner 		aper_alloc = allocate_aperture();
457250c2277SThomas Gleixner 		if (!aper_alloc) {
458c140df97SIngo Molnar 			/*
459c140df97SIngo Molnar 			 * Could disable AGP and IOMMU here, but it's
460c140df97SIngo Molnar 			 * probably not worth it. But the later users
461c140df97SIngo Molnar 			 * cannot deal with bad apertures and turning
462c140df97SIngo Molnar 			 * on the aperture over memory causes very
463c140df97SIngo Molnar 			 * strange problems, so it's better to panic
464c140df97SIngo Molnar 			 * early.
465c140df97SIngo Molnar 			 */
466250c2277SThomas Gleixner 			panic("Not enough memory for aperture");
467250c2277SThomas Gleixner 		}
468250c2277SThomas Gleixner 	} else {
469480125baSKonrad Rzeszutek Wilk 		return 0;
470250c2277SThomas Gleixner 	}
471250c2277SThomas Gleixner 
472250c2277SThomas Gleixner 	/* Fix up the north bridges */
47324d9b70bSJan Beulich 	for (i = 0; i < amd_nb_bus_dev_ranges[i].dev_limit; i++) {
474260133abSBorislav Petkov 		int bus, dev_base, dev_limit;
475260133abSBorislav Petkov 
476260133abSBorislav Petkov 		/*
477260133abSBorislav Petkov 		 * Don't enable translation yet but enable GART IO and CPU
478260133abSBorislav Petkov 		 * accesses and set DISTLBWALKPRB since GART table memory is UC.
479260133abSBorislav Petkov 		 */
480c34151a7SJoerg Roedel 		u32 ctl = aper_order << 1;
48155c0d721SYinghai Lu 
48224d9b70bSJan Beulich 		bus = amd_nb_bus_dev_ranges[i].bus;
48324d9b70bSJan Beulich 		dev_base = amd_nb_bus_dev_ranges[i].dev_base;
48424d9b70bSJan Beulich 		dev_limit = amd_nb_bus_dev_ranges[i].dev_limit;
48555c0d721SYinghai Lu 		for (slot = dev_base; slot < dev_limit; slot++) {
486eec1d4faSHans Rosenfeld 			if (!early_is_amd_nb(read_pci_config(bus, slot, 3, 0x00)))
487250c2277SThomas Gleixner 				continue;
488250c2277SThomas Gleixner 
489260133abSBorislav Petkov 			write_pci_config(bus, slot, 3, AMD64_GARTAPERTURECTL, ctl);
49055c0d721SYinghai Lu 			write_pci_config(bus, slot, 3, AMD64_GARTAPERTUREBASE, aper_alloc >> 25);
49155c0d721SYinghai Lu 		}
492250c2277SThomas Gleixner 	}
4936703f6d1SRafael J. Wysocki 
4946703f6d1SRafael J. Wysocki 	set_up_gart_resume(aper_order, aper_alloc);
495480125baSKonrad Rzeszutek Wilk 
496480125baSKonrad Rzeszutek Wilk 	return 1;
497250c2277SThomas Gleixner }
498