amd64-agp.c 20 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/*
 * Copyright 2001-2003 SuSE Labs.
 * Distributed under the GNU public license, v2.
 *
 * This is a GART driver for the AMD Opteron/Athlon64 on-CPU northbridge.
 * It also includes support for the AMD 8151 AGP bridge,
 * although it doesn't actually do much, as all the real
 * work is done in the northbridge(s).
 */

#include <linux/module.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/agp_backend.h>
15
#include <linux/mmzone.h>
Tim Schmielau's avatar
Tim Schmielau committed
16
#include <asm/page.h>		/* PAGE_SIZE */
17
#include <asm/e820.h>
18
#include <asm/k8.h>
19
#include <asm/gart.h>
Linus Torvalds's avatar
Linus Torvalds committed
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include "agp.h"

/* NVIDIA K8 registers */
#define NVIDIA_X86_64_0_APBASE		0x10
#define NVIDIA_X86_64_1_APBASE1		0x50
#define NVIDIA_X86_64_1_APLIMIT1	0x54
#define NVIDIA_X86_64_1_APSIZE		0xa8
#define NVIDIA_X86_64_1_APBASE2		0xd8
#define NVIDIA_X86_64_1_APLIMIT2	0xdc

/* ULi K8 registers */
#define ULI_X86_64_BASE_ADDR		0x10
#define ULI_X86_64_HTT_FEA_REG		0x50
#define ULI_X86_64_ENU_SCR_REG		0x54

static struct resource *aperture_resource;
36
static int __initdata agp_try_unsupported = 1;
37
static int agp_bridges_found;
Linus Torvalds's avatar
Linus Torvalds committed
38
39
40

static void amd64_tlbflush(struct agp_memory *temp)
{
41
	k8_flush_garts();
Linus Torvalds's avatar
Linus Torvalds committed
42
43
44
45
46
47
}

static int amd64_insert_memory(struct agp_memory *mem, off_t pg_start, int type)
{
	int i, j, num_entries;
	long long tmp;
48
49
	int mask_type;
	struct agp_bridge_data *bridge = mem->bridge;
Linus Torvalds's avatar
Linus Torvalds committed
50
51
52
53
	u32 pte;

	num_entries = agp_num_entries();

54
	if (type != mem->type)
Linus Torvalds's avatar
Linus Torvalds committed
55
		return -EINVAL;
56
57
58
59
	mask_type = bridge->driver->agp_type_to_mask_type(bridge, type);
	if (mask_type != 0)
		return -EINVAL;

Linus Torvalds's avatar
Linus Torvalds committed
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74

	/* Make sure we can fit the range in the gatt table. */
	/* FIXME: could wrap */
	if (((unsigned long)pg_start + mem->page_count) > num_entries)
		return -EINVAL;

	j = pg_start;

	/* gatt table should be empty. */
	while (j < (pg_start + mem->page_count)) {
		if (!PGE_EMPTY(agp_bridge, readl(agp_bridge->gatt_table+j)))
			return -EBUSY;
		j++;
	}

Joe Perches's avatar
Joe Perches committed
75
	if (!mem->is_flushed) {
Linus Torvalds's avatar
Linus Torvalds committed
76
		global_cache_flush();
Joe Perches's avatar
Joe Perches committed
77
		mem->is_flushed = true;
Linus Torvalds's avatar
Linus Torvalds committed
78
79
80
81
	}

	for (i = 0, j = pg_start; i < mem->page_count; i++, j++) {
		tmp = agp_bridge->driver->mask_memory(agp_bridge,
82
						      page_to_phys(mem->pages[i]),
83
						      mask_type);
Linus Torvalds's avatar
Linus Torvalds committed
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126

		BUG_ON(tmp & 0xffffff0000000ffcULL);
		pte = (tmp & 0x000000ff00000000ULL) >> 28;
		pte |=(tmp & 0x00000000fffff000ULL);
		pte |= GPTE_VALID | GPTE_COHERENT;

		writel(pte, agp_bridge->gatt_table+j);
		readl(agp_bridge->gatt_table+j);	/* PCI Posting. */
	}
	amd64_tlbflush(mem);
	return 0;
}

/*
 * This hack alters the order element according
 * to the size of a long. It sucks. I totally disown this, even
 * though it does appear to work for the most part.
 */
static struct aper_size_info_32 amd64_aperture_sizes[7] =
{
	{32,   8192,   3+(sizeof(long)/8), 0 },
	{64,   16384,  4+(sizeof(long)/8), 1<<1 },
	{128,  32768,  5+(sizeof(long)/8), 1<<2 },
	{256,  65536,  6+(sizeof(long)/8), 1<<1 | 1<<2 },
	{512,  131072, 7+(sizeof(long)/8), 1<<3 },
	{1024, 262144, 8+(sizeof(long)/8), 1<<1 | 1<<3},
	{2048, 524288, 9+(sizeof(long)/8), 1<<2 | 1<<3}
};


