amd64-agp.c 19.8 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 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
	.owner			= THIS_MODULE,
	.aperture_sizes		= amd_8151_sizes,
	.size_type		= U32_APER_SIZE,
	.num_aperture_sizes	= 7,
	.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,
228
	.agp_alloc_pages	= agp_generic_alloc_pages,
Linus Torvalds's avatar
Linus Torvalds committed
229
	.agp_destroy_page	= agp_generic_destroy_page,
230
	.agp_destroy_pages	= agp_generic_destroy_pages,
231
	.agp_type_to_mask_type  = agp_generic_type_to_mask_type,
Linus Torvalds's avatar
Linus Torvalds committed
232 233 234
};

/* Some basic sanity checks for the aperture. */
235
static int __devinit agp_aperture_valid(u64 aper, u32 size)
Linus Torvalds's avatar
Linus Torvalds committed
236
{
237
	if (!aperture_valid(aper, size, 32*1024*1024))
Linus Torvalds's avatar
Linus Torvalds committed
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
		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
256
 * in aperture.c, but when IOMMU is not enabled or we run
Linus Torvalds's avatar
Linus Torvalds committed
257 258 259 260 261 262 263 264 265 266 267 268 269 270
 * 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;

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

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

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

	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);
294 295 296 297 298

	/*
	 * 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
299
	if (order >=0 && aper + (32ULL<<(20 + order)) > 0x100000000ULL) {
300 301
		dev_info(&agp->dev, "aperture size %u MB is not right, using settings from NB\n",
			 32 << order);
302 303 304
		order = nb_order;
	}

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

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

315 316
	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
317 318 319 320 321 322

	return 0;
}

static __devinit int cache_nbs (struct pci_dev *pdev, u32 cap_ptr)
{
323 324 325 326 327 328 329 330 331
	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) {
332
			dev_err(&dev->dev, "no usable aperture found\n");
Linus Torvalds's avatar
Linus Torvalds committed
333 334
#ifdef __x86_64__
			/* should port this to i386 */
335
			dev_err(&dev->dev, "consider rebooting with iommu=memaper=2 to get a good aperture\n");
Linus Torvalds's avatar
Linus Torvalds committed
336 337 338 339
#endif
			return -1;
		}
	}
340
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
341 342 343 344 345 346 347
}

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

348
	switch (pdev->revision) {
Linus Torvalds's avatar
Linus Torvalds committed
349 350 351 352 353 354 355 356 357
	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;
	}

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

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


372
static const struct aper_size_info_32 uli_sizes[7] =
Linus Torvalds's avatar
Linus Torvalds committed
373 374 375 376 377 378 379 380 381 382 383 384 385 386 387
{
	{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;
	int i;
	unsigned size = amd64_fetch_size();
388 389

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

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

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

	/* shadow x86-64 registers into ULi registers */
406
	pci_read_config_dword (k8_northbridges[0], AMD64_GARTAPERTUREBASE, &httfea);
Linus Torvalds's avatar
Linus Torvalds committed
407 408 409 410 411 412 413 414 415 416 417 418 419 420 421

	/* if x86-64 aperture base is beyond 4G, exit here */
	if ((httfea & 0x7fff) >> (32 - 25))
		return -ENODEV;

	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);
422 423

	pci_dev_put(dev1);
Linus Torvalds's avatar
Linus Torvalds committed
424 425 426 427
	return 0;
}


428
static const struct aper_size_info_32 nforce3_sizes[5] =
Linus Torvalds's avatar
Linus Torvalds committed
429 430 431 432 433 434 435 436 437 438
{
	{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. */
439
static int nforce3_agp_init(struct pci_dev *pdev)
Linus Torvalds's avatar
Linus Torvalds committed
440 441 442 443 444 445
{
	u32 tmp, apbase, apbar, aplimit;
	struct pci_dev *dev1;
	int i;
	unsigned size = amd64_fetch_size();

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

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

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

	if (i == ARRAY_SIZE(nforce3_sizes)) {
459
		dev_info(&pdev->dev, "no NForce3 size found for %d\n", size);
Linus Torvalds's avatar
Linus Torvalds committed
460 461 462 463 464 465 466 467 468
		return -ENODEV;
	}

	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 */
469
	pci_read_config_dword (k8_northbridges[0], AMD64_GARTAPERTUREBASE, &apbase);
Linus Torvalds's avatar
Linus Torvalds committed
470 471

	/* if x86-64 aperture base is beyond 4G, exit here */
472
	if ( (apbase & 0x7fff) >> (32 - 25) ) {
473
		dev_info(&pdev->dev, "aperture base > 4G\n");
474 475
		return -ENODEV;
	}
Linus Torvalds's avatar
Linus Torvalds committed
476 477 478 479 480 481 482 483 484 485 486 487 488 489

	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);

490 491
	pci_dev_put(dev1);

Linus Torvalds's avatar
Linus Torvalds committed
492 493 494 495 496 497 498 499
	return 0;
}

static int __devinit agp_amd64_probe(struct pci_dev *pdev,
				     const struct pci_device_id *ent)
{
	struct agp_bridge_data *bridge;
	u8 cap_ptr;
500
	int err;
Linus Torvalds's avatar
Linus Torvalds committed
501 502 503 504 505 506 507 508 509 510 511 512 513 514 515

	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 {
516 517
		dev_info(&pdev->dev, "AGP bridge [%04x/%04x]\n",
			 pdev->vendor, pdev->device);
Linus Torvalds's avatar
Linus Torvalds committed
518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548
	}

	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);
549 550 551 552 553 554
	err = agp_add_bridge(bridge);
	if (err < 0)
		return err;

	agp_bridges_found++;
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
555 556 557 558 559 560
}

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

