generic.c 20.1 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
/*
Dave Jones's avatar
Dave Jones committed
2
 * AGPGART driver.
3
 * Copyright (C) 2002-2003 Dave Jones.
4
 * Copyright (C) 1999 Jeff Hartmann.
Linus Torvalds's avatar
Linus Torvalds committed
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
 * Copyright (C) 1999 Precision Insight, Inc.
 * Copyright (C) 1999 Xi Graphics, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * JEFF HARTMANN, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM, 
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 
 * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
26 27
 * TODO: 
 * - Allocate more than order 0 pages to avoid too much linear map splitting.
Linus Torvalds's avatar
Linus Torvalds committed
28 29 30 31 32 33 34
 */
#include <linux/config.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/pagemap.h>
#include <linux/miscdevice.h>
Linus Torvalds's avatar
Linus Torvalds committed
35
#include <linux/pm.h>
Linus Torvalds's avatar
Linus Torvalds committed
36
#include <linux/agp_backend.h>
37
#include <linux/vmalloc.h>
Linus Torvalds's avatar
Linus Torvalds committed
38 39
#include "agp.h"

40
__u32 *agp_gatt_table; 
41
int agp_memory_reserved;
Linus Torvalds's avatar
Linus Torvalds committed
42 43 44

/* 
 * Generic routines for handling agp_memory structures -
45
 * They use the basic page allocation routines to do the brunt of the work.
Linus Torvalds's avatar
Linus Torvalds committed
46 47
 */

48
void agp_free_key(int key)
Linus Torvalds's avatar
Linus Torvalds committed
49
{
50
	if (key < 0)
Linus Torvalds's avatar
Linus Torvalds committed
51
		return;
52 53

	if (key < MAXKEY)
54
		clear_bit(key, agp_bridge->key_list);
Linus Torvalds's avatar
Linus Torvalds committed
55
}
56 57
EXPORT_SYMBOL(agp_free_key);

Linus Torvalds's avatar
Linus Torvalds committed
58 59 60 61 62

static int agp_get_key(void)
{
	int bit;

63
	bit = find_first_zero_bit(agp_bridge->key_list, MAXKEY);
Linus Torvalds's avatar
Linus Torvalds committed
64
	if (bit < MAXKEY) {
65
		set_bit(bit, agp_bridge->key_list);
Linus Torvalds's avatar
Linus Torvalds committed
66 67 68 69 70
		return bit;
	}
	return -1;
}

71

72
agp_memory *agp_create_memory(int scratch_pages)
Linus Torvalds's avatar
Linus Torvalds committed
73 74 75 76 77
{
	agp_memory *new;

	new = kmalloc(sizeof(agp_memory), GFP_KERNEL);

78
	if (new == NULL)
Linus Torvalds's avatar
Linus Torvalds committed
79
		return NULL;
80

Linus Torvalds's avatar
Linus Torvalds committed
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
	memset(new, 0, sizeof(agp_memory));
	new->key = agp_get_key();

	if (new->key < 0) {
		kfree(new);
		return NULL;
	}
	new->memory = vmalloc(PAGE_SIZE * scratch_pages);

	if (new->memory == NULL) {
		agp_free_key(new->key);
		kfree(new);
		return NULL;
	}
	new->num_scratch_pages = scratch_pages;
	return new;
}
98 99
EXPORT_SYMBOL(agp_create_memory);

100 101 102 103 104 105 106
/**
 *	agp_free_memory - free memory associated with an agp_memory pointer.
 *
 *	@curr:		agp_memory pointer to be freed.
 *
 *	It is the only function that can be called when the backend is not owned
 *	by the caller.  (So it can free memory on client death.)
107
 */
Linus Torvalds's avatar
Linus Torvalds committed
108 109
void agp_free_memory(agp_memory * curr)
{
110
	size_t i;
Linus Torvalds's avatar
Linus Torvalds committed
111

112
	if ((agp_bridge->type == NOT_SUPPORTED) || (curr == NULL))
Linus Torvalds's avatar
Linus Torvalds committed
113
		return;
114 115

	if (curr->is_bound == TRUE)
Linus Torvalds's avatar
Linus Torvalds committed
116
		agp_unbind_memory(curr);
117

Linus Torvalds's avatar
Linus Torvalds committed
118
	if (curr->type != 0) {
119
		agp_bridge->driver->free_by_type(curr);
Linus Torvalds's avatar
Linus Torvalds committed
120 121 122 123
		return;
	}
	if (curr->page_count != 0) {
		for (i = 0; i < curr->page_count; i++) {
124
			agp_bridge->driver->agp_destroy_page(phys_to_virt(curr->memory[i]));
Linus Torvalds's avatar
Linus Torvalds committed
125 126 127 128 129 130
		}
	}
	agp_free_key(curr->key);
	vfree(curr->memory);
	kfree(curr);
}
131
EXPORT_SYMBOL(agp_free_memory);
Linus Torvalds's avatar
Linus Torvalds committed
132 133 134