/*
 * Get the current Aperture size from the x86-64.
 * Note, that there may be multiple x86-64's, but we just return
 * the value from the first one we find. The set_size functions
 * keep the rest coherent anyway. Or at least should do.
 */
static int amd64_fetch_size(void)
{
	struct pci_dev *dev;
	int i;
	u32 temp;
	struct aper_size_info_32 *values;

127
	dev = k8_northbridges[0];
Linus Torvalds's avatar
Linus Torvalds committed
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
	if (dev==NULL)
		return 0;

	pci_read_config_dword(dev, AMD64_GARTAPERTURECTL, &temp);
	temp = (temp & 0xe);
	values = A_SIZE_32(amd64_aperture_sizes);

	for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++) {
		if (temp == values[i].size_value) {
			agp_bridge->previous_size =
			    agp_bridge->current_size = (void *) (values + i);

			agp_bridge->aperture_size_idx = i;
			return values[i].size;
		}
	}
	return 0;
}

/*
 * In a multiprocessor x86-64 system, this function gets
 * called once for each CPU.
 */
151
static u64 amd64_configure(struct pci_dev *hammer, u64 gatt_table)
Linus Torvalds's avatar
Linus Torvalds committed
152
153
154
{
	u64 aperturebase;
	u32 tmp;
155
	u64 aper_base;
Linus Torvalds's avatar
Linus Torvalds committed
156
157

	/* Address to map to */
158
	pci_read_config_dword(hammer, AMD64_GARTAPERTUREBASE, &tmp);
Linus Torvalds's avatar
Linus Torvalds committed
159
160
161
	aperturebase = tmp << 25;
	aper_base = (aperturebase & PCI_BASE_ADDRESS_MEM_MASK);

162
	enable_gart_translation(hammer, gatt_table);
Linus Torvalds's avatar
Linus Torvalds committed
163
164
165
166
167

	return aper_base;
}


Dave Jones's avatar
Dave Jones committed
168
static const struct aper_size_info_32 amd_8151_sizes[7] =
Linus Torvalds's avatar
Linus Torvalds committed
169
170
171
172
173
174
175
{
	{2048, 524288, 9, 0x00000000 },	/* 0 0 0 0 0 0 */
	{1024, 262144, 8, 0x00000400 },	/* 1 0 0 0 0 0 */
	{512,  131072, 7, 0x00000600 },	/* 1 1 0 0 0 0 */
	{256,  65536,  6, 0x00000700 },	/* 1 1 1 0 0 0 */
	{128,  32768,  5, 0x00000720 },	/* 1 1 1 1 0 0 */
	{64,   16384,  4, 0x00000730 },	/* 1 1 1 1 1 0 */
176
	{32,   8192,   3, 0x00000738 }	/* 1 1 1 1 1 1 */
Linus Torvalds's avatar
Linus Torvalds committed
177
178
179
180
};

static int amd_8151_configure(void)
{
181
	unsigned long gatt_bus = virt_to_phys(agp_bridge->gatt_table_real);
182
	int i;
Linus Torvalds's avatar
Linus Torvalds committed
183
184

	/* Configure AGP regs in each x86-64 host bridge. */
185
        for (i = 0; i < num_k8_northbridges; i++) {
Linus Torvalds's avatar
Linus Torvalds committed
186
		agp_bridge->gart_bus_addr =
187
				amd64_configure(k8_northbridges[i], gatt_bus);
Linus Torvalds's avatar
Linus Torvalds committed
188
	}
189
	k8_flush_garts();
Linus Torvalds's avatar
Linus Torvalds committed
190
191
192
193
194
195
196
	return 0;
}


static void amd64_cleanup(void)
{
	u32 tmp;
197
198
199
	int i;
        for (i = 0; i < num_k8_northbridges; i++) {
		struct pci_dev *dev = k8_northbridges[i];
Linus Torvalds's avatar
Linus Torvalds committed
200
		/* disable gart translation */
201
		pci_read_config_dword(dev, AMD64_GARTAPERTURECTL, &tmp);
Linus Torvalds's avatar
Linus Torvalds committed
202
		tmp &= ~AMD64_GARTEN;
203
		pci_write_config_dword(dev, AMD64_GARTAPERTURECTL, tmp);
Linus Torvalds's avatar
Linus Torvalds committed
204
205
206
207
	}
}