561
	release_mem_region(virt_to_phys(bridge->gatt_table_real),
Linus Torvalds's avatar
Linus Torvalds committed
562 563 564 565 566
			   amd64_aperture_sizes[bridge->aperture_size_idx].size);
	agp_remove_bridge(bridge);
	agp_put_bridge(bridge);
}

567 568 569 570 571 572 573 574 575 576 577 578 579 580 581
#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);

582 583 584
	if (pdev->vendor == PCI_VENDOR_ID_NVIDIA)
		nforce3_agp_init(pdev);

585 586 587 588 589
	return amd_8151_configure();
}

#endif /* CONFIG_PM */

Linus Torvalds's avatar
Linus Torvalds committed
590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 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
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,
	},
635 636 637 638 639
	/* VIA K8M890 / K8N890 */
	{
	.class          = (PCI_CLASS_BRIDGE_HOST << 8),
	.class_mask     = ~0,
	.vendor         = PCI_VENDOR_ID_VIA,
Dave Jones's avatar
Dave Jones committed
640
	.device         = PCI_DEVICE_ID_VIA_VT3336,
641 642 643
	.subvendor      = PCI_ANY_ID,
	.subdevice      = PCI_ANY_ID,
	},
Linus Torvalds's avatar
Linus Torvalds committed
644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 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
	/* 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
688 689 690 691 692 693 694 695 696
	/* 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,
	},
697 698 699 700 701
	/* ALI/ULI M1695 */
	{
	.class		= (PCI_CLASS_BRIDGE_HOST << 8),
	.class_mask	= ~0,
	.vendor		= PCI_VENDOR_ID_AL,
702
	.device		= 0x1695,
703 704 705 706
	.subvendor	= PCI_ANY_ID,
	.subdevice	= PCI_ANY_ID,
	},

Linus Torvalds's avatar
Linus Torvalds committed
707 708 709 710 711 712 713 714 715 716
	{ }
};

MODULE_DEVICE_TABLE(pci, agp_amd64_pci_table);

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,
717 718 719 720
#ifdef CONFIG_PM
	.suspend	= agp_amd64_suspend,
	.resume		= agp_amd64_resume,
#endif
Linus Torvalds's avatar
Linus Torvalds committed
721 722 723 724 725 726 727 728 729 730
};


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

	if (agp_off)
		return -EINVAL;
731

732 733 734 735 736
	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
737 738 739 740 741 742 743 744 745 746 747 748
		struct pci_dev *dev;
		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 */
749
		if (!pci_dev_present(k8_nb_ids))
Linus Torvalds's avatar
Linus Torvalds committed
750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767
			return -ENODEV;

		/* Look for any AGP bridge */
		dev = NULL;
		err = -ENODEV;
		for_each_pci_dev(dev) {
			if (!pci_find_capability(dev, PCI_CAP_ID_AGP))
				continue;
			/* Only one bridge supported right now */
			if (agp_amd64_probe(dev, NULL) == 0) {
				err = 0;
				break;
			}
		}
	}
	return err;
}

768 769
static int __init agp_amd64_mod_init(void)
{
770
#ifndef MODULE
771 772
	if (gart_iommu_aperture)
		return agp_bridges_found ? 0 : -ENODEV;
773
#endif
774 775 776
	return agp_amd64_init();
}

Linus Torvalds's avatar
Linus Torvalds committed
777 778
static void __exit agp_amd64_cleanup(void)
{
779
#ifndef MODULE
780 781
	if (gart_iommu_aperture)
		return;
782
#endif
Linus Torvalds's avatar
Linus Torvalds committed
783 784 785 786 787
	if (aperture_resource)
		release_resource(aperture_resource);
	pci_unregister_driver(&agp_amd64_pci_driver);
}

788
module_init(agp_amd64_mod_init);
Linus Torvalds's avatar
Linus Torvalds committed
789 790
module_exit(agp_amd64_cleanup);

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