#define ENTRIES_PER_PAGE		(PAGE_SIZE / sizeof(unsigned long))

135 136 137 138 139 140 141 142 143 144
/**
 *	agp_allocate_memory  -  allocate a group of pages of a certain type.
 *
 *	@page_count:	size_t argument of the number of pages
 *	@type:	u32 argument of the type of memory to be allocated.  
 *
 *	Every agp bridge device will allow you to allocate AGP_NORMAL_MEMORY which
 *	maps to physical ram.  Any other type is device dependent.
 *
 *	It returns NULL whenever memory is unavailable. 
145
 */
Linus Torvalds's avatar
Linus Torvalds committed
146 147 148 149
agp_memory *agp_allocate_memory(size_t page_count, u32 type)
{
	int scratch_pages;
	agp_memory *new;
150
	size_t i;
Linus Torvalds's avatar
Linus Torvalds committed
151

152
	if (agp_bridge->type == NOT_SUPPORTED)
Linus Torvalds's avatar
Linus Torvalds committed
153
		return NULL;
154

155
	if ((atomic_read(&agp_bridge->current_memory_agp) + page_count) > agp_bridge->max_memory_agp)
Linus Torvalds's avatar
Linus Torvalds committed
156 157 158
		return NULL;

	if (type != 0) {
159
		new = agp_bridge->driver->alloc_by_type(page_count, type);
Linus Torvalds's avatar
Linus Torvalds committed
160 161 162 163 164 165 166
		return new;
	}

	scratch_pages = (page_count + ENTRIES_PER_PAGE - 1) / ENTRIES_PER_PAGE;

	new = agp_create_memory(scratch_pages);

167
	if (new == NULL)
Linus Torvalds's avatar
Linus Torvalds committed
168
		return NULL;
169

Linus Torvalds's avatar
Linus Torvalds committed
170
	for (i = 0; i < page_count; i++) {
171
		void *addr = agp_bridge->driver->agp_alloc_page();
Linus Torvalds's avatar
Linus Torvalds committed
172

173
		if (addr == NULL) {
Linus Torvalds's avatar
Linus Torvalds committed
174 175 176
			agp_free_memory(new);
			return NULL;
		}
Bjorn Helgaas's avatar
Bjorn Helgaas committed
177
		new->memory[i] = virt_to_phys(addr);
Linus Torvalds's avatar
Linus Torvalds committed
178 179 180
		new->page_count++;
	}

181 182
	flush_agp_mappings();

Linus Torvalds's avatar
Linus Torvalds committed
183 184
	return new;
}
185 186
EXPORT_SYMBOL(agp_allocate_memory);

Linus Torvalds's avatar
Linus Torvalds committed
187 188 189

/* End - Generic routines for handling agp_memory structures */

190

Linus Torvalds's avatar
Linus Torvalds committed
191 192 193 194 195
static int agp_return_size(void)
{
	int current_size;
	void *temp;

196
	temp = agp_bridge->current_size;
Linus Torvalds's avatar
Linus Torvalds committed
197

198
	switch (agp_bridge->driver->size_type) {
Linus Torvalds's avatar
Linus Torvalds committed
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
	case U8_APER_SIZE:
		current_size = A_SIZE_8(temp)->size;
		break;
	case U16_APER_SIZE:
		current_size = A_SIZE_16(temp)->size;
		break;
	case U32_APER_SIZE:
		current_size = A_SIZE_32(temp)->size;
		break;
	case LVL2_APER_SIZE:
		current_size = A_SIZE_LVL2(temp)->size;
		break;
	case FIXED_APER_SIZE:
		current_size = A_SIZE_FIX(temp)->size;
		break;
	default:
		current_size = 0;
		break;
	}

Andi Kleen's avatar
Andi Kleen committed
219 220 221
	current_size -= (agp_memory_reserved / (1024*1024));
	if (current_size <0)
		current_size = 0;
Linus Torvalds's avatar
Linus Torvalds committed
222 223 224
	return current_size;
}

225