Dave Jones's avatar
Dave Jones committed
208
static const struct agp_bridge_driver amd_8151_driver = {
Linus Torvalds's avatar
Linus Torvalds committed
209
210
211
212
	.owner			= THIS_MODULE,
	.aperture_sizes		= amd_8151_sizes,
	.size_type		= U32_APER_SIZE,
	.num_aperture_sizes	= 7,
213
	.needs_scratch_page	= true,
Linus Torvalds's avatar
Linus Torvalds committed
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
	.configure		= amd_8151_configure,
	.fetch_size		= amd64_fetch_size,
	.cleanup		= amd64_cleanup,
	.tlb_flush		= amd64_tlbflush,
	.mask_memory		= agp_generic_mask_memory,
	.masks			= NULL,
	.agp_enable		= agp_generic_enable,
	.cache_flush		= global_cache_flush,
	.create_gatt_table	= agp_generic_create_gatt_table,
	.free_gatt_table	= agp_generic_free_gatt_table,
	.insert_memory		= amd64_insert_memory,
	.remove_memory		= agp_generic_remove_memory,
	.alloc_by_type		= agp_generic_alloc_by_type,
	.free_by_type		= agp_generic_free_by_type,
	.agp_alloc_page		= agp_generic_alloc_page,
229
	.agp_alloc_pages	= agp_generic_alloc_pages,
Linus Torvalds's avatar
Linus Torvalds committed
230
	.agp_destroy_page	= agp_generic_destroy_page,
231
	.agp_destroy_pages	= agp_generic_destroy_pages,
232
	.agp_type_to_mask_type  = agp_generic_type_to_mask_type,
Linus Torvalds's avatar
Linus Torvalds committed
233
234
235
};

/* Some basic sanity checks for the aperture. */
236
static int __devinit agp_aperture_valid(u64 aper, u32 size)
Linus Torvalds's avatar
Linus Torvalds committed
237
{
238
	if (!aperture_valid(aper, size, 32*1024*1024))
Linus Torvalds's avatar
Linus Torvalds committed
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
		return 0;

	/* Request the Aperture. This catches cases when someone else
	   already put a mapping in there - happens with some very broken BIOS

	   Maybe better to use pci_assign_resource/pci_enable_device instead
	   trusting the bridges? */
	if (!aperture_resource &&
	    !(aperture_resource = request_mem_region(aper, size, "aperture"))) {
		printk(KERN_ERR PFX "Aperture conflicts with PCI mapping.\n");
		return 0;
	}
	return 1;
}

/*
 * W*s centric BIOS sometimes only set up the aperture in the AGP
 * bridge, not the northbridge. On AMD64 this is handled early
257
 * in aperture.c, but when IOMMU is not enabled or we run
Linus Torvalds's avatar
Linus Torvalds committed
258
259
260
261
262
263
264
265
266
267
268
269
270
271
 * on a 32bit kernel this needs to be redone.
 * Unfortunately it is impossible to fix the aperture here because it's too late
 * to allocate that much memory. But at least error out cleanly instead of
 * crashing.
 */
static __devinit int fix_northbridge(struct pci_dev *nb, struct pci_dev *agp,
								 u16 cap)
{
	u32 aper_low, aper_hi;
	u64 aper, nb_aper;
	int order = 0;
	u32 nb_order, nb_base;
	u16 apsize;

272
	pci_read_config_dword(nb, AMD64_GARTAPERTURECTL, &nb_order);
Linus Torvalds's avatar
Linus Torvalds committed
273
	nb_order = (nb_order >> 1) & 7;
274
	pci_read_config_dword(nb, AMD64_GARTAPERTUREBASE, &nb_base);
Linus Torvalds's avatar
Linus Torvalds committed
275
276
277
278
279
	nb_aper = nb_base << 25;

	/* Northbridge seems to contain crap. Try the AGP bridge. */

	pci_read_config_word(agp, cap+0x14, &apsize);
280
281
282
	if (apsize == 0xffff) {
		if (agp_aperture_valid(nb_aper, (32*1024*1024)<<nb_order))
			return 0;
Linus Torvalds's avatar
Linus Torvalds committed
283
		return -1;
284
	}
Linus Torvalds's avatar
Linus Torvalds committed
285
286
287
288
289
290
291
292
293
294

	apsize &= 0xfff;
	/* Some BIOS use weird encodings not in the AGPv3 table. */
	if (apsize & 0xff)
		apsize |= 0xf00;
	order = 7 - hweight16(apsize);

	pci_read_config_dword(agp, 0x10, &aper_low);
	pci_read_config_dword(agp, 0x14, &aper_hi);
	aper = (aper_low & ~((1<<22)-1)) | ((u64)aper_hi << 32);
295
296
297
298
299

	/*
	 * On some sick chips APSIZE is 0. This means it wants 4G
	 * so let double check that order, and lets trust the AMD NB settings
	 */
Yinghai Lu's avatar
Yinghai Lu committed
300
	if (order >=0 && aper + (32ULL<<(20 + order)) > 0x100000000ULL) {
301
302
		dev_info(&agp->dev, "aperture size %u MB is not right, using settings from NB\n",
			 32 << order);
303
304
305
		order = nb_order;
	}

306
307
308
309
310
	if (nb_order >= order) {
		if (agp_aperture_valid(nb_aper, (32*1024*1024)<<nb_order))
			return 0;
	}

311
312
	dev_info(&agp->dev, "aperture from AGP @ %Lx size %u MB\n",
		 aper, 32 << order);
313
	if (order < 0 || !agp_aperture_valid(aper, (32*1024*1024)<<order))
Linus Torvalds's avatar
Linus Torvalds committed
314
315
		return -1;

316
317
	pci_write_config_dword(nb, AMD64_GARTAPERTURECTL, order << 1);
	pci_write_config_dword(nb, AMD64_GARTAPERTUREBASE, aper >> 25);
Linus Torvalds's avatar
Linus Torvalds committed
318
319
320
321
322
323

	return 0;
}

static __devinit int cache_nbs (struct pci_dev *pdev, u32 cap_ptr)
{
324
325
326
327
328
329
330
331
332
	int i;

	if (cache_k8_northbridges() < 0)
		return -ENODEV;

	i = 0;
	for (i = 0; i < num_k8_northbridges; i++) {
		struct pci_dev *dev = k8_northbridges[i];
		if (fix_northbridge(dev, pdev, cap_ptr) < 0) {
333
			dev_err(&dev->dev, "no usable aperture found\n");
Linus Torvalds's avatar
Linus Torvalds committed
334
335
#ifdef __x86_64__
			/* should port this to i386 */
336
			dev_err(&dev->dev, "consider rebooting with iommu=memaper=2 to get a good aperture\n");
Linus Torvalds's avatar
Linus Torvalds committed
337
338
339
340
#endif
			return -1;
		}
	}
341
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
342
343
344
345
346
347
348
}

/* Handle AMD 8151 quirks */
static void __devinit amd8151_init(struct pci_dev *pdev, struct agp_bridge_data *bridge)
{
	char *revstring;

349
	switch (pdev->revision) {
Linus Torvalds's avatar
Linus Torvalds committed
350
351
352
353
354
355
356
357
358
	case 0x01: revstring="A0"; break;
	case 0x02: revstring="A1"; break;
	case 0x11: revstring="B0"; break;
	case 0x12: revstring="B1"; break;
	case 0x13: revstring="B2"; break;
	case 0x14: revstring="B3"; break;
	default:   revstring="??"; break;
	}

359
	dev_info(&pdev->dev, "AMD 8151 AGP Bridge rev %s\n", revstring);
Linus Torvalds's avatar
Linus Torvalds committed
360
361
362
363
364

	/*
	 * Work around errata.
	 * Chips before B2 stepping incorrectly reporting v3.5
	 */
365
	if (pdev->revision < 0x13) {
366
		dev_info(&pdev->dev, "correcting AGP revision (reports 3.5, is really 3.0)\n");
Linus Torvalds's avatar
Linus Torvalds committed
367
368
369
370
371
372
		bridge->major_version = 3;
		bridge->minor_version = 0;
	}
}