226 227 228 229 230
int agp_num_entries(void)
{
	int num_entries;
	void *temp;

231
	temp = agp_bridge->current_size;
232

233
	switch (agp_bridge->driver->size_type) {
234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
	case U8_APER_SIZE:
		num_entries = A_SIZE_8(temp)->num_entries;
		break;
	case U16_APER_SIZE:
		num_entries = A_SIZE_16(temp)->num_entries;
		break;
	case U32_APER_SIZE:
		num_entries = A_SIZE_32(temp)->num_entries;
		break;
	case LVL2_APER_SIZE:
		num_entries = A_SIZE_LVL2(temp)->num_entries;
		break;
	case FIXED_APER_SIZE:
		num_entries = A_SIZE_FIX(temp)->num_entries;
		break;
	default:
		num_entries = 0;
		break;
	}

	num_entries -= agp_memory_reserved>>PAGE_SHIFT;
	if (num_entries<0)
		num_entries = 0;
	return num_entries;
}
259
EXPORT_SYMBOL_GPL(agp_num_entries);
260

Linus Torvalds's avatar
Linus Torvalds committed
261

262 263 264 265 266 267 268
/**
 *	agp_copy_info  -  copy bridge state information
 *
 *	@info:		agp_kern_info pointer.  The caller should insure that this pointer is valid. 
 *
 *	This function copies information about the agp bridge device and the state of
 *	the agp backend into an agp_kern_info pointer.
269
 */
270
int agp_copy_info(agp_kern_info * info)
Linus Torvalds's avatar
Linus Torvalds committed
271 272
{
	memset(info, 0, sizeof(agp_kern_info));
273 274
	if (agp_bridge->type == NOT_SUPPORTED) {
		info->chipset = agp_bridge->type;
275
		return -EIO;
Linus Torvalds's avatar
Linus Torvalds committed
276
	}
277 278 279 280 281 282
	info->version.major = agp_bridge->version->major;
	info->version.minor = agp_bridge->version->minor;
	info->device = agp_bridge->dev;
	info->chipset = agp_bridge->type;
	info->mode = agp_bridge->mode;
	info->aper_base = agp_bridge->gart_bus_addr;
Linus Torvalds's avatar
Linus Torvalds committed
283
	info->aper_size = agp_return_size();
284 285
	info->max_memory = agp_bridge->max_memory_agp;
	info->current_memory = atomic_read(&agp_bridge->current_memory_agp);
286
	info->cant_use_aperture = agp_bridge->driver->cant_use_aperture;
287
	info->vm_ops = agp_bridge->vm_ops;
288
	info->page_mask = ~0UL;
289
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
290
}
291 292
EXPORT_SYMBOL(agp_copy_info);

Linus Torvalds's avatar
Linus Torvalds committed
293 294 295

/* End - Routine to copy over information structure */

296

Linus Torvalds's avatar
Linus Torvalds committed
297 298 299 300 301 302
/*
 * Routines for handling swapping of agp_memory into the GATT -
 * These routines take agp_memory and insert them into the GATT.
 * They call device specific routines to actually write to the GATT.
 */

303 304
/**
 *	agp_bind_memory  -  Bind an agp_memory structure into the GATT.
305
 * 
306 307 308 309 310
 *	@curr:		agp_memory pointer
 *	@pg_start:	an offset into the graphics aperture translation table
 *
 *	It returns -EINVAL if the pointer == NULL.
 *	It returns -EBUSY if the area of the table requested is already in use.
311
 */
Linus Torvalds's avatar
Linus Torvalds committed
312 313 314 315
int agp_bind_memory(agp_memory * curr, off_t pg_start)
{
	int ret_val;

316
	if ((agp_bridge->type == NOT_SUPPORTED) ||
Linus Torvalds's avatar
Linus Torvalds committed
317 318 319 320
	    (curr == NULL) || (curr->is_bound == TRUE)) {
		return -EINVAL;
	}
	if (curr->is_flushed == FALSE) {
321
		agp_bridge->driver->cache_flush();
Linus Torvalds's avatar
Linus Torvalds committed
322 323
		curr->is_flushed = TRUE;
	}
324
	ret_val = agp_bridge->driver->insert_memory(curr, pg_start, curr->type);
Linus Torvalds's avatar
Linus Torvalds committed
325

326
	if (ret_val != 0)
Linus Torvalds's avatar
Linus Torvalds committed
327
		return ret_val;
328

Linus Torvalds's avatar
Linus Torvalds committed
329 330 331 332
	curr->is_bound = TRUE;
	curr->pg_start = pg_start;
	return 0;
}
333 334
EXPORT_SYMBOL(agp_bind_memory);

Linus Torvalds's avatar
Linus Torvalds committed
335

336 337
/**
 *	agp_unbind_memory  -  Removes an agp_memory structure from the GATT
338
 * 
339
 * @curr:	agp_memory pointer to be removed from the GATT.
340 341 342 343
 * 
 * It returns -EINVAL if this piece of agp_memory is not currently bound to
 * the graphics aperture translation table or if the agp_memory pointer == NULL
 */