373
static const struct aper_size_info_32 uli_sizes[7] =
Linus Torvalds's avatar
Linus Torvalds committed
374
375
376
377
378
379
380
381
382
383
384
385
386
{
	{256, 65536, 6, 10},
	{128, 32768, 5, 9},
	{64, 16384, 4, 8},
	{32, 8192, 3, 7},
	{16, 4096, 2, 6},
	{8, 2048, 1, 4},
	{4, 1024, 0, 3}
};
static int __devinit uli_agp_init(struct pci_dev *pdev)
{
	u32 httfea,baseaddr,enuscr;
	struct pci_dev *dev1;
387
	int i, ret;
Linus Torvalds's avatar
Linus Torvalds committed
388
	unsigned size = amd64_fetch_size();
389
390

	dev_info(&pdev->dev, "setting up ULi AGP\n");
391
	dev1 = pci_get_slot (pdev->bus,PCI_DEVFN(0,0));
Linus Torvalds's avatar
Linus Torvalds committed
392
	if (dev1 == NULL) {
393
		dev_info(&pdev->dev, "can't find ULi secondary device\n");
Linus Torvalds's avatar
Linus Torvalds committed
394
395
396
397
398
399
400
401
		return -ENODEV;
	}

	for (i = 0; i < ARRAY_SIZE(uli_sizes); i++)
		if (uli_sizes[i].size == size)
			break;

	if (i == ARRAY_SIZE(uli_sizes)) {
402
		dev_info(&pdev->dev, "no ULi size found for %d\n", size);
403
404
		ret = -ENODEV;
		goto put;
Linus Torvalds's avatar
Linus Torvalds committed
405
406
407
	}

	/* shadow x86-64 registers into ULi registers */
408
	pci_read_config_dword (k8_northbridges[0], AMD64_GARTAPERTUREBASE, &httfea);
Linus Torvalds's avatar
Linus Torvalds committed
409
410

	/* if x86-64 aperture base is beyond 4G, exit here */
411
412
413
414
	if ((httfea & 0x7fff) >> (32 - 25)) {
		ret = -ENODEV;
		goto put;
	}
Linus Torvalds's avatar
Linus Torvalds committed
415
416
417
418
419
420
421
422
423
424
425

	httfea = (httfea& 0x7fff) << 25;

	pci_read_config_dword(pdev, ULI_X86_64_BASE_ADDR, &baseaddr);
	baseaddr&= ~PCI_BASE_ADDRESS_MEM_MASK;
	baseaddr|= httfea;
	pci_write_config_dword(pdev, ULI_X86_64_BASE_ADDR, baseaddr);

	enuscr= httfea+ (size * 1024 * 1024) - 1;
	pci_write_config_dword(dev1, ULI_X86_64_HTT_FEA_REG, httfea);
	pci_write_config_dword(dev1, ULI_X86_64_ENU_SCR_REG, enuscr);
426
427
	ret = 0;
put:
428
	pci_dev_put(dev1);
429
	return ret;
Linus Torvalds's avatar
Linus Torvalds committed
430
431
432
}


433
static const struct aper_size_info_32 nforce3_sizes[5] =
Linus Torvalds's avatar
Linus Torvalds committed
434
435
436
437
438
439
440
441
442
443
{
	{512,  131072, 7, 0x00000000 },
	{256,  65536,  6, 0x00000008 },
	{128,  32768,  5, 0x0000000C },
	{64,   16384,  4, 0x0000000E },
	{32,   8192,   3, 0x0000000F }
};

/* Handle shadow device of the Nvidia NForce3 */
/* CHECK-ME original 2.4 version set up some IORRs. Check if that is needed. */
444
static int nforce3_agp_init(struct pci_dev *pdev)
Linus Torvalds's avatar
Linus Torvalds committed
445
446
447
{
	u32 tmp, apbase, apbar, aplimit;
	struct pci_dev *dev1;
448
	int i, ret;
Linus Torvalds's avatar
Linus Torvalds committed
449
450
	unsigned size = amd64_fetch_size();

451
	dev_info(&pdev->dev, "setting up Nforce3 AGP\n");
Linus Torvalds's avatar
Linus Torvalds committed
452

453
	dev1 = pci_get_slot(pdev->bus, PCI_DEVFN(11, 0));
Linus Torvalds's avatar
Linus Torvalds committed
454
	if (dev1 == NULL) {
455
		dev_info(&pdev->dev, "can't find Nforce3 secondary device\n");
Linus Torvalds's avatar
Linus Torvalds committed
456
457
458
459
460
461
462
463
		return -ENODEV;
	}

	for (i = 0; i < ARRAY_SIZE(nforce3_sizes); i++)
		if (nforce3_sizes[i].size == size)
			break;

	if (i == ARRAY_SIZE(nforce3_sizes)) {
464
		dev_info(&pdev->dev, "no NForce3 size found for %d\n", size);
465
466
		ret = -ENODEV;
		goto put;
Linus Torvalds's avatar
Linus Torvalds committed
467
468
469
470
471
472
473
474
	}

	pci_read_config_dword(dev1, NVIDIA_X86_64_1_APSIZE, &tmp);
	tmp &= ~(0xf);
	tmp |= nforce3_sizes[i].size_value;
	pci_write_config_dword(dev1, NVIDIA_X86_64_1_APSIZE, tmp);

	/* shadow x86-64 registers into NVIDIA registers */
475
	pci_read_config_dword (k8_northbridges[0], AMD64_GARTAPERTUREBASE, &apbase);
Linus Torvalds's avatar
Linus Torvalds committed
476
477

	/* if x86-64 aperture base is beyond 4G, exit here */
478
	if ( (apbase & 0x7fff) >> (32 - 25) ) {
479
		dev_info(&pdev->dev, "aperture base > 4G\n");
480
481
		ret = -ENODEV;
		goto put;
482
	}
Linus Torvalds's avatar
Linus Torvalds committed
483
484
485
486
487
488
489
490
491
492
493
494
495
496

	apbase = (apbase & 0x7fff) << 25;

	pci_read_config_dword(pdev, NVIDIA_X86_64_0_APBASE, &apbar);
	apbar &= ~PCI_BASE_ADDRESS_MEM_MASK;
	apbar |= apbase;
	pci_write_config_dword(pdev, NVIDIA_X86_64_0_APBASE, apbar);

	aplimit = apbase + (size * 1024 * 1024) - 1;
	pci_write_config_dword(dev1, NVIDIA_X86_64_1_APBASE1, apbase);
	pci_write_config_dword(dev1, NVIDIA_X86_64_1_APLIMIT1, aplimit);
	pci_write_config_dword(dev1, NVIDIA_X86_64_1_APBASE2, apbase);
	pci_write_config_dword(dev1, NVIDIA_X86_64_1_APLIMIT2, aplimit);

497
498
	ret = 0;
put:
499
500
	pci_dev_put(dev1);

501
	return ret;
Linus Torvalds's avatar
Linus Torvalds committed
502
503
504
505
506
507
508
}