Linus Torvalds's avatar
Linus Torvalds committed
344 345 346 347
int agp_unbind_memory(agp_memory * curr)
{
	int ret_val;

348
	if ((agp_bridge->type == NOT_SUPPORTED) || (curr == NULL))
Linus Torvalds's avatar
Linus Torvalds committed
349
		return -EINVAL;
350 351

	if (curr->is_bound != TRUE)
Linus Torvalds's avatar
Linus Torvalds committed
352
		return -EINVAL;
353

354
	ret_val = agp_bridge->driver->remove_memory(curr, curr->pg_start, curr->type);
Linus Torvalds's avatar
Linus Torvalds committed
355

356
	if (ret_val != 0)
Linus Torvalds's avatar
Linus Torvalds committed
357
		return ret_val;
358

Linus Torvalds's avatar
Linus Torvalds committed
359 360 361 362
	curr->is_bound = FALSE;
	curr->pg_start = 0;
	return 0;
}
363
EXPORT_SYMBOL(agp_unbind_memory);
Linus Torvalds's avatar
Linus Torvalds committed
364 365 366 367 368

/* End - Routines for handling swapping of agp_memory into the GATT */


/* Generic Agp routines - Start */
369

370 371 372 373 374 375 376 377 378 379 380 381 382 383 384
u32 agp_collect_device_status(u32 mode, u32 command)
{
	struct pci_dev *device;
	u8 agp;
	u32 scratch; 

	pci_for_each_dev(device) {
		agp = pci_find_capability(device, PCI_CAP_ID_AGP);
		if (!agp)
			continue;

		/*
		 * Ok, here we have a AGP device. Disable impossible 
		 * settings, and adjust the readqueue to the minimum.
		 */
385
		pci_read_config_dword(device, agp + PCI_AGP_STATUS, &scratch);
386 387

		/* adjust RQ depth */
388 389 390 391
		command = ((command & ~AGPSTAT_RQ_DEPTH) |
		     min_t(u32, (mode & AGPSTAT_RQ_DEPTH),
			 min_t(u32, (command & AGPSTAT_RQ_DEPTH),
			     (scratch & AGPSTAT_RQ_DEPTH))));
392 393

		/* disable SBA if it's not supported */
394 395
		if (!((command & AGPSTAT_SBA) && (scratch & AGPSTAT_SBA) && (mode & AGPSTAT_SBA)))
			command &= ~AGPSTAT_SBA;
396 397

		/* disable FW if it's not supported */
398 399 400 401 402 403 404 405 406 407 408 409
		if (!((command & AGPSTAT_FW) && (scratch & AGPSTAT_FW) && (mode & AGPSTAT_FW)))
			command &= ~AGPSTAT_FW;

		/* Set speed */
		if (!((command & AGPSTAT2_4X) && (scratch & AGPSTAT2_4X) && (mode & AGPSTAT2_4X)))
			command &= ~AGPSTAT2_4X;

		if (!((command & AGPSTAT2_2X) && (scratch & AGPSTAT2_2X) && (mode & AGPSTAT2_2X)))
			command &= ~AGPSTAT2_2X;

		if (!((command & AGPSTAT2_1X) && (scratch & AGPSTAT2_1X) && (mode & AGPSTAT2_1X)))
			command &= ~AGPSTAT2_1X;
410 411
	}

412 413 414
	/* Now we know what mode it should be, clear out the unwanted bits. */
	if (command & AGPSTAT2_4X)
		command &= ~(AGPSTAT2_1X | AGPSTAT2_2X);	/* 4X */
415

416 417
	if (command & AGPSTAT2_2X)
		command &= ~(AGPSTAT2_1X | AGPSTAT2_4X);	/* 2X */
418

419 420
	if (command & AGPSTAT2_1X)
		command &= ~(AGPSTAT2_2X | AGPSTAT2_4X);	/* 1Xf */
421 422 423

	return command;
}
424 425
EXPORT_SYMBOL(agp_collect_device_status);

426

427
void agp_device_command(u32 command, int agp_v3)
428 429
{
	struct pci_dev *device;
430 431 432 433 434
	int mode;

	mode = command & 0x7;
	if (agp_v3)
		mode *= 4;
435 436 437 438 439 440

	pci_for_each_dev(device) {
		u8 agp = pci_find_capability(device, PCI_CAP_ID_AGP);
		if (!agp)
			continue;

441 442
		printk(KERN_INFO PFX "Putting AGP V%d device at %s into %dx mode\n",
				agp_v3 ? 3 : 2, device->slot_name, mode);
443
		pci_write_config_dword(device, agp + PCI_AGP_COMMAND, command);
444 445
	}
}
446 447
EXPORT_SYMBOL(agp_device_command);

448