static int __devinit agp_amd64_probe(struct pci_dev *pdev,
				     const struct pci_device_id *ent)
{
	struct agp_bridge_data *bridge;
	u8 cap_ptr;
509
	int err;
Linus Torvalds's avatar
Linus Torvalds committed
510

511
512
513
514
	/* The Highlander principle */
	if (agp_bridges_found)
		return -ENODEV;

Linus Torvalds's avatar
Linus Torvalds committed
515
516
517
518
519
520
521
522
523
524
525
526
527
528
	cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP);
	if (!cap_ptr)
		return -ENODEV;

	/* Could check for AGPv3 here */

	bridge = agp_alloc_bridge();
	if (!bridge)
		return -ENOMEM;

	if (pdev->vendor == PCI_VENDOR_ID_AMD &&
	    pdev->device == PCI_DEVICE_ID_AMD_8151_0) {
		amd8151_init(pdev, bridge);
	} else {
529
530
		dev_info(&pdev->dev, "AGP bridge [%04x/%04x]\n",
			 pdev->vendor, pdev->device);
Linus Torvalds's avatar
Linus Torvalds committed
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
	}

	bridge->driver = &amd_8151_driver;
	bridge->dev = pdev;
	bridge->capndx = cap_ptr;

	/* Fill in the mode register */
	pci_read_config_dword(pdev, bridge->capndx+PCI_AGP_STATUS, &bridge->mode);

	if (cache_nbs(pdev, cap_ptr) == -1) {
		agp_put_bridge(bridge);
		return -ENODEV;
	}

	if (pdev->vendor == PCI_VENDOR_ID_NVIDIA) {
		int ret = nforce3_agp_init(pdev);
		if (ret) {
			agp_put_bridge(bridge);
			return ret;
		}
	}

	if (pdev->vendor == PCI_VENDOR_ID_AL) {
		int ret = uli_agp_init(pdev);
		if (ret) {
			agp_put_bridge(bridge);
			return ret;
		}
	}

	pci_set_drvdata(pdev, bridge);
562
563
564
565
566
567
	err = agp_add_bridge(bridge);
	if (err < 0)
		return err;

	agp_bridges_found++;
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
568
569
570
571
572
573
}

static void __devexit agp_amd64_remove(struct pci_dev *pdev)
{
	struct agp_bridge_data *bridge = pci_get_drvdata(pdev);

574
	release_mem_region(virt_to_phys(bridge->gatt_table_real),
Linus Torvalds's avatar
Linus Torvalds committed
575
576
577
			   amd64_aperture_sizes[bridge->aperture_size_idx].size);
	agp_remove_bridge(bridge);
	agp_put_bridge(bridge);
578
579

	agp_bridges_found--;
Linus Torvalds's avatar
Linus Torvalds committed
580
581
}

582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
#ifdef CONFIG_PM

static int agp_amd64_suspend(struct pci_dev *pdev, pm_message_t state)
{
	pci_save_state(pdev);
	pci_set_power_state(pdev, pci_choose_state(pdev, state));

	return 0;
}

static int agp_amd64_resume(struct pci_dev *pdev)
{
	pci_set_power_state(pdev, PCI_D0);
	pci_restore_state(pdev);

597
598
599
	if (pdev->vendor == PCI_VENDOR_ID_NVIDIA)
		nforce3_agp_init(pdev);

600
601
602
603
604
	return amd_8151_configure();
}