449
void agp_generic_enable(u32 mode)
Linus Torvalds's avatar
Linus Torvalds committed
450
{
451
	u32 command, ncapid, major, minor;
Linus Torvalds's avatar
Linus Torvalds committed
452

453 454 455 456
	pci_read_config_dword(agp_bridge->dev, agp_bridge->capndx, &ncapid);
	major = (ncapid >> 20) & 0xf;
	minor = (ncapid >> 16) & 0xf;
	printk(KERN_INFO PFX "Found an AGP %d.%d compliant device.\n",major, minor);
Linus Torvalds's avatar
Linus Torvalds committed
457

458 459 460 461 462 463
	if(major >= 3) {
		u32 agp_3_0;

		pci_read_config_dword(agp_bridge->dev, agp_bridge->capndx + 0x4, &agp_3_0);
		/* Check to see if we are operating in 3.0 mode */
		if((agp_3_0 >> 3) & 0x1) {
464
			agp_3_0_node_enable(agp_bridge, mode, minor);
465 466 467 468 469 470
			return;
		} else {
			printk (KERN_INFO PFX "not in AGP 3.0 mode, falling back to 2.x\n");
		}
	}

Dave Jones's avatar
Dave Jones committed
471 472 473
	/* AGP v<3 */
	pci_read_config_dword(agp_bridge->dev,
		      agp_bridge->capndx + PCI_AGP_STATUS, &command);
Linus Torvalds's avatar
Linus Torvalds committed
474

Dave Jones's avatar
Dave Jones committed
475
	command = agp_collect_device_status(mode, command);
476
	command |= AGPSTAT_AGP_ENABLE;
Linus Torvalds's avatar
Linus Torvalds committed
477

Dave Jones's avatar
Dave Jones committed
478 479 480
	pci_write_config_dword(agp_bridge->dev,
		       agp_bridge->capndx + PCI_AGP_COMMAND, command);
	agp_device_command(command, 0);
Linus Torvalds's avatar
Linus Torvalds committed
481
}
482 483
EXPORT_SYMBOL(agp_generic_enable);

Linus Torvalds's avatar
Linus Torvalds committed
484

485
int agp_generic_create_gatt_table(void)
Linus Torvalds's avatar
Linus Torvalds committed
486 487 488 489 490 491 492 493 494 495 496
{
	char *table;
	char *table_end;
	int size;
	int page_order;
	int num_entries;
	int i;
	void *temp;
	struct page *page;

	/* The generic routines can't handle 2 level gatt's */
497
	if (agp_bridge->driver->size_type == LVL2_APER_SIZE)
Linus Torvalds's avatar
Linus Torvalds committed
498 499 500
		return -EINVAL;

	table = NULL;
501 502
	i = agp_bridge->aperture_size_idx;
	temp = agp_bridge->current_size;
Linus Torvalds's avatar
Linus Torvalds committed
503 504
	size = page_order = num_entries = 0;

505
	if (agp_bridge->driver->size_type != FIXED_APER_SIZE) {
Linus Torvalds's avatar
Linus Torvalds committed
506
		do {
507
			switch (agp_bridge->driver->size_type) {
Linus Torvalds's avatar
Linus Torvalds committed
508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537
			case U8_APER_SIZE:
				size = A_SIZE_8(temp)->size;
				page_order =
				    A_SIZE_8(temp)->page_order;
				num_entries =
				    A_SIZE_8(temp)->num_entries;
				break;
			case U16_APER_SIZE:
				size = A_SIZE_16(temp)->size;
				page_order = A_SIZE_16(temp)->page_order;
				num_entries = A_SIZE_16(temp)->num_entries;
				break;
			case U32_APER_SIZE:
				size = A_SIZE_32(temp)->size;
				page_order = A_SIZE_32(temp)->page_order;
				num_entries = A_SIZE_32(temp)->num_entries;
				break;
				/* This case will never really happen. */
			case FIXED_APER_SIZE:
			case LVL2_APER_SIZE:
			default:
				size = page_order = num_entries = 0;
				break;
			}

			table = (char *) __get_free_pages(GFP_KERNEL,
							  page_order);

			if (table == NULL) {
				i++;
538
				switch (agp_bridge->driver->size_type) {
Linus Torvalds's avatar
Linus Torvalds committed
539
				case U8_APER_SIZE:
540
					agp_bridge->current_size = A_IDX8(agp_bridge);
Linus Torvalds's avatar
Linus Torvalds committed
541 542
					break;
				case U16_APER_SIZE:
543
					agp_bridge->current_size = A_IDX16(agp_bridge);
Linus Torvalds's avatar
Linus Torvalds committed
544 545
					break;
				case U32_APER_SIZE:
546
					agp_bridge->current_size = A_IDX32(agp_bridge);
Linus Torvalds's avatar
Linus Torvalds committed
547
					break;
548
					/* This case will never really happen. */
Linus Torvalds's avatar
Linus Torvalds committed
549 550 551
				case FIXED_APER_SIZE:
				case LVL2_APER_SIZE:
				default:
552 553
					agp_bridge->current_size =
					    agp_bridge->current_size;
Linus Torvalds's avatar
Linus Torvalds committed
554 555
					break;
				}
556
				temp = agp_bridge->current_size;	
Linus Torvalds's avatar
Linus Torvalds committed
557
			} else {
558
				agp_bridge->aperture_size_idx = i;
Linus Torvalds's avatar
Linus Torvalds committed
559
			}
560
		} while (!table && (i < agp_bridge->driver->num_aperture_sizes));
Linus Torvalds's avatar
Linus Torvalds committed
561
	} else {
562 563 564
		size = ((struct aper_size_info_fixed *) temp)->size;
		page_order = ((struct aper_size_info_fixed *) temp)->page_order;
		num_entries = ((struct aper_size_info_fixed *) temp)->num_entries;
Linus Torvalds's avatar
Linus Torvalds committed
565 566 567
		table = (char *) __get_free_pages(GFP_KERNEL, page_order);
	}

568
	if (table == NULL)
Linus Torvalds's avatar
Linus Torvalds committed
569
		return -ENOMEM;
570

Linus Torvalds's avatar
Linus Torvalds committed
571 572 573
	table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1);

	for (page = virt_to_page(table); page <= virt_to_page(table_end); page++)