#endif /* CONFIG_PM */

Linus Torvalds's avatar
Linus Torvalds committed
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
static struct pci_device_id agp_amd64_pci_table[] = {
	{
	.class		= (PCI_CLASS_BRIDGE_HOST << 8),
	.class_mask	= ~0,
	.vendor		= PCI_VENDOR_ID_AMD,
	.device		= PCI_DEVICE_ID_AMD_8151_0,
	.subvendor	= PCI_ANY_ID,
	.subdevice	= PCI_ANY_ID,
	},
	/* ULi M1689 */
	{
	.class		= (PCI_CLASS_BRIDGE_HOST << 8),
	.class_mask	= ~0,
	.vendor		= PCI_VENDOR_ID_AL,
	.device		= PCI_DEVICE_ID_AL_M1689,
	.subvendor	= PCI_ANY_ID,
	.subdevice	= PCI_ANY_ID,
	},
	/* VIA K8T800Pro */
	{
	.class		= (PCI_CLASS_BRIDGE_HOST << 8),
	.class_mask	= ~0,
	.vendor		= PCI_VENDOR_ID_VIA,
	.device		= PCI_DEVICE_ID_VIA_K8T800PRO_0,
	.subvendor	= PCI_ANY_ID,
	.subdevice	= PCI_ANY_ID,
	},
	/* VIA K8T800 */
	{
	.class		= (PCI_CLASS_BRIDGE_HOST << 8),
	.class_mask	= ~0,
	.vendor		= PCI_VENDOR_ID_VIA,
	.device		= PCI_DEVICE_ID_VIA_8385_0,
	.subvendor	= PCI_ANY_ID,
	.subdevice	= PCI_ANY_ID,
	},
	/* VIA K8M800 / K8N800 */
	{
	.class		= (PCI_CLASS_BRIDGE_HOST << 8),
	.class_mask	= ~0,
	.vendor		= PCI_VENDOR_ID_VIA,
	.device		= PCI_DEVICE_ID_VIA_8380_0,
	.subvendor	= PCI_ANY_ID,
	.subdevice	= PCI_ANY_ID,
	},
650
651
652
653
654
	/* VIA K8M890 / K8N890 */
	{
	.class          = (PCI_CLASS_BRIDGE_HOST << 8),
	.class_mask     = ~0,
	.vendor         = PCI_VENDOR_ID_VIA,
Dave Jones's avatar
Dave Jones committed
655
	.device         = PCI_DEVICE_ID_VIA_VT3336,
656
657
658
	.subvendor      = PCI_ANY_ID,
	.subdevice      = PCI_ANY_ID,
	},
Linus Torvalds's avatar
Linus Torvalds committed
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
	/* VIA K8T890 */
	{
	.class		= (PCI_CLASS_BRIDGE_HOST << 8),
	.class_mask	= ~0,
	.vendor		= PCI_VENDOR_ID_VIA,
	.device		= PCI_DEVICE_ID_VIA_3238_0,
	.subvendor	= PCI_ANY_ID,
	.subdevice	= PCI_ANY_ID,
	},
	/* VIA K8T800/K8M800/K8N800 */
	{
	.class		= (PCI_CLASS_BRIDGE_HOST << 8),
	.class_mask	= ~0,
	.vendor		= PCI_VENDOR_ID_VIA,
	.device		= PCI_DEVICE_ID_VIA_838X_1,
	.subvendor	= PCI_ANY_ID,
	.subdevice	= PCI_ANY_ID,
	},
	/* NForce3 */
	{
	.class		= (PCI_CLASS_BRIDGE_HOST << 8),
	.class_mask	= ~0,
	.vendor		= PCI_VENDOR_ID_NVIDIA,
	.device		= PCI_DEVICE_ID_NVIDIA_NFORCE3,
	.subvendor	= PCI_ANY_ID,
	.subdevice	= PCI_ANY_ID,
	},
	{
	.class		= (PCI_CLASS_BRIDGE_HOST << 8),
	.class_mask	= ~0,
	.vendor		= PCI_VENDOR_ID_NVIDIA,
	.device		= PCI_DEVICE_ID_NVIDIA_NFORCE3S,
	.subvendor	= PCI_ANY_ID,
	.subdevice	= PCI_ANY_ID,
	},
	/* SIS 755 */
	{
	.class		= (PCI_CLASS_BRIDGE_HOST << 8),
	.class_mask	= ~0,
	.vendor		= PCI_VENDOR_ID_SI,
	.device		= PCI_DEVICE_ID_SI_755,
	.subvendor	= PCI_ANY_ID,
	.subdevice	= PCI_ANY_ID,
	},
Dave Jones's avatar
Dave Jones committed
703
704
705
706
707
708
709
710
711
	/* SIS 760 */
	{
	.class		= (PCI_CLASS_BRIDGE_HOST << 8),
	.class_mask	= ~0,
	.vendor		= PCI_VENDOR_ID_SI,
	.device		= PCI_DEVICE_ID_SI_760,
	.subvendor	= PCI_ANY_ID,
	.subdevice	= PCI_ANY_ID,
	},
712
713
714
715
716
	/* ALI/ULI M1695 */
	{
	.class		= (PCI_CLASS_BRIDGE_HOST << 8),
	.class_mask	= ~0,
	.vendor		= PCI_VENDOR_ID_AL,
717
	.device		= 0x1695,
718
719
720
721
	.subvendor	= PCI_ANY_ID,
	.subdevice	= PCI_ANY_ID,
	},

Linus Torvalds's avatar
Linus Torvalds committed
722
723
724
725
726
	{ }
};