574
		SetPageReserved(page);
Linus Torvalds's avatar
Linus Torvalds committed
575

576
	agp_bridge->gatt_table_real = (u32 *) table;
577
	agp_gatt_table = (void *)table; 
578

579
	agp_bridge->driver->cache_flush();
580
	agp_bridge->gatt_table = ioremap_nocache(virt_to_phys(table),
Linus Torvalds's avatar
Linus Torvalds committed
581
					(PAGE_SIZE * (1 << page_order)));
582
	agp_bridge->driver->cache_flush();
Linus Torvalds's avatar
Linus Torvalds committed
583

584
	if (agp_bridge->gatt_table == NULL) {
Linus Torvalds's avatar
Linus Torvalds committed
585
		for (page = virt_to_page(table); page <= virt_to_page(table_end); page++)
586
			ClearPageReserved(page);
Linus Torvalds's avatar
Linus Torvalds committed
587 588 589 590 591

		free_pages((unsigned long) table, page_order);

		return -ENOMEM;
	}
592
	agp_bridge->gatt_bus_addr = virt_to_phys(agp_bridge->gatt_table_real);
Linus Torvalds's avatar
Linus Torvalds committed
593

594
	/* AK: bogus, should encode addresses > 4GB */
595
	for (i = 0; i < num_entries; i++)
596
		agp_bridge->gatt_table[i] = (unsigned long) agp_bridge->scratch_page;
Linus Torvalds's avatar
Linus Torvalds committed
597 598 599

	return 0;
}
600 601
EXPORT_SYMBOL(agp_generic_create_gatt_table);

602
int agp_generic_suspend(void)
Linus Torvalds's avatar
Linus Torvalds committed
603 604 605
{
	return 0;
}
606 607
EXPORT_SYMBOL(agp_generic_suspend);

Linus Torvalds's avatar
Linus Torvalds committed
608

609
void agp_generic_resume(void)
Linus Torvalds's avatar
Linus Torvalds committed
610
{
Linus Torvalds's avatar
Linus Torvalds committed
611
	return;
Linus Torvalds's avatar
Linus Torvalds committed
612
}
613 614
EXPORT_SYMBOL(agp_generic_resume);

Linus Torvalds's avatar
Linus Torvalds committed
615

616
int agp_generic_free_gatt_table(void)
Linus Torvalds's avatar
Linus Torvalds committed
617 618 619 620 621 622
{
	int page_order;
	char *table, *table_end;
	void *temp;
	struct page *page;

623
	temp = agp_bridge->current_size;
Linus Torvalds's avatar
Linus Torvalds committed
624

625
	switch (agp_bridge->driver->size_type) {
Linus Torvalds's avatar
Linus Torvalds committed
626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648
	case U8_APER_SIZE:
		page_order = A_SIZE_8(temp)->page_order;
		break;
	case U16_APER_SIZE:
		page_order = A_SIZE_16(temp)->page_order;
		break;
	case U32_APER_SIZE:
		page_order = A_SIZE_32(temp)->page_order;
		break;
	case FIXED_APER_SIZE:
		page_order = A_SIZE_FIX(temp)->page_order;
		break;
	case LVL2_APER_SIZE:
		/* The generic routines can't deal with 2 level gatt's */
		return -EINVAL;
		break;
	default:
		page_order = 0;
		break;
	}

	/* Do not worry about freeing memory, because if this is
	 * called, then all agp memory is deallocated and removed
649
	 * from the table. */
Linus Torvalds's avatar
Linus Torvalds committed
650

651 652
	iounmap(agp_bridge->gatt_table);
	table = (char *) agp_bridge->gatt_table_real;
Linus Torvalds's avatar
Linus Torvalds committed
653 654 655
	table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1);

	for (page = virt_to_page(table); page <= virt_to_page(table_end); page++)
656
		ClearPageReserved(page);
Linus Torvalds's avatar
Linus Torvalds committed
657

658
	free_pages((unsigned long) agp_bridge->gatt_table_real, page_order);
659 660 661 662 663 664

	agp_gatt_table = NULL;
	agp_bridge->gatt_table = NULL;
	agp_bridge->gatt_table_real = NULL;
	agp_bridge->gatt_bus_addr = NULL;

Linus Torvalds's avatar
Linus Torvalds committed
665 666
	return 0;
}
667 668
EXPORT_SYMBOL(agp_generic_free_gatt_table);

Linus Torvalds's avatar
Linus Torvalds committed
669

670
int agp_generic_insert_memory(agp_memory * mem, off_t pg_start, int type)
Linus Torvalds's avatar
Linus Torvalds committed
671
{
672 673 674
	int num_entries;
	size_t i;
	off_t j;
Linus Torvalds's avatar
Linus Torvalds committed
675 676
	void *temp;

677
	temp = agp_bridge->current_size;
Linus Torvalds's avatar
Linus Torvalds committed
678

679
	switch (agp_bridge->driver->size_type) {
Linus Torvalds's avatar
Linus Torvalds committed
680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700
	case U8_APER_SIZE:
		num_entries = A_SIZE_8(temp)->num_entries;
		break;
	case U16_APER_SIZE:
		num_entries = A_SIZE_16(temp)->num_entries;
		break;
	case U32_APER_SIZE:
		num_entries = A_SIZE_32(temp)->num_entries;
		break;
	case FIXED_APER_SIZE:
		num_entries = A_SIZE_FIX(temp)->num_entries;
		break;
	case LVL2_APER_SIZE:
		/* The generic routines can't deal with 2 level gatt's */
		return -EINVAL;
		break;
	default:
		num_entries = 0;
		break;
	}

701 702 703
	num_entries -= agp_memory_reserved/PAGE_SIZE;
	if (num_entries < 0) num_entries = 0;

Linus Torvalds's avatar
Linus Torvalds committed
704 705 706 707
	if (type != 0 || mem->type != 0) {
		/* The generic routines know nothing of memory types */
		return -EINVAL;
	}
708

709
	/* AK: could wrap */
710
	if ((pg_start + mem->page_count) > num_entries)
Linus Torvalds's avatar
Linus Torvalds committed
711
		return -EINVAL;
712

Linus Torvalds's avatar
Linus Torvalds committed
713 714 715
	j = pg_start;

	while (j < (pg_start + mem->page_count)) {
716
		if (!PGE_EMPTY(agp_bridge, agp_bridge->gatt_table[j])) {
Linus Torvalds's avatar
Linus Torvalds committed
717 718 719 720 721 722
			return -EBUSY;
		}
		j++;
	}

	if (mem->is_flushed == FALSE) {
723
		agp_bridge->driver->cache_flush();
Linus Torvalds's avatar
Linus Torvalds committed
724 725
		mem->is_flushed = TRUE;
	}
726 727

	for (i = 0, j = pg_start; i < mem->page_count; i++, j++)
728
		agp_bridge->gatt_table[j] =
729 730
				agp_bridge->driver->mask_memory(
						mem->memory[i], mem->type);
Linus Torvalds's avatar
Linus Torvalds committed
731

732
	agp_bridge->driver->tlb_flush(mem);
Linus Torvalds's avatar
Linus Torvalds committed
733 734
	return 0;
}
735 736
EXPORT_SYMBOL(agp_generic_insert_memory);

Linus Torvalds's avatar
Linus Torvalds committed
737

738
int agp_generic_remove_memory(agp_memory * mem, off_t pg_start, int type)
Linus Torvalds's avatar
Linus Torvalds committed
739
{
740
	size_t i;
Linus Torvalds's avatar
Linus Torvalds committed
741 742 743 744 745

	if (type != 0 || mem->type != 0) {
		/* The generic routines know nothing of memory types */
		return -EINVAL;
	}
746 747

	/* AK: bogus, should encode addresses > 4GB */
Linus Torvalds's avatar
Linus Torvalds committed
748
	for (i = pg_start; i < (mem->page_count + pg_start); i++) {
749 750
		agp_bridge->gatt_table[i] =
		    (unsigned long) agp_bridge->scratch_page;
Linus Torvalds's avatar
Linus Torvalds committed
751 752
	}

753
	agp_bridge->driver->tlb_flush(mem);
Linus Torvalds's avatar
Linus Torvalds committed
754 755
	return 0;
}
756 757
EXPORT_SYMBOL(agp_generic_remove_memory);