MODULE_DEVICE_TABLE(pci, agp_amd64_pci_table);

727
728
729
730
731
static DEFINE_PCI_DEVICE_TABLE(agp_amd64_pci_promisc_table) = {
	{ PCI_DEVICE_CLASS(0, 0) },
	{ }
};

Linus Torvalds's avatar
Linus Torvalds committed
732
733
734
735
736
static struct pci_driver agp_amd64_pci_driver = {
	.name		= "agpgart-amd64",
	.id_table	= agp_amd64_pci_table,
	.probe		= agp_amd64_probe,
	.remove		= agp_amd64_remove,
737
738
739
740
#ifdef CONFIG_PM
	.suspend	= agp_amd64_suspend,
	.resume		= agp_amd64_resume,
#endif
Linus Torvalds's avatar
Linus Torvalds committed
741
742
743
744
745
746
747
748
749
750
};


/* Not static due to IOMMU code calling it early. */
int __init agp_amd64_init(void)
{
	int err = 0;

	if (agp_off)
		return -EINVAL;
751

752
753
754
755
756
	err = pci_register_driver(&agp_amd64_pci_driver);
	if (err < 0)
		return err;

	if (agp_bridges_found == 0) {
Linus Torvalds's avatar
Linus Torvalds committed
757
758
759
760
761
762
763
764
765
766
767
		if (!agp_try_unsupported && !agp_try_unsupported_boot) {
			printk(KERN_INFO PFX "No supported AGP bridge found.\n");
#ifdef MODULE
			printk(KERN_INFO PFX "You can try agp_try_unsupported=1\n");
#else
			printk(KERN_INFO PFX "You can boot with agp=try_unsupported\n");
#endif
			return -ENODEV;
		}

		/* First check that we have at least one AMD64 NB */
768
		if (!pci_dev_present(k8_nb_ids))
Linus Torvalds's avatar
Linus Torvalds committed
769
770
771
			return -ENODEV;

		/* Look for any AGP bridge */
772
773
774
775
		agp_amd64_pci_driver.id_table = agp_amd64_pci_promisc_table;
		err = driver_attach(&agp_amd64_pci_driver.driver);
		if (err == 0 && agp_bridges_found == 0)
			err = -ENODEV;
Linus Torvalds's avatar
Linus Torvalds committed
776
777
778
779
	}
	return err;
}

780
781
static int __init agp_amd64_mod_init(void)
{
782
#ifndef MODULE
783
784
	if (gart_iommu_aperture)
		return agp_bridges_found ? 0 : -ENODEV;
785
#endif
786
787
788
	return agp_amd64_init();
}

Linus Torvalds's avatar
Linus Torvalds committed
789
790
static void __exit agp_amd64_cleanup(void)
{
791
#ifndef MODULE
792
793
	if (gart_iommu_aperture)
		return;
794
#endif
Linus Torvalds's avatar
Linus Torvalds committed
795
796
797
798
799
	if (aperture_resource)
		release_resource(aperture_resource);
	pci_unregister_driver(&agp_amd64_pci_driver);
}

800
module_init(agp_amd64_mod_init);
Linus Torvalds's avatar
Linus Torvalds committed
801
802
module_exit(agp_amd64_cleanup);

Dave Jones's avatar
Dave Jones committed
803
MODULE_AUTHOR("Dave Jones <davej@redhat.com>, Andi Kleen");
Linus Torvalds's avatar
Linus Torvalds committed
804
805
module_param(agp_try_unsupported, bool, 0);
MODULE_LICENSE("GPL");