Linus Torvalds's avatar
Linus Torvalds committed
758

759
agp_memory *agp_generic_alloc_by_type(size_t page_count, int type)
Linus Torvalds's avatar
Linus Torvalds committed
760 761 762
{
	return NULL;
}
763 764
EXPORT_SYMBOL(agp_generic_alloc_by_type);

Linus Torvalds's avatar
Linus Torvalds committed
765

766
void agp_generic_free_by_type(agp_memory * curr)
Linus Torvalds's avatar
Linus Torvalds committed
767
{
768
	if (curr->memory != NULL)
Linus Torvalds's avatar
Linus Torvalds committed
769
		vfree(curr->memory);
770

Linus Torvalds's avatar
Linus Torvalds committed
771 772 773
	agp_free_key(curr->key);
	kfree(curr);
}
774 775
EXPORT_SYMBOL(agp_generic_free_by_type);

Linus Torvalds's avatar
Linus Torvalds committed
776

Linus Torvalds's avatar
Linus Torvalds committed
777 778
/* 
 * Basic Page Allocation Routines -
779 780
 * These routines handle page allocation and by default they reserve the allocated 
 * memory.  They also handle incrementing the current_memory_agp value, Which is checked
Linus Torvalds's avatar
Linus Torvalds committed
781 782 783
 * against a maximum value.
 */

784
void *agp_generic_alloc_page(void)
Linus Torvalds's avatar
Linus Torvalds committed
785
{
Linus Torvalds's avatar
Linus Torvalds committed
786 787 788
	struct page * page;
	
	page = alloc_page(GFP_KERNEL);
789
	if (page == NULL)
Linus Torvalds's avatar
Linus Torvalds committed
790
		return 0;
791

792 793
	map_page_into_agp(page);

794
	get_page(page);
Andrew Morton's avatar
Andrew Morton committed
795
	SetPageLocked(page);
796
	atomic_inc(&agp_bridge->current_memory_agp);
797
	return page_address(page);
Linus Torvalds's avatar
Linus Torvalds committed
798
}
799 800
EXPORT_SYMBOL(agp_generic_alloc_page);

Linus Torvalds's avatar
Linus Torvalds committed
801

802
void agp_generic_destroy_page(void *addr)
Linus Torvalds's avatar
Linus Torvalds committed
803
{
Linus Torvalds's avatar
Linus Torvalds committed
804
	struct page *page;
Linus Torvalds's avatar
Linus Torvalds committed
805

806
	if (addr == NULL)
Linus Torvalds's avatar
Linus Torvalds committed
807
		return;
808

809 810
	page = virt_to_page(addr);
	unmap_page_from_agp(page);
811
	put_page(page);
Andrew Morton's avatar
Andrew Morton committed
812
	unlock_page(page);
813
	free_page((unsigned long)addr);
814
	atomic_dec(&agp_bridge->current_memory_agp);
Linus Torvalds's avatar
Linus Torvalds committed
815
}
816
EXPORT_SYMBOL(agp_generic_destroy_page);
Linus Torvalds's avatar
Linus Torvalds committed
817 818 819

/* End Basic Page Allocation Routines */

820

821 822
/** 
 * agp_enable  -  initialise the agp point-to-point connection.
823
 * 
824
 * @mode:	agp mode register value to configure with.
825
 */
Linus Torvalds's avatar
Linus Torvalds committed
826 827
void agp_enable(u32 mode)
{
828
	if (agp_bridge->type == NOT_SUPPORTED)
829
		return;
830
	agp_bridge->driver->agp_enable(mode);
Linus Torvalds's avatar
Linus Torvalds committed
831
}
832
EXPORT_SYMBOL(agp_enable);
833

834 835 836 837 838 839
#ifdef CONFIG_SMP
static void ipi_handler(void *null)
{
	flush_agp_cache();
}
#endif
840

841 842 843 844 845 846 847 848 849 850
void global_cache_flush(void)
{
#ifdef CONFIG_SMP
	if (on_each_cpu(ipi_handler, NULL, 1, 1) != 0)
		panic(PFX "timed out waiting for the other CPUs!\n");
#else
	flush_agp_cache();
#endif
}
EXPORT_SYMBOL(global_cache_flush);
851