xref: /linux/drivers/char/agp/generic.c (revision 1c14cfbb)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * AGPGART driver.
31da177e4SLinus Torvalds  * Copyright (C) 2004 Silicon Graphics, Inc.
41da177e4SLinus Torvalds  * Copyright (C) 2002-2005 Dave Jones.
51da177e4SLinus Torvalds  * Copyright (C) 1999 Jeff Hartmann.
61da177e4SLinus Torvalds  * Copyright (C) 1999 Precision Insight, Inc.
71da177e4SLinus Torvalds  * Copyright (C) 1999 Xi Graphics, Inc.
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  * Permission is hereby granted, free of charge, to any person obtaining a
101da177e4SLinus Torvalds  * copy of this software and associated documentation files (the "Software"),
111da177e4SLinus Torvalds  * to deal in the Software without restriction, including without limitation
121da177e4SLinus Torvalds  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
131da177e4SLinus Torvalds  * and/or sell copies of the Software, and to permit persons to whom the
141da177e4SLinus Torvalds  * Software is furnished to do so, subject to the following conditions:
151da177e4SLinus Torvalds  *
161da177e4SLinus Torvalds  * The above copyright notice and this permission notice shall be included
171da177e4SLinus Torvalds  * in all copies or substantial portions of the Software.
181da177e4SLinus Torvalds  *
191da177e4SLinus Torvalds  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
201da177e4SLinus Torvalds  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
211da177e4SLinus Torvalds  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
221da177e4SLinus Torvalds  * JEFF HARTMANN, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM,
231da177e4SLinus Torvalds  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
241da177e4SLinus Torvalds  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
251da177e4SLinus Torvalds  * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
261da177e4SLinus Torvalds  *
271da177e4SLinus Torvalds  * TODO:
281da177e4SLinus Torvalds  * - Allocate more than order 0 pages to avoid too much linear map splitting.
291da177e4SLinus Torvalds  */
301da177e4SLinus Torvalds #include <linux/module.h>
311da177e4SLinus Torvalds #include <linux/pci.h>
321da177e4SLinus Torvalds #include <linux/init.h>
331da177e4SLinus Torvalds #include <linux/pagemap.h>
341da177e4SLinus Torvalds #include <linux/miscdevice.h>
351da177e4SLinus Torvalds #include <linux/pm.h>
361da177e4SLinus Torvalds #include <linux/agp_backend.h>
371da177e4SLinus Torvalds #include <linux/vmalloc.h>
381da177e4SLinus Torvalds #include <linux/dma-mapping.h>
391da177e4SLinus Torvalds #include <linux/mm.h>
401da177e4SLinus Torvalds #include <asm/io.h>
411da177e4SLinus Torvalds #include <asm/cacheflush.h>
421da177e4SLinus Torvalds #include <asm/pgtable.h>
431da177e4SLinus Torvalds #include "agp.h"
441da177e4SLinus Torvalds 
451da177e4SLinus Torvalds __u32 *agp_gatt_table;
461da177e4SLinus Torvalds int agp_memory_reserved;
471da177e4SLinus Torvalds 
481da177e4SLinus Torvalds /*
491da177e4SLinus Torvalds  * Needed by the Nforce GART driver for the time being. Would be
501da177e4SLinus Torvalds  * nice to do this some other way instead of needing this export.
511da177e4SLinus Torvalds  */
521da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(agp_memory_reserved);
531da177e4SLinus Torvalds 
541da177e4SLinus Torvalds #if defined(CONFIG_X86)
551da177e4SLinus Torvalds int map_page_into_agp(struct page *page)
561da177e4SLinus Torvalds {
571da177e4SLinus Torvalds 	int i;
581da177e4SLinus Torvalds 	i = change_page_attr(page, 1, PAGE_KERNEL_NOCACHE);
5988d51967SAlan Hourihane 	/* Caller's responsibility to call global_flush_tlb() for
6088d51967SAlan Hourihane 	 * performance reasons */
611da177e4SLinus Torvalds 	return i;
621da177e4SLinus Torvalds }
631da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(map_page_into_agp);
641da177e4SLinus Torvalds 
651da177e4SLinus Torvalds int unmap_page_from_agp(struct page *page)
661da177e4SLinus Torvalds {
671da177e4SLinus Torvalds 	int i;
681da177e4SLinus Torvalds 	i = change_page_attr(page, 1, PAGE_KERNEL);
6988d51967SAlan Hourihane 	/* Caller's responsibility to call global_flush_tlb() for
7088d51967SAlan Hourihane 	 * performance reasons */
711da177e4SLinus Torvalds 	return i;
721da177e4SLinus Torvalds }
731da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(unmap_page_from_agp);
741da177e4SLinus Torvalds #endif
751da177e4SLinus Torvalds 
761da177e4SLinus Torvalds /*
771da177e4SLinus Torvalds  * Generic routines for handling agp_memory structures -
781da177e4SLinus Torvalds  * They use the basic page allocation routines to do the brunt of the work.
791da177e4SLinus Torvalds  */
801da177e4SLinus Torvalds 
811da177e4SLinus Torvalds void agp_free_key(int key)
821da177e4SLinus Torvalds {
831da177e4SLinus Torvalds 	if (key < 0)
841da177e4SLinus Torvalds 		return;
851da177e4SLinus Torvalds 
861da177e4SLinus Torvalds 	if (key < MAXKEY)
871da177e4SLinus Torvalds 		clear_bit(key, agp_bridge->key_list);
881da177e4SLinus Torvalds }
891da177e4SLinus Torvalds EXPORT_SYMBOL(agp_free_key);
901da177e4SLinus Torvalds 
911da177e4SLinus Torvalds 
921da177e4SLinus Torvalds static int agp_get_key(void)
931da177e4SLinus Torvalds {
941da177e4SLinus Torvalds 	int bit;
951da177e4SLinus Torvalds 
961da177e4SLinus Torvalds 	bit = find_first_zero_bit(agp_bridge->key_list, MAXKEY);
971da177e4SLinus Torvalds 	if (bit < MAXKEY) {
981da177e4SLinus Torvalds 		set_bit(bit, agp_bridge->key_list);
991da177e4SLinus Torvalds 		return bit;
1001da177e4SLinus Torvalds 	}
1011da177e4SLinus Torvalds 	return -1;
1021da177e4SLinus Torvalds }
1031da177e4SLinus Torvalds 
104a030ce44SThomas Hellstrom /*
105a030ce44SThomas Hellstrom  * Use kmalloc if possible for the page list. Otherwise fall back to
106a030ce44SThomas Hellstrom  * vmalloc. This speeds things up and also saves memory for small AGP
107a030ce44SThomas Hellstrom  * regions.
108a030ce44SThomas Hellstrom  */
109a030ce44SThomas Hellstrom 
110a030ce44SThomas Hellstrom void agp_alloc_page_array(size_t size, struct agp_memory *mem)
111a030ce44SThomas Hellstrom {
112a030ce44SThomas Hellstrom 	mem->memory = NULL;
113a030ce44SThomas Hellstrom 	mem->vmalloc_flag = 0;
114a030ce44SThomas Hellstrom 
115*1c14cfbbSAndrew Morton 	if (size <= 2*PAGE_SIZE)
116a030ce44SThomas Hellstrom 		mem->memory = kmalloc(size, GFP_KERNEL | __GFP_NORETRY);
117a030ce44SThomas Hellstrom 	if (mem->memory == NULL) {
118a030ce44SThomas Hellstrom 		mem->memory = vmalloc(size);
119a030ce44SThomas Hellstrom 		mem->vmalloc_flag = 1;
120a030ce44SThomas Hellstrom 	}
121a030ce44SThomas Hellstrom }
122a030ce44SThomas Hellstrom EXPORT_SYMBOL(agp_alloc_page_array);
123a030ce44SThomas Hellstrom 
124a030ce44SThomas Hellstrom void agp_free_page_array(struct agp_memory *mem)
125a030ce44SThomas Hellstrom {
126a030ce44SThomas Hellstrom 	if (mem->vmalloc_flag) {
127a030ce44SThomas Hellstrom 		vfree(mem->memory);
128a030ce44SThomas Hellstrom 	} else {
129a030ce44SThomas Hellstrom 		kfree(mem->memory);
130a030ce44SThomas Hellstrom 	}
131a030ce44SThomas Hellstrom }
132a030ce44SThomas Hellstrom EXPORT_SYMBOL(agp_free_page_array);
133a030ce44SThomas Hellstrom 
134a030ce44SThomas Hellstrom 
135a030ce44SThomas Hellstrom static struct agp_memory *agp_create_user_memory(unsigned long num_agp_pages)
136a030ce44SThomas Hellstrom {
137a030ce44SThomas Hellstrom 	struct agp_memory *new;
138a030ce44SThomas Hellstrom 	unsigned long alloc_size = num_agp_pages*sizeof(struct page *);
139a030ce44SThomas Hellstrom 
140*1c14cfbbSAndrew Morton 	new = kzalloc(sizeof(struct agp_memory), GFP_KERNEL);
141a030ce44SThomas Hellstrom 	if (new == NULL)
142a030ce44SThomas Hellstrom 		return NULL;
143a030ce44SThomas Hellstrom 
144a030ce44SThomas Hellstrom 	new->key = agp_get_key();
145a030ce44SThomas Hellstrom 
146a030ce44SThomas Hellstrom 	if (new->key < 0) {
147a030ce44SThomas Hellstrom 		kfree(new);
148a030ce44SThomas Hellstrom 		return NULL;
149a030ce44SThomas Hellstrom 	}
150a030ce44SThomas Hellstrom 
151a030ce44SThomas Hellstrom 	agp_alloc_page_array(alloc_size, new);
152a030ce44SThomas Hellstrom 
153a030ce44SThomas Hellstrom 	if (new->memory == NULL) {
154a030ce44SThomas Hellstrom 		agp_free_key(new->key);
155a030ce44SThomas Hellstrom 		kfree(new);
156a030ce44SThomas Hellstrom 		return NULL;
157a030ce44SThomas Hellstrom 	}
158a030ce44SThomas Hellstrom 	new->num_scratch_pages = 0;
159a030ce44SThomas Hellstrom 	return new;
160a030ce44SThomas Hellstrom }
161a030ce44SThomas Hellstrom 
1621da177e4SLinus Torvalds struct agp_memory *agp_create_memory(int scratch_pages)
1631da177e4SLinus Torvalds {
1641da177e4SLinus Torvalds 	struct agp_memory *new;
1651da177e4SLinus Torvalds 
1660ea27d9fSDave Jones 	new = kzalloc(sizeof(struct agp_memory), GFP_KERNEL);
1671da177e4SLinus Torvalds 	if (new == NULL)
1681da177e4SLinus Torvalds 		return NULL;
1691da177e4SLinus Torvalds 
1701da177e4SLinus Torvalds 	new->key = agp_get_key();
1711da177e4SLinus Torvalds 
1721da177e4SLinus Torvalds 	if (new->key < 0) {
1731da177e4SLinus Torvalds 		kfree(new);
1741da177e4SLinus Torvalds 		return NULL;
1751da177e4SLinus Torvalds 	}
176a030ce44SThomas Hellstrom 
177a030ce44SThomas Hellstrom 	agp_alloc_page_array(PAGE_SIZE * scratch_pages, new);
1781da177e4SLinus Torvalds 
1791da177e4SLinus Torvalds 	if (new->memory == NULL) {
1801da177e4SLinus Torvalds 		agp_free_key(new->key);
1811da177e4SLinus Torvalds 		kfree(new);
1821da177e4SLinus Torvalds 		return NULL;
1831da177e4SLinus Torvalds 	}
1841da177e4SLinus Torvalds 	new->num_scratch_pages = scratch_pages;
185a030ce44SThomas Hellstrom 	new->type = AGP_NORMAL_MEMORY;
1861da177e4SLinus Torvalds 	return new;
1871da177e4SLinus Torvalds }
1881da177e4SLinus Torvalds EXPORT_SYMBOL(agp_create_memory);
1891da177e4SLinus Torvalds 
1901da177e4SLinus Torvalds /**
1911da177e4SLinus Torvalds  *	agp_free_memory - free memory associated with an agp_memory pointer.
1921da177e4SLinus Torvalds  *
1931da177e4SLinus Torvalds  *	@curr:		agp_memory pointer to be freed.
1941da177e4SLinus Torvalds  *
1951da177e4SLinus Torvalds  *	It is the only function that can be called when the backend is not owned
1961da177e4SLinus Torvalds  *	by the caller.  (So it can free memory on client death.)
1971da177e4SLinus Torvalds  */
1981da177e4SLinus Torvalds void agp_free_memory(struct agp_memory *curr)
1991da177e4SLinus Torvalds {
2001da177e4SLinus Torvalds 	size_t i;
2011da177e4SLinus Torvalds 
2021da177e4SLinus Torvalds 	if (curr == NULL)
2031da177e4SLinus Torvalds 		return;
2041da177e4SLinus Torvalds 
2051da177e4SLinus Torvalds 	if (curr->is_bound == TRUE)
2061da177e4SLinus Torvalds 		agp_unbind_memory(curr);
2071da177e4SLinus Torvalds 
208a030ce44SThomas Hellstrom 	if (curr->type >= AGP_USER_TYPES) {
209a030ce44SThomas Hellstrom 		agp_generic_free_by_type(curr);
210a030ce44SThomas Hellstrom 		return;
211a030ce44SThomas Hellstrom 	}
212a030ce44SThomas Hellstrom 
2131da177e4SLinus Torvalds 	if (curr->type != 0) {
2141da177e4SLinus Torvalds 		curr->bridge->driver->free_by_type(curr);
2151da177e4SLinus Torvalds 		return;
2161da177e4SLinus Torvalds 	}
2171da177e4SLinus Torvalds 	if (curr->page_count != 0) {
2181da177e4SLinus Torvalds 		for (i = 0; i < curr->page_count; i++) {
21907eee78eSKeir Fraser 			curr->bridge->driver->agp_destroy_page(gart_to_virt(curr->memory[i]));
2201da177e4SLinus Torvalds 		}
2216730c3c1SLinus Torvalds 		flush_agp_mappings();
2221da177e4SLinus Torvalds 	}
2231da177e4SLinus Torvalds 	agp_free_key(curr->key);
224a030ce44SThomas Hellstrom 	agp_free_page_array(curr);
2251da177e4SLinus Torvalds 	kfree(curr);
2261da177e4SLinus Torvalds }
2271da177e4SLinus Torvalds EXPORT_SYMBOL(agp_free_memory);
2281da177e4SLinus Torvalds 
2291da177e4SLinus Torvalds #define ENTRIES_PER_PAGE		(PAGE_SIZE / sizeof(unsigned long))
2301da177e4SLinus Torvalds 
2311da177e4SLinus Torvalds /**
2321da177e4SLinus Torvalds  *	agp_allocate_memory  -  allocate a group of pages of a certain type.
2331da177e4SLinus Torvalds  *
2341da177e4SLinus Torvalds  *	@page_count:	size_t argument of the number of pages
2351da177e4SLinus Torvalds  *	@type:	u32 argument of the type of memory to be allocated.
2361da177e4SLinus Torvalds  *
2371da177e4SLinus Torvalds  *	Every agp bridge device will allow you to allocate AGP_NORMAL_MEMORY which
2381da177e4SLinus Torvalds  *	maps to physical ram.  Any other type is device dependent.
2391da177e4SLinus Torvalds  *
2401da177e4SLinus Torvalds  *	It returns NULL whenever memory is unavailable.
2411da177e4SLinus Torvalds  */
2421da177e4SLinus Torvalds struct agp_memory *agp_allocate_memory(struct agp_bridge_data *bridge,
2431da177e4SLinus Torvalds 					size_t page_count, u32 type)
2441da177e4SLinus Torvalds {
2451da177e4SLinus Torvalds 	int scratch_pages;
2461da177e4SLinus Torvalds 	struct agp_memory *new;
2471da177e4SLinus Torvalds 	size_t i;
2481da177e4SLinus Torvalds 
2491da177e4SLinus Torvalds 	if (!bridge)
2501da177e4SLinus Torvalds 		return NULL;
2511da177e4SLinus Torvalds 
2521da177e4SLinus Torvalds 	if ((atomic_read(&bridge->current_memory_agp) + page_count) > bridge->max_memory_agp)
2531da177e4SLinus Torvalds 		return NULL;
2541da177e4SLinus Torvalds 
255a030ce44SThomas Hellstrom 	if (type >= AGP_USER_TYPES) {
256a030ce44SThomas Hellstrom 		new = agp_generic_alloc_user(page_count, type);
257a030ce44SThomas Hellstrom 		if (new)
258a030ce44SThomas Hellstrom 			new->bridge = bridge;
259a030ce44SThomas Hellstrom 		return new;
260a030ce44SThomas Hellstrom 	}
261a030ce44SThomas Hellstrom 
2621da177e4SLinus Torvalds 	if (type != 0) {
2631da177e4SLinus Torvalds 		new = bridge->driver->alloc_by_type(page_count, type);
2641da177e4SLinus Torvalds 		if (new)
2651da177e4SLinus Torvalds 			new->bridge = bridge;
2661da177e4SLinus Torvalds 		return new;
2671da177e4SLinus Torvalds 	}
2681da177e4SLinus Torvalds 
2691da177e4SLinus Torvalds 	scratch_pages = (page_count + ENTRIES_PER_PAGE - 1) / ENTRIES_PER_PAGE;
2701da177e4SLinus Torvalds 
2711da177e4SLinus Torvalds 	new = agp_create_memory(scratch_pages);
2721da177e4SLinus Torvalds 
2731da177e4SLinus Torvalds 	if (new == NULL)
2741da177e4SLinus Torvalds 		return NULL;
2751da177e4SLinus Torvalds 
2761da177e4SLinus Torvalds 	for (i = 0; i < page_count; i++) {
2771da177e4SLinus Torvalds 		void *addr = bridge->driver->agp_alloc_page(bridge);
2781da177e4SLinus Torvalds 
2791da177e4SLinus Torvalds 		if (addr == NULL) {
2801da177e4SLinus Torvalds 			agp_free_memory(new);
2811da177e4SLinus Torvalds 			return NULL;
2821da177e4SLinus Torvalds 		}
28307eee78eSKeir Fraser 		new->memory[i] = virt_to_gart(addr);
2841da177e4SLinus Torvalds 		new->page_count++;
2851da177e4SLinus Torvalds 	}
2861da177e4SLinus Torvalds 	new->bridge = bridge;
2871da177e4SLinus Torvalds 
2881da177e4SLinus Torvalds 	flush_agp_mappings();
2891da177e4SLinus Torvalds 
2901da177e4SLinus Torvalds 	return new;
2911da177e4SLinus Torvalds }
2921da177e4SLinus Torvalds EXPORT_SYMBOL(agp_allocate_memory);
2931da177e4SLinus Torvalds 
2941da177e4SLinus Torvalds 
2951da177e4SLinus Torvalds /* End - Generic routines for handling agp_memory structures */
2961da177e4SLinus Torvalds 
2971da177e4SLinus Torvalds 
2981da177e4SLinus Torvalds static int agp_return_size(void)
2991da177e4SLinus Torvalds {
3001da177e4SLinus Torvalds 	int current_size;
3011da177e4SLinus Torvalds 	void *temp;
3021da177e4SLinus Torvalds 
3031da177e4SLinus Torvalds 	temp = agp_bridge->current_size;
3041da177e4SLinus Torvalds 
3051da177e4SLinus Torvalds 	switch (agp_bridge->driver->size_type) {
3061da177e4SLinus Torvalds 	case U8_APER_SIZE:
3071da177e4SLinus Torvalds 		current_size = A_SIZE_8(temp)->size;
3081da177e4SLinus Torvalds 		break;
3091da177e4SLinus Torvalds 	case U16_APER_SIZE:
3101da177e4SLinus Torvalds 		current_size = A_SIZE_16(temp)->size;
3111da177e4SLinus Torvalds 		break;
3121da177e4SLinus Torvalds 	case U32_APER_SIZE:
3131da177e4SLinus Torvalds 		current_size = A_SIZE_32(temp)->size;
3141da177e4SLinus Torvalds 		break;
3151da177e4SLinus Torvalds 	case LVL2_APER_SIZE:
3161da177e4SLinus Torvalds 		current_size = A_SIZE_LVL2(temp)->size;
3171da177e4SLinus Torvalds 		break;
3181da177e4SLinus Torvalds 	case FIXED_APER_SIZE:
3191da177e4SLinus Torvalds 		current_size = A_SIZE_FIX(temp)->size;
3201da177e4SLinus Torvalds 		break;
3211da177e4SLinus Torvalds 	default:
3221da177e4SLinus Torvalds 		current_size = 0;
3231da177e4SLinus Torvalds 		break;
3241da177e4SLinus Torvalds 	}
3251da177e4SLinus Torvalds 
3261da177e4SLinus Torvalds 	current_size -= (agp_memory_reserved / (1024*1024));
3271da177e4SLinus Torvalds 	if (current_size <0)
3281da177e4SLinus Torvalds 		current_size = 0;
3291da177e4SLinus Torvalds 	return current_size;
3301da177e4SLinus Torvalds }
3311da177e4SLinus Torvalds 
3321da177e4SLinus Torvalds 
3331da177e4SLinus Torvalds int agp_num_entries(void)
3341da177e4SLinus Torvalds {
3351da177e4SLinus Torvalds 	int num_entries;
3361da177e4SLinus Torvalds 	void *temp;
3371da177e4SLinus Torvalds 
3381da177e4SLinus Torvalds 	temp = agp_bridge->current_size;
3391da177e4SLinus Torvalds 
3401da177e4SLinus Torvalds 	switch (agp_bridge->driver->size_type) {
3411da177e4SLinus Torvalds 	case U8_APER_SIZE:
3421da177e4SLinus Torvalds 		num_entries = A_SIZE_8(temp)->num_entries;
3431da177e4SLinus Torvalds 		break;
3441da177e4SLinus Torvalds 	case U16_APER_SIZE:
3451da177e4SLinus Torvalds 		num_entries = A_SIZE_16(temp)->num_entries;
3461da177e4SLinus Torvalds 		break;
3471da177e4SLinus Torvalds 	case U32_APER_SIZE:
3481da177e4SLinus Torvalds 		num_entries = A_SIZE_32(temp)->num_entries;
3491da177e4SLinus Torvalds 		break;
3501da177e4SLinus Torvalds 	case LVL2_APER_SIZE:
3511da177e4SLinus Torvalds 		num_entries = A_SIZE_LVL2(temp)->num_entries;
3521da177e4SLinus Torvalds 		break;
3531da177e4SLinus Torvalds 	case FIXED_APER_SIZE:
3541da177e4SLinus Torvalds 		num_entries = A_SIZE_FIX(temp)->num_entries;
3551da177e4SLinus Torvalds 		break;
3561da177e4SLinus Torvalds 	default:
3571da177e4SLinus Torvalds 		num_entries = 0;
3581da177e4SLinus Torvalds 		break;
3591da177e4SLinus Torvalds 	}
3601da177e4SLinus Torvalds 
3611da177e4SLinus Torvalds 	num_entries -= agp_memory_reserved>>PAGE_SHIFT;
3621da177e4SLinus Torvalds 	if (num_entries<0)
3631da177e4SLinus Torvalds 		num_entries = 0;
3641da177e4SLinus Torvalds 	return num_entries;
3651da177e4SLinus Torvalds }
3661da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(agp_num_entries);
3671da177e4SLinus Torvalds 
3681da177e4SLinus Torvalds 
3691da177e4SLinus Torvalds /**
3701da177e4SLinus Torvalds  *	agp_copy_info  -  copy bridge state information
3711da177e4SLinus Torvalds  *
3721da177e4SLinus Torvalds  *	@info:		agp_kern_info pointer.  The caller should insure that this pointer is valid.
3731da177e4SLinus Torvalds  *
3741da177e4SLinus Torvalds  *	This function copies information about the agp bridge device and the state of
3751da177e4SLinus Torvalds  *	the agp backend into an agp_kern_info pointer.
3761da177e4SLinus Torvalds  */
3771da177e4SLinus Torvalds int agp_copy_info(struct agp_bridge_data *bridge, struct agp_kern_info *info)
3781da177e4SLinus Torvalds {
3791da177e4SLinus Torvalds 	memset(info, 0, sizeof(struct agp_kern_info));
3801da177e4SLinus Torvalds 	if (!bridge) {
3811da177e4SLinus Torvalds 		info->chipset = NOT_SUPPORTED;
3821da177e4SLinus Torvalds 		return -EIO;
3831da177e4SLinus Torvalds 	}
3841da177e4SLinus Torvalds 
3851da177e4SLinus Torvalds 	info->version.major = bridge->version->major;
3861da177e4SLinus Torvalds 	info->version.minor = bridge->version->minor;
3871da177e4SLinus Torvalds 	info->chipset = SUPPORTED;
3881da177e4SLinus Torvalds 	info->device = bridge->dev;
38966bb8bf8SDavid Mosberger 	if (bridge->mode & AGPSTAT_MODE_3_0)
3901da177e4SLinus Torvalds 		info->mode = bridge->mode & ~AGP3_RESERVED_MASK;
3911da177e4SLinus Torvalds 	else
3921da177e4SLinus Torvalds 		info->mode = bridge->mode & ~AGP2_RESERVED_MASK;
3931da177e4SLinus Torvalds 	info->aper_base = bridge->gart_bus_addr;
3941da177e4SLinus Torvalds 	info->aper_size = agp_return_size();
3951da177e4SLinus Torvalds 	info->max_memory = bridge->max_memory_agp;
3961da177e4SLinus Torvalds 	info->current_memory = atomic_read(&bridge->current_memory_agp);
3971da177e4SLinus Torvalds 	info->cant_use_aperture = bridge->driver->cant_use_aperture;
3981da177e4SLinus Torvalds 	info->vm_ops = bridge->vm_ops;
3991da177e4SLinus Torvalds 	info->page_mask = ~0UL;
4001da177e4SLinus Torvalds 	return 0;
4011da177e4SLinus Torvalds }
4021da177e4SLinus Torvalds EXPORT_SYMBOL(agp_copy_info);
4031da177e4SLinus Torvalds 
4041da177e4SLinus Torvalds /* End - Routine to copy over information structure */
4051da177e4SLinus Torvalds 
4061da177e4SLinus Torvalds /*
4071da177e4SLinus Torvalds  * Routines for handling swapping of agp_memory into the GATT -
4081da177e4SLinus Torvalds  * These routines take agp_memory and insert them into the GATT.
4091da177e4SLinus Torvalds  * They call device specific routines to actually write to the GATT.
4101da177e4SLinus Torvalds  */
4111da177e4SLinus Torvalds 
4121da177e4SLinus Torvalds /**
4131da177e4SLinus Torvalds  *	agp_bind_memory  -  Bind an agp_memory structure into the GATT.
4141da177e4SLinus Torvalds  *
4151da177e4SLinus Torvalds  *	@curr:		agp_memory pointer
4161da177e4SLinus Torvalds  *	@pg_start:	an offset into the graphics aperture translation table
4171da177e4SLinus Torvalds  *
4181da177e4SLinus Torvalds  *	It returns -EINVAL if the pointer == NULL.
4191da177e4SLinus Torvalds  *	It returns -EBUSY if the area of the table requested is already in use.
4201da177e4SLinus Torvalds  */
4211da177e4SLinus Torvalds int agp_bind_memory(struct agp_memory *curr, off_t pg_start)
4221da177e4SLinus Torvalds {
4231da177e4SLinus Torvalds 	int ret_val;
4241da177e4SLinus Torvalds 
4251da177e4SLinus Torvalds 	if (curr == NULL)
4261da177e4SLinus Torvalds 		return -EINVAL;
4271da177e4SLinus Torvalds 
4281da177e4SLinus Torvalds 	if (curr->is_bound == TRUE) {
4291da177e4SLinus Torvalds 		printk(KERN_INFO PFX "memory %p is already bound!\n", curr);
4301da177e4SLinus Torvalds 		return -EINVAL;
4311da177e4SLinus Torvalds 	}
4321da177e4SLinus Torvalds 	if (curr->is_flushed == FALSE) {
4331da177e4SLinus Torvalds 		curr->bridge->driver->cache_flush();
4341da177e4SLinus Torvalds 		curr->is_flushed = TRUE;
4351da177e4SLinus Torvalds 	}
4361da177e4SLinus Torvalds 	ret_val = curr->bridge->driver->insert_memory(curr, pg_start, curr->type);
4371da177e4SLinus Torvalds 
4381da177e4SLinus Torvalds 	if (ret_val != 0)
4391da177e4SLinus Torvalds 		return ret_val;
4401da177e4SLinus Torvalds 
4411da177e4SLinus Torvalds 	curr->is_bound = TRUE;
4421da177e4SLinus Torvalds 	curr->pg_start = pg_start;
4431da177e4SLinus Torvalds 	return 0;
4441da177e4SLinus Torvalds }
4451da177e4SLinus Torvalds EXPORT_SYMBOL(agp_bind_memory);
4461da177e4SLinus Torvalds 
4471da177e4SLinus Torvalds 
4481da177e4SLinus Torvalds /**
4491da177e4SLinus Torvalds  *	agp_unbind_memory  -  Removes an agp_memory structure from the GATT
4501da177e4SLinus Torvalds  *
4511da177e4SLinus Torvalds  * @curr:	agp_memory pointer to be removed from the GATT.
4521da177e4SLinus Torvalds  *
4531da177e4SLinus Torvalds  * It returns -EINVAL if this piece of agp_memory is not currently bound to
4541da177e4SLinus Torvalds  * the graphics aperture translation table or if the agp_memory pointer == NULL
4551da177e4SLinus Torvalds  */
4561da177e4SLinus Torvalds int agp_unbind_memory(struct agp_memory *curr)
4571da177e4SLinus Torvalds {
4581da177e4SLinus Torvalds 	int ret_val;
4591da177e4SLinus Torvalds 
4601da177e4SLinus Torvalds 	if (curr == NULL)
4611da177e4SLinus Torvalds 		return -EINVAL;
4621da177e4SLinus Torvalds 
4631da177e4SLinus Torvalds 	if (curr->is_bound != TRUE) {
4641da177e4SLinus Torvalds 		printk(KERN_INFO PFX "memory %p was not bound!\n", curr);
4651da177e4SLinus Torvalds 		return -EINVAL;
4661da177e4SLinus Torvalds 	}
4671da177e4SLinus Torvalds 
4681da177e4SLinus Torvalds 	ret_val = curr->bridge->driver->remove_memory(curr, curr->pg_start, curr->type);
4691da177e4SLinus Torvalds 
4701da177e4SLinus Torvalds 	if (ret_val != 0)
4711da177e4SLinus Torvalds 		return ret_val;
4721da177e4SLinus Torvalds 
4731da177e4SLinus Torvalds 	curr->is_bound = FALSE;
4741da177e4SLinus Torvalds 	curr->pg_start = 0;
4751da177e4SLinus Torvalds 	return 0;
4761da177e4SLinus Torvalds }
4771da177e4SLinus Torvalds EXPORT_SYMBOL(agp_unbind_memory);
4781da177e4SLinus Torvalds 
4791da177e4SLinus Torvalds /* End - Routines for handling swapping of agp_memory into the GATT */
4801da177e4SLinus Torvalds 
4811da177e4SLinus Torvalds 
4821da177e4SLinus Torvalds /* Generic Agp routines - Start */
4831da177e4SLinus Torvalds static void agp_v2_parse_one(u32 *requested_mode, u32 *bridge_agpstat, u32 *vga_agpstat)
4841da177e4SLinus Torvalds {
4851da177e4SLinus Torvalds 	u32 tmp;
4861da177e4SLinus Torvalds 
4871da177e4SLinus Torvalds 	if (*requested_mode & AGP2_RESERVED_MASK) {
488c4dd4582SDave Jones 		printk(KERN_INFO PFX "reserved bits set (%x) in mode 0x%x. Fixed.\n",
489c4dd4582SDave Jones 			*requested_mode & AGP2_RESERVED_MASK, *requested_mode);
4901da177e4SLinus Torvalds 		*requested_mode &= ~AGP2_RESERVED_MASK;
4911da177e4SLinus Torvalds 	}
4921da177e4SLinus Torvalds 
49328af24bbSDave Jones 	/*
49428af24bbSDave Jones 	 * Some dumb bridges are programmed to disobey the AGP2 spec.
49528af24bbSDave Jones 	 * This is likely a BIOS misprogramming rather than poweron default, or
49628af24bbSDave Jones 	 * it would be a lot more common.
49728af24bbSDave Jones 	 * https://bugs.freedesktop.org/show_bug.cgi?id=8816
49828af24bbSDave Jones 	 * AGPv2 spec 6.1.9 states:
49928af24bbSDave Jones 	 *   The RATE field indicates the data transfer rates supported by this
50028af24bbSDave Jones 	 *   device. A.G.P. devices must report all that apply.
50128af24bbSDave Jones 	 * Fix them up as best we can.
50228af24bbSDave Jones 	 */
50328af24bbSDave Jones 	switch (*bridge_agpstat & 7) {
50428af24bbSDave Jones 	case 4:
50528af24bbSDave Jones 		*bridge_agpstat |= (AGPSTAT2_2X | AGPSTAT2_1X);
50628af24bbSDave Jones 		printk(KERN_INFO PFX "BIOS bug. AGP bridge claims to only support x4 rate"
50728af24bbSDave Jones 			"Fixing up support for x2 & x1\n");
50828af24bbSDave Jones 		break;
50928af24bbSDave Jones 	case 2:
51028af24bbSDave Jones 		*bridge_agpstat |= AGPSTAT2_1X;
51128af24bbSDave Jones 		printk(KERN_INFO PFX "BIOS bug. AGP bridge claims to only support x2 rate"
51228af24bbSDave Jones 			"Fixing up support for x1\n");
51328af24bbSDave Jones 		break;
51428af24bbSDave Jones 	default:
51528af24bbSDave Jones 		break;
51628af24bbSDave Jones 	}
51728af24bbSDave Jones 
5181da177e4SLinus Torvalds 	/* Check the speed bits make sense. Only one should be set. */
5191da177e4SLinus Torvalds 	tmp = *requested_mode & 7;
5201da177e4SLinus Torvalds 	switch (tmp) {
5211da177e4SLinus Torvalds 		case 0:
5221da177e4SLinus Torvalds 			printk(KERN_INFO PFX "%s tried to set rate=x0. Setting to x1 mode.\n", current->comm);
5231da177e4SLinus Torvalds 			*requested_mode |= AGPSTAT2_1X;
5241da177e4SLinus Torvalds 			break;
5251da177e4SLinus Torvalds 		case 1:
5261da177e4SLinus Torvalds 		case 2:
5271da177e4SLinus Torvalds 			break;
5281da177e4SLinus Torvalds 		case 3:
5291da177e4SLinus Torvalds 			*requested_mode &= ~(AGPSTAT2_1X);	/* rate=2 */
5301da177e4SLinus Torvalds 			break;
5311da177e4SLinus Torvalds 		case 4:
5321da177e4SLinus Torvalds 			break;
5331da177e4SLinus Torvalds 		case 5:
5341da177e4SLinus Torvalds 		case 6:
5351da177e4SLinus Torvalds 		case 7:
5361da177e4SLinus Torvalds 			*requested_mode &= ~(AGPSTAT2_1X|AGPSTAT2_2X); /* rate=4*/
5371da177e4SLinus Torvalds 			break;
5381da177e4SLinus Torvalds 	}
5391da177e4SLinus Torvalds 
5401da177e4SLinus Torvalds 	/* disable SBA if it's not supported */
5411da177e4SLinus Torvalds 	if (!((*bridge_agpstat & AGPSTAT_SBA) && (*vga_agpstat & AGPSTAT_SBA) && (*requested_mode & AGPSTAT_SBA)))
5421da177e4SLinus Torvalds 		*bridge_agpstat &= ~AGPSTAT_SBA;
5431da177e4SLinus Torvalds 
5441da177e4SLinus Torvalds 	/* Set rate */
5451da177e4SLinus Torvalds 	if (!((*bridge_agpstat & AGPSTAT2_4X) && (*vga_agpstat & AGPSTAT2_4X) && (*requested_mode & AGPSTAT2_4X)))
5461da177e4SLinus Torvalds 		*bridge_agpstat &= ~AGPSTAT2_4X;
5471da177e4SLinus Torvalds 
5481da177e4SLinus Torvalds 	if (!((*bridge_agpstat & AGPSTAT2_2X) && (*vga_agpstat & AGPSTAT2_2X) && (*requested_mode & AGPSTAT2_2X)))
5491da177e4SLinus Torvalds 		*bridge_agpstat &= ~AGPSTAT2_2X;
5501da177e4SLinus Torvalds 
5511da177e4SLinus Torvalds 	if (!((*bridge_agpstat & AGPSTAT2_1X) && (*vga_agpstat & AGPSTAT2_1X) && (*requested_mode & AGPSTAT2_1X)))
5521da177e4SLinus Torvalds 		*bridge_agpstat &= ~AGPSTAT2_1X;
5531da177e4SLinus Torvalds 
5541da177e4SLinus Torvalds 	/* Now we know what mode it should be, clear out the unwanted bits. */
5551da177e4SLinus Torvalds 	if (*bridge_agpstat & AGPSTAT2_4X)
5561da177e4SLinus Torvalds 		*bridge_agpstat &= ~(AGPSTAT2_1X | AGPSTAT2_2X);	/* 4X */
5571da177e4SLinus Torvalds 
5581da177e4SLinus Torvalds 	if (*bridge_agpstat & AGPSTAT2_2X)
5591da177e4SLinus Torvalds 		*bridge_agpstat &= ~(AGPSTAT2_1X | AGPSTAT2_4X);	/* 2X */
5601da177e4SLinus Torvalds 
5611da177e4SLinus Torvalds 	if (*bridge_agpstat & AGPSTAT2_1X)
5621da177e4SLinus Torvalds 		*bridge_agpstat &= ~(AGPSTAT2_2X | AGPSTAT2_4X);	/* 1X */
5631da177e4SLinus Torvalds 
5641da177e4SLinus Torvalds 	/* Apply any errata. */
5651da177e4SLinus Torvalds 	if (agp_bridge->flags & AGP_ERRATA_FASTWRITES)
5661da177e4SLinus Torvalds 		*bridge_agpstat &= ~AGPSTAT_FW;
5671da177e4SLinus Torvalds 
5681da177e4SLinus Torvalds 	if (agp_bridge->flags & AGP_ERRATA_SBA)
5691da177e4SLinus Torvalds 		*bridge_agpstat &= ~AGPSTAT_SBA;
5701da177e4SLinus Torvalds 
5711da177e4SLinus Torvalds 	if (agp_bridge->flags & AGP_ERRATA_1X) {
5721da177e4SLinus Torvalds 		*bridge_agpstat &= ~(AGPSTAT2_2X | AGPSTAT2_4X);
5731da177e4SLinus Torvalds 		*bridge_agpstat |= AGPSTAT2_1X;
5741da177e4SLinus Torvalds 	}
5751da177e4SLinus Torvalds 
5761da177e4SLinus Torvalds 	/* If we've dropped down to 1X, disable fast writes. */
5771da177e4SLinus Torvalds 	if (*bridge_agpstat & AGPSTAT2_1X)
5781da177e4SLinus Torvalds 		*bridge_agpstat &= ~AGPSTAT_FW;
5791da177e4SLinus Torvalds }
5801da177e4SLinus Torvalds 
5811da177e4SLinus Torvalds /*
5821da177e4SLinus Torvalds  * requested_mode = Mode requested by (typically) X.
5831da177e4SLinus Torvalds  * bridge_agpstat = PCI_AGP_STATUS from agp bridge.
5841da177e4SLinus Torvalds  * vga_agpstat = PCI_AGP_STATUS from graphic card.
5851da177e4SLinus Torvalds  */
5861da177e4SLinus Torvalds static void agp_v3_parse_one(u32 *requested_mode, u32 *bridge_agpstat, u32 *vga_agpstat)
5871da177e4SLinus Torvalds {
5881da177e4SLinus Torvalds 	u32 origbridge=*bridge_agpstat, origvga=*vga_agpstat;
5891da177e4SLinus Torvalds 	u32 tmp;
5901da177e4SLinus Torvalds 
5911da177e4SLinus Torvalds 	if (*requested_mode & AGP3_RESERVED_MASK) {
592c4dd4582SDave Jones 		printk(KERN_INFO PFX "reserved bits set (%x) in mode 0x%x. Fixed.\n",
593c4dd4582SDave Jones 			*requested_mode & AGP3_RESERVED_MASK, *requested_mode);
5941da177e4SLinus Torvalds 		*requested_mode &= ~AGP3_RESERVED_MASK;
5951da177e4SLinus Torvalds 	}
5961da177e4SLinus Torvalds 
5971da177e4SLinus Torvalds 	/* Check the speed bits make sense. */
5981da177e4SLinus Torvalds 	tmp = *requested_mode & 7;
5991da177e4SLinus Torvalds 	if (tmp == 0) {
6001da177e4SLinus Torvalds 		printk(KERN_INFO PFX "%s tried to set rate=x0. Setting to AGP3 x4 mode.\n", current->comm);
6011da177e4SLinus Torvalds 		*requested_mode |= AGPSTAT3_4X;
6021da177e4SLinus Torvalds 	}
6031da177e4SLinus Torvalds 	if (tmp >= 3) {
6041da177e4SLinus Torvalds 		printk(KERN_INFO PFX "%s tried to set rate=x%d. Setting to AGP3 x8 mode.\n", current->comm, tmp * 4);
6051da177e4SLinus Torvalds 		*requested_mode = (*requested_mode & ~7) | AGPSTAT3_8X;
6061da177e4SLinus Torvalds 	}
6071da177e4SLinus Torvalds 
6081da177e4SLinus Torvalds 	/* ARQSZ - Set the value to the maximum one.
6091da177e4SLinus Torvalds 	 * Don't allow the mode register to override values. */
6101da177e4SLinus Torvalds 	*bridge_agpstat = ((*bridge_agpstat & ~AGPSTAT_ARQSZ) |
6111da177e4SLinus Torvalds 		max_t(u32,(*bridge_agpstat & AGPSTAT_ARQSZ),(*vga_agpstat & AGPSTAT_ARQSZ)));
6121da177e4SLinus Torvalds 
6131da177e4SLinus Torvalds 	/* Calibration cycle.
6141da177e4SLinus Torvalds 	 * Don't allow the mode register to override values. */
6151da177e4SLinus Torvalds 	*bridge_agpstat = ((*bridge_agpstat & ~AGPSTAT_CAL_MASK) |
6161da177e4SLinus Torvalds 		min_t(u32,(*bridge_agpstat & AGPSTAT_CAL_MASK),(*vga_agpstat & AGPSTAT_CAL_MASK)));
6171da177e4SLinus Torvalds 
6181da177e4SLinus Torvalds 	/* SBA *must* be supported for AGP v3 */
6191da177e4SLinus Torvalds 	*bridge_agpstat |= AGPSTAT_SBA;
6201da177e4SLinus Torvalds 
6211da177e4SLinus Torvalds 	/*
6221da177e4SLinus Torvalds 	 * Set speed.
6231da177e4SLinus Torvalds 	 * Check for invalid speeds. This can happen when applications
6241da177e4SLinus Torvalds 	 * written before the AGP 3.0 standard pass AGP2.x modes to AGP3 hardware
6251da177e4SLinus Torvalds 	 */
6261da177e4SLinus Torvalds 	if (*requested_mode & AGPSTAT_MODE_3_0) {
6271da177e4SLinus Torvalds 		/*
6281da177e4SLinus Torvalds 		 * Caller hasn't a clue what it is doing. Bridge is in 3.0 mode,
6291da177e4SLinus Torvalds 		 * have been passed a 3.0 mode, but with 2.x speed bits set.
6301da177e4SLinus Torvalds 		 * AGP2.x 4x -> AGP3.0 4x.
6311da177e4SLinus Torvalds 		 */
6321da177e4SLinus Torvalds 		if (*requested_mode & AGPSTAT2_4X) {
6331da177e4SLinus Torvalds 			printk(KERN_INFO PFX "%s passes broken AGP3 flags (%x). Fixed.\n",
6341da177e4SLinus Torvalds 						current->comm, *requested_mode);
6351da177e4SLinus Torvalds 			*requested_mode &= ~AGPSTAT2_4X;
6361da177e4SLinus Torvalds 			*requested_mode |= AGPSTAT3_4X;
6371da177e4SLinus Torvalds 		}
6381da177e4SLinus Torvalds 	} else {
6391da177e4SLinus Torvalds 		/*
6401da177e4SLinus Torvalds 		 * The caller doesn't know what they are doing. We are in 3.0 mode,
6411da177e4SLinus Torvalds 		 * but have been passed an AGP 2.x mode.
6421da177e4SLinus Torvalds 		 * Convert AGP 1x,2x,4x -> AGP 3.0 4x.
6431da177e4SLinus Torvalds 		 */
6441da177e4SLinus Torvalds 		printk(KERN_INFO PFX "%s passes broken AGP2 flags (%x) in AGP3 mode. Fixed.\n",
6451da177e4SLinus Torvalds 					current->comm, *requested_mode);
6461da177e4SLinus Torvalds 		*requested_mode &= ~(AGPSTAT2_4X | AGPSTAT2_2X | AGPSTAT2_1X);
6471da177e4SLinus Torvalds 		*requested_mode |= AGPSTAT3_4X;
6481da177e4SLinus Torvalds 	}
6491da177e4SLinus Torvalds 
6501da177e4SLinus Torvalds 	if (*requested_mode & AGPSTAT3_8X) {
6511da177e4SLinus Torvalds 		if (!(*bridge_agpstat & AGPSTAT3_8X)) {
6521da177e4SLinus Torvalds 			*bridge_agpstat &= ~(AGPSTAT3_8X | AGPSTAT3_RSVD);
6531da177e4SLinus Torvalds 			*bridge_agpstat |= AGPSTAT3_4X;
6548c8b8385SDave Jones 			printk(KERN_INFO PFX "%s requested AGPx8 but bridge not capable.\n", current->comm);
6551da177e4SLinus Torvalds 			return;
6561da177e4SLinus Torvalds 		}
6571da177e4SLinus Torvalds 		if (!(*vga_agpstat & AGPSTAT3_8X)) {
6581da177e4SLinus Torvalds 			*bridge_agpstat &= ~(AGPSTAT3_8X | AGPSTAT3_RSVD);
6591da177e4SLinus Torvalds 			*bridge_agpstat |= AGPSTAT3_4X;
6608c8b8385SDave Jones 			printk(KERN_INFO PFX "%s requested AGPx8 but graphic card not capable.\n", current->comm);
6611da177e4SLinus Torvalds 			return;
6621da177e4SLinus Torvalds 		}
6631da177e4SLinus Torvalds 		/* All set, bridge & device can do AGP x8*/
6641da177e4SLinus Torvalds 		*bridge_agpstat &= ~(AGPSTAT3_4X | AGPSTAT3_RSVD);
6651da177e4SLinus Torvalds 		goto done;
6661da177e4SLinus Torvalds 
667edf03fb0SDave Jones 	} else if (*requested_mode & AGPSTAT3_4X) {
668edf03fb0SDave Jones 		*bridge_agpstat &= ~(AGPSTAT3_8X | AGPSTAT3_RSVD);
669edf03fb0SDave Jones 		*bridge_agpstat |= AGPSTAT3_4X;
670edf03fb0SDave Jones 		goto done;
671edf03fb0SDave Jones 
6721da177e4SLinus Torvalds 	} else {
6731da177e4SLinus Torvalds 
6741da177e4SLinus Torvalds 		/*
675edf03fb0SDave Jones 		 * If we didn't specify an AGP mode, we see if both
676edf03fb0SDave Jones 		 * the graphics card, and the bridge can do x8, and use if so.
677edf03fb0SDave Jones 		 * If not, we fall back to x4 mode.
6781da177e4SLinus Torvalds 		 */
679edf03fb0SDave Jones 		if ((*bridge_agpstat & AGPSTAT3_8X) && (*vga_agpstat & AGPSTAT3_8X)) {
6802cc1a413SDave Jones 			printk(KERN_INFO PFX "No AGP mode specified. Setting to highest mode "
6812cc1a413SDave Jones 				"supported by bridge & card (x8).\n");
682edf03fb0SDave Jones 			*bridge_agpstat &= ~(AGPSTAT3_4X | AGPSTAT3_RSVD);
683edf03fb0SDave Jones 			*vga_agpstat &= ~(AGPSTAT3_4X | AGPSTAT3_RSVD);
684edf03fb0SDave Jones 		} else {
685edf03fb0SDave Jones 			printk(KERN_INFO PFX "Fell back to AGPx4 mode because");
686edf03fb0SDave Jones 			if (!(*bridge_agpstat & AGPSTAT3_8X)) {
6872cc1a413SDave Jones 				printk(KERN_INFO PFX "bridge couldn't do x8. bridge_agpstat:%x (orig=%x)\n",
6882cc1a413SDave Jones 					*bridge_agpstat, origbridge);
6891da177e4SLinus Torvalds 				*bridge_agpstat &= ~(AGPSTAT3_8X | AGPSTAT3_RSVD);
6901da177e4SLinus Torvalds 				*bridge_agpstat |= AGPSTAT3_4X;
691edf03fb0SDave Jones 			}
692edf03fb0SDave Jones 			if (!(*vga_agpstat & AGPSTAT3_8X)) {
6932cc1a413SDave Jones 				printk(KERN_INFO PFX "graphics card couldn't do x8. vga_agpstat:%x (orig=%x)\n",
6942cc1a413SDave Jones 					*vga_agpstat, origvga);
695edf03fb0SDave Jones 				*vga_agpstat &= ~(AGPSTAT3_8X | AGPSTAT3_RSVD);
696edf03fb0SDave Jones 				*vga_agpstat |= AGPSTAT3_4X;
697edf03fb0SDave Jones 			}
6981da177e4SLinus Torvalds 		}
6991da177e4SLinus Torvalds 	}
7001da177e4SLinus Torvalds 
7011da177e4SLinus Torvalds done:
7021da177e4SLinus Torvalds 	/* Apply any errata. */
7031da177e4SLinus Torvalds 	if (agp_bridge->flags & AGP_ERRATA_FASTWRITES)
7041da177e4SLinus Torvalds 		*bridge_agpstat &= ~AGPSTAT_FW;
7051da177e4SLinus Torvalds 
7061da177e4SLinus Torvalds 	if (agp_bridge->flags & AGP_ERRATA_SBA)
7071da177e4SLinus Torvalds 		*bridge_agpstat &= ~AGPSTAT_SBA;
7081da177e4SLinus Torvalds 
7091da177e4SLinus Torvalds 	if (agp_bridge->flags & AGP_ERRATA_1X) {
7101da177e4SLinus Torvalds 		*bridge_agpstat &= ~(AGPSTAT2_2X | AGPSTAT2_4X);
7111da177e4SLinus Torvalds 		*bridge_agpstat |= AGPSTAT2_1X;
7121da177e4SLinus Torvalds 	}
7131da177e4SLinus Torvalds }
7141da177e4SLinus Torvalds 
7151da177e4SLinus Torvalds 
7161da177e4SLinus Torvalds /**
7171da177e4SLinus Torvalds  * agp_collect_device_status - determine correct agp_cmd from various agp_stat's
7181da177e4SLinus Torvalds  * @bridge: an agp_bridge_data struct allocated for the AGP host bridge.
7191da177e4SLinus Torvalds  * @requested_mode: requested agp_stat from userspace (Typically from X)
7201da177e4SLinus Torvalds  * @bridge_agpstat: current agp_stat from AGP bridge.
7211da177e4SLinus Torvalds  *
7221da177e4SLinus Torvalds  * This function will hunt for an AGP graphics card, and try to match
7231da177e4SLinus Torvalds  * the requested mode to the capabilities of both the bridge and the card.
7241da177e4SLinus Torvalds  */
7251da177e4SLinus Torvalds u32 agp_collect_device_status(struct agp_bridge_data *bridge, u32 requested_mode, u32 bridge_agpstat)
7261da177e4SLinus Torvalds {
7271da177e4SLinus Torvalds 	struct pci_dev *device = NULL;
7281da177e4SLinus Torvalds 	u32 vga_agpstat;
7291da177e4SLinus Torvalds 	u8 cap_ptr;
7301da177e4SLinus Torvalds 
7311da177e4SLinus Torvalds 	for (;;) {
7321da177e4SLinus Torvalds 		device = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, device);
7331da177e4SLinus Torvalds 		if (!device) {
7341da177e4SLinus Torvalds 			printk(KERN_INFO PFX "Couldn't find an AGP VGA controller.\n");
7351da177e4SLinus Torvalds 			return 0;
7361da177e4SLinus Torvalds 		}
7371da177e4SLinus Torvalds 		cap_ptr = pci_find_capability(device, PCI_CAP_ID_AGP);
7381da177e4SLinus Torvalds 		if (cap_ptr)
7391da177e4SLinus Torvalds 			break;
7401da177e4SLinus Torvalds 	}
7411da177e4SLinus Torvalds 
7421da177e4SLinus Torvalds 	/*
7431da177e4SLinus Torvalds 	 * Ok, here we have a AGP device. Disable impossible
7441da177e4SLinus Torvalds 	 * settings, and adjust the readqueue to the minimum.
7451da177e4SLinus Torvalds 	 */
7461da177e4SLinus Torvalds 	pci_read_config_dword(device, cap_ptr+PCI_AGP_STATUS, &vga_agpstat);
7471da177e4SLinus Torvalds 
7481da177e4SLinus Torvalds 	/* adjust RQ depth */
7491da177e4SLinus Torvalds 	bridge_agpstat = ((bridge_agpstat & ~AGPSTAT_RQ_DEPTH) |
7501da177e4SLinus Torvalds 	     min_t(u32, (requested_mode & AGPSTAT_RQ_DEPTH),
7511da177e4SLinus Torvalds 		 min_t(u32, (bridge_agpstat & AGPSTAT_RQ_DEPTH), (vga_agpstat & AGPSTAT_RQ_DEPTH))));
7521da177e4SLinus Torvalds 
7531da177e4SLinus Torvalds 	/* disable FW if it's not supported */
7541da177e4SLinus Torvalds 	if (!((bridge_agpstat & AGPSTAT_FW) &&
7551da177e4SLinus Torvalds 		 (vga_agpstat & AGPSTAT_FW) &&
7561da177e4SLinus Torvalds 		 (requested_mode & AGPSTAT_FW)))
7571da177e4SLinus Torvalds 		bridge_agpstat &= ~AGPSTAT_FW;
7581da177e4SLinus Torvalds 
7591da177e4SLinus Torvalds 	/* Check to see if we are operating in 3.0 mode */
76066bb8bf8SDavid Mosberger 	if (agp_bridge->mode & AGPSTAT_MODE_3_0)
7611da177e4SLinus Torvalds 		agp_v3_parse_one(&requested_mode, &bridge_agpstat, &vga_agpstat);
7621da177e4SLinus Torvalds 	else
7631da177e4SLinus Torvalds 		agp_v2_parse_one(&requested_mode, &bridge_agpstat, &vga_agpstat);
7641da177e4SLinus Torvalds 
7651da177e4SLinus Torvalds 	pci_dev_put(device);
7661da177e4SLinus Torvalds 	return bridge_agpstat;
7671da177e4SLinus Torvalds }
7681da177e4SLinus Torvalds EXPORT_SYMBOL(agp_collect_device_status);
7691da177e4SLinus Torvalds 
7701da177e4SLinus Torvalds 
7711da177e4SLinus Torvalds void agp_device_command(u32 bridge_agpstat, int agp_v3)
7721da177e4SLinus Torvalds {
7731da177e4SLinus Torvalds 	struct pci_dev *device = NULL;
7741da177e4SLinus Torvalds 	int mode;
7751da177e4SLinus Torvalds 
7761da177e4SLinus Torvalds 	mode = bridge_agpstat & 0x7;
7771da177e4SLinus Torvalds 	if (agp_v3)
7781da177e4SLinus Torvalds 		mode *= 4;
7791da177e4SLinus Torvalds 
7801da177e4SLinus Torvalds 	for_each_pci_dev(device) {
7811da177e4SLinus Torvalds 		u8 agp = pci_find_capability(device, PCI_CAP_ID_AGP);
7821da177e4SLinus Torvalds 		if (!agp)
7831da177e4SLinus Torvalds 			continue;
7841da177e4SLinus Torvalds 
7851da177e4SLinus Torvalds 		printk(KERN_INFO PFX "Putting AGP V%d device at %s into %dx mode\n",
7861da177e4SLinus Torvalds 				agp_v3 ? 3 : 2, pci_name(device), mode);
7871da177e4SLinus Torvalds 		pci_write_config_dword(device, agp + PCI_AGP_COMMAND, bridge_agpstat);
7881da177e4SLinus Torvalds 	}
7891da177e4SLinus Torvalds }
7901da177e4SLinus Torvalds EXPORT_SYMBOL(agp_device_command);
7911da177e4SLinus Torvalds 
7921da177e4SLinus Torvalds 
7931da177e4SLinus Torvalds void get_agp_version(struct agp_bridge_data *bridge)
7941da177e4SLinus Torvalds {
7951da177e4SLinus Torvalds 	u32 ncapid;
7961da177e4SLinus Torvalds 
7971da177e4SLinus Torvalds 	/* Exit early if already set by errata workarounds. */
7981da177e4SLinus Torvalds 	if (bridge->major_version != 0)
7991da177e4SLinus Torvalds 		return;
8001da177e4SLinus Torvalds 
8011da177e4SLinus Torvalds 	pci_read_config_dword(bridge->dev, bridge->capndx, &ncapid);
8021da177e4SLinus Torvalds 	bridge->major_version = (ncapid >> AGP_MAJOR_VERSION_SHIFT) & 0xf;
8031da177e4SLinus Torvalds 	bridge->minor_version = (ncapid >> AGP_MINOR_VERSION_SHIFT) & 0xf;
8041da177e4SLinus Torvalds }
8051da177e4SLinus Torvalds EXPORT_SYMBOL(get_agp_version);
8061da177e4SLinus Torvalds 
8071da177e4SLinus Torvalds 
8081da177e4SLinus Torvalds void agp_generic_enable(struct agp_bridge_data *bridge, u32 requested_mode)
8091da177e4SLinus Torvalds {
8101da177e4SLinus Torvalds 	u32 bridge_agpstat, temp;
8111da177e4SLinus Torvalds 
8121da177e4SLinus Torvalds 	get_agp_version(agp_bridge);
8131da177e4SLinus Torvalds 
8141da177e4SLinus Torvalds 	printk(KERN_INFO PFX "Found an AGP %d.%d compliant device at %s.\n",
8151da177e4SLinus Torvalds 				agp_bridge->major_version,
8161da177e4SLinus Torvalds 				agp_bridge->minor_version,
8171da177e4SLinus Torvalds 				pci_name(agp_bridge->dev));
8181da177e4SLinus Torvalds 
8191da177e4SLinus Torvalds 	pci_read_config_dword(agp_bridge->dev,
8201da177e4SLinus Torvalds 		      agp_bridge->capndx + PCI_AGP_STATUS, &bridge_agpstat);
8211da177e4SLinus Torvalds 
8221da177e4SLinus Torvalds 	bridge_agpstat = agp_collect_device_status(agp_bridge, requested_mode, bridge_agpstat);
8231da177e4SLinus Torvalds 	if (bridge_agpstat == 0)
8241da177e4SLinus Torvalds 		/* Something bad happened. FIXME: Return error code? */
8251da177e4SLinus Torvalds 		return;
8261da177e4SLinus Torvalds 
8271da177e4SLinus Torvalds 	bridge_agpstat |= AGPSTAT_AGP_ENABLE;
8281da177e4SLinus Torvalds 
8291da177e4SLinus Torvalds 	/* Do AGP version specific frobbing. */
8301da177e4SLinus Torvalds 	if (bridge->major_version >= 3) {
83166bb8bf8SDavid Mosberger 		if (bridge->mode & AGPSTAT_MODE_3_0) {
8321da177e4SLinus Torvalds 			/* If we have 3.5, we can do the isoch stuff. */
8331da177e4SLinus Torvalds 			if (bridge->minor_version >= 5)
8341da177e4SLinus Torvalds 				agp_3_5_enable(bridge);
8351da177e4SLinus Torvalds 			agp_device_command(bridge_agpstat, TRUE);
8361da177e4SLinus Torvalds 			return;
8371da177e4SLinus Torvalds 		} else {
8381da177e4SLinus Torvalds 		    /* Disable calibration cycle in RX91<1> when not in AGP3.0 mode of operation.*/
8391da177e4SLinus Torvalds 		    bridge_agpstat &= ~(7<<10) ;
8401da177e4SLinus Torvalds 		    pci_read_config_dword(bridge->dev,
8411da177e4SLinus Torvalds 					bridge->capndx+AGPCTRL, &temp);
8421da177e4SLinus Torvalds 		    temp |= (1<<9);
8431da177e4SLinus Torvalds 		    pci_write_config_dword(bridge->dev,
8441da177e4SLinus Torvalds 					bridge->capndx+AGPCTRL, temp);
8451da177e4SLinus Torvalds 
8461da177e4SLinus Torvalds 		    printk(KERN_INFO PFX "Device is in legacy mode,"
8471da177e4SLinus Torvalds 				" falling back to 2.x\n");
8481da177e4SLinus Torvalds 		}
8491da177e4SLinus Torvalds 	}
8501da177e4SLinus Torvalds 
8511da177e4SLinus Torvalds 	/* AGP v<3 */
8521da177e4SLinus Torvalds 	agp_device_command(bridge_agpstat, FALSE);
8531da177e4SLinus Torvalds }
8541da177e4SLinus Torvalds EXPORT_SYMBOL(agp_generic_enable);
8551da177e4SLinus Torvalds 
8561da177e4SLinus Torvalds 
8571da177e4SLinus Torvalds int agp_generic_create_gatt_table(struct agp_bridge_data *bridge)
8581da177e4SLinus Torvalds {
8591da177e4SLinus Torvalds 	char *table;
8601da177e4SLinus Torvalds 	char *table_end;
8611da177e4SLinus Torvalds 	int size;
8621da177e4SLinus Torvalds 	int page_order;
8631da177e4SLinus Torvalds 	int num_entries;
8641da177e4SLinus Torvalds 	int i;
8651da177e4SLinus Torvalds 	void *temp;
8661da177e4SLinus Torvalds 	struct page *page;
8671da177e4SLinus Torvalds 
8681da177e4SLinus Torvalds 	/* The generic routines can't handle 2 level gatt's */
8691da177e4SLinus Torvalds 	if (bridge->driver->size_type == LVL2_APER_SIZE)
8701da177e4SLinus Torvalds 		return -EINVAL;
8711da177e4SLinus Torvalds 
8721da177e4SLinus Torvalds 	table = NULL;
8731da177e4SLinus Torvalds 	i = bridge->aperture_size_idx;
8741da177e4SLinus Torvalds 	temp = bridge->current_size;
8751da177e4SLinus Torvalds 	size = page_order = num_entries = 0;
8761da177e4SLinus Torvalds 
8771da177e4SLinus Torvalds 	if (bridge->driver->size_type != FIXED_APER_SIZE) {
8781da177e4SLinus Torvalds 		do {
8791da177e4SLinus Torvalds 			switch (bridge->driver->size_type) {
8801da177e4SLinus Torvalds 			case U8_APER_SIZE:
8811da177e4SLinus Torvalds 				size = A_SIZE_8(temp)->size;
8821da177e4SLinus Torvalds 				page_order =
8831da177e4SLinus Torvalds 				    A_SIZE_8(temp)->page_order;
8841da177e4SLinus Torvalds 				num_entries =
8851da177e4SLinus Torvalds 				    A_SIZE_8(temp)->num_entries;
8861da177e4SLinus Torvalds 				break;
8871da177e4SLinus Torvalds 			case U16_APER_SIZE:
8881da177e4SLinus Torvalds 				size = A_SIZE_16(temp)->size;
8891da177e4SLinus Torvalds 				page_order = A_SIZE_16(temp)->page_order;
8901da177e4SLinus Torvalds 				num_entries = A_SIZE_16(temp)->num_entries;
8911da177e4SLinus Torvalds 				break;
8921da177e4SLinus Torvalds 			case U32_APER_SIZE:
8931da177e4SLinus Torvalds 				size = A_SIZE_32(temp)->size;
8941da177e4SLinus Torvalds 				page_order = A_SIZE_32(temp)->page_order;
8951da177e4SLinus Torvalds 				num_entries = A_SIZE_32(temp)->num_entries;
8961da177e4SLinus Torvalds 				break;
8971da177e4SLinus Torvalds 				/* This case will never really happen. */
8981da177e4SLinus Torvalds 			case FIXED_APER_SIZE:
8991da177e4SLinus Torvalds 			case LVL2_APER_SIZE:
9001da177e4SLinus Torvalds 			default:
9011da177e4SLinus Torvalds 				size = page_order = num_entries = 0;
9021da177e4SLinus Torvalds 				break;
9031da177e4SLinus Torvalds 			}
9041da177e4SLinus Torvalds 
90507eee78eSKeir Fraser 			table = alloc_gatt_pages(page_order);
9061da177e4SLinus Torvalds 
9071da177e4SLinus Torvalds 			if (table == NULL) {
9081da177e4SLinus Torvalds 				i++;
9091da177e4SLinus Torvalds 				switch (bridge->driver->size_type) {
9101da177e4SLinus Torvalds 				case U8_APER_SIZE:
9111da177e4SLinus Torvalds 					bridge->current_size = A_IDX8(bridge);
9121da177e4SLinus Torvalds 					break;
9131da177e4SLinus Torvalds 				case U16_APER_SIZE:
9141da177e4SLinus Torvalds 					bridge->current_size = A_IDX16(bridge);
9151da177e4SLinus Torvalds 					break;
9161da177e4SLinus Torvalds 				case U32_APER_SIZE:
9171da177e4SLinus Torvalds 					bridge->current_size = A_IDX32(bridge);
9181da177e4SLinus Torvalds 					break;
91989197e34SDave Jones 				/* These cases will never really happen. */
9201da177e4SLinus Torvalds 				case FIXED_APER_SIZE:
9211da177e4SLinus Torvalds 				case LVL2_APER_SIZE:
9221da177e4SLinus Torvalds 				default:
9231da177e4SLinus Torvalds 					break;
9241da177e4SLinus Torvalds 				}
9251da177e4SLinus Torvalds 				temp = bridge->current_size;
9261da177e4SLinus Torvalds 			} else {
9271da177e4SLinus Torvalds 				bridge->aperture_size_idx = i;
9281da177e4SLinus Torvalds 			}
9291da177e4SLinus Torvalds 		} while (!table && (i < bridge->driver->num_aperture_sizes));
9301da177e4SLinus Torvalds 	} else {
9311da177e4SLinus Torvalds 		size = ((struct aper_size_info_fixed *) temp)->size;
9321da177e4SLinus Torvalds 		page_order = ((struct aper_size_info_fixed *) temp)->page_order;
9331da177e4SLinus Torvalds 		num_entries = ((struct aper_size_info_fixed *) temp)->num_entries;
93407eee78eSKeir Fraser 		table = alloc_gatt_pages(page_order);
9351da177e4SLinus Torvalds 	}
9361da177e4SLinus Torvalds 
9371da177e4SLinus Torvalds 	if (table == NULL)
9381da177e4SLinus Torvalds 		return -ENOMEM;
9391da177e4SLinus Torvalds 
9401da177e4SLinus Torvalds 	table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1);
9411da177e4SLinus Torvalds 
9421da177e4SLinus Torvalds 	for (page = virt_to_page(table); page <= virt_to_page(table_end); page++)
9431da177e4SLinus Torvalds 		SetPageReserved(page);
9441da177e4SLinus Torvalds 
9451da177e4SLinus Torvalds 	bridge->gatt_table_real = (u32 *) table;
9461da177e4SLinus Torvalds 	agp_gatt_table = (void *)table;
9471da177e4SLinus Torvalds 
9481da177e4SLinus Torvalds 	bridge->driver->cache_flush();
94907eee78eSKeir Fraser 	bridge->gatt_table = ioremap_nocache(virt_to_gart(table),
9501da177e4SLinus Torvalds 					(PAGE_SIZE * (1 << page_order)));
9511da177e4SLinus Torvalds 	bridge->driver->cache_flush();
9521da177e4SLinus Torvalds 
9531da177e4SLinus Torvalds 	if (bridge->gatt_table == NULL) {
9541da177e4SLinus Torvalds 		for (page = virt_to_page(table); page <= virt_to_page(table_end); page++)
9551da177e4SLinus Torvalds 			ClearPageReserved(page);
9561da177e4SLinus Torvalds 
95707eee78eSKeir Fraser 		free_gatt_pages(table, page_order);
9581da177e4SLinus Torvalds 
9591da177e4SLinus Torvalds 		return -ENOMEM;
9601da177e4SLinus Torvalds 	}
96107eee78eSKeir Fraser 	bridge->gatt_bus_addr = virt_to_gart(bridge->gatt_table_real);
9621da177e4SLinus Torvalds 
9631da177e4SLinus Torvalds 	/* AK: bogus, should encode addresses > 4GB */
9641da177e4SLinus Torvalds 	for (i = 0; i < num_entries; i++) {
9651da177e4SLinus Torvalds 		writel(bridge->scratch_page, bridge->gatt_table+i);
9661da177e4SLinus Torvalds 		readl(bridge->gatt_table+i);	/* PCI Posting. */
9671da177e4SLinus Torvalds 	}
9681da177e4SLinus Torvalds 
9691da177e4SLinus Torvalds 	return 0;
9701da177e4SLinus Torvalds }
9711da177e4SLinus Torvalds EXPORT_SYMBOL(agp_generic_create_gatt_table);
9721da177e4SLinus Torvalds 
9731da177e4SLinus Torvalds int agp_generic_free_gatt_table(struct agp_bridge_data *bridge)
9741da177e4SLinus Torvalds {
9751da177e4SLinus Torvalds 	int page_order;
9761da177e4SLinus Torvalds 	char *table, *table_end;
9771da177e4SLinus Torvalds 	void *temp;
9781da177e4SLinus Torvalds 	struct page *page;
9791da177e4SLinus Torvalds 
9801da177e4SLinus Torvalds 	temp = bridge->current_size;
9811da177e4SLinus Torvalds 
9821da177e4SLinus Torvalds 	switch (bridge->driver->size_type) {
9831da177e4SLinus Torvalds 	case U8_APER_SIZE:
9841da177e4SLinus Torvalds 		page_order = A_SIZE_8(temp)->page_order;
9851da177e4SLinus Torvalds 		break;
9861da177e4SLinus Torvalds 	case U16_APER_SIZE:
9871da177e4SLinus Torvalds 		page_order = A_SIZE_16(temp)->page_order;
9881da177e4SLinus Torvalds 		break;
9891da177e4SLinus Torvalds 	case U32_APER_SIZE:
9901da177e4SLinus Torvalds 		page_order = A_SIZE_32(temp)->page_order;
9911da177e4SLinus Torvalds 		break;
9921da177e4SLinus Torvalds 	case FIXED_APER_SIZE:
9931da177e4SLinus Torvalds 		page_order = A_SIZE_FIX(temp)->page_order;
9941da177e4SLinus Torvalds 		break;
9951da177e4SLinus Torvalds 	case LVL2_APER_SIZE:
9961da177e4SLinus Torvalds 		/* The generic routines can't deal with 2 level gatt's */
9971da177e4SLinus Torvalds 		return -EINVAL;
9981da177e4SLinus Torvalds 		break;
9991da177e4SLinus Torvalds 	default:
10001da177e4SLinus Torvalds 		page_order = 0;
10011da177e4SLinus Torvalds 		break;
10021da177e4SLinus Torvalds 	}
10031da177e4SLinus Torvalds 
10041da177e4SLinus Torvalds 	/* Do not worry about freeing memory, because if this is
10051da177e4SLinus Torvalds 	 * called, then all agp memory is deallocated and removed
10061da177e4SLinus Torvalds 	 * from the table. */
10071da177e4SLinus Torvalds 
10081da177e4SLinus Torvalds 	iounmap(bridge->gatt_table);
10091da177e4SLinus Torvalds 	table = (char *) bridge->gatt_table_real;
10101da177e4SLinus Torvalds 	table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1);
10111da177e4SLinus Torvalds 
10121da177e4SLinus Torvalds 	for (page = virt_to_page(table); page <= virt_to_page(table_end); page++)
10131da177e4SLinus Torvalds 		ClearPageReserved(page);
10141da177e4SLinus Torvalds 
101507eee78eSKeir Fraser 	free_gatt_pages(bridge->gatt_table_real, page_order);
10161da177e4SLinus Torvalds 
10171da177e4SLinus Torvalds 	agp_gatt_table = NULL;
10181da177e4SLinus Torvalds 	bridge->gatt_table = NULL;
10191da177e4SLinus Torvalds 	bridge->gatt_table_real = NULL;
10201da177e4SLinus Torvalds 	bridge->gatt_bus_addr = 0;
10211da177e4SLinus Torvalds 
10221da177e4SLinus Torvalds 	return 0;
10231da177e4SLinus Torvalds }
10241da177e4SLinus Torvalds EXPORT_SYMBOL(agp_generic_free_gatt_table);
10251da177e4SLinus Torvalds 
10261da177e4SLinus Torvalds 
10271da177e4SLinus Torvalds int agp_generic_insert_memory(struct agp_memory * mem, off_t pg_start, int type)
10281da177e4SLinus Torvalds {
10291da177e4SLinus Torvalds 	int num_entries;
10301da177e4SLinus Torvalds 	size_t i;
10311da177e4SLinus Torvalds 	off_t j;
10321da177e4SLinus Torvalds 	void *temp;
10331da177e4SLinus Torvalds 	struct agp_bridge_data *bridge;
1034a030ce44SThomas Hellstrom 	int mask_type;
10351da177e4SLinus Torvalds 
10361da177e4SLinus Torvalds 	bridge = mem->bridge;
10371da177e4SLinus Torvalds 	if (!bridge)
10381da177e4SLinus Torvalds 		return -EINVAL;
10391da177e4SLinus Torvalds 
10405aa80c72SThomas Hellstrom 	if (mem->page_count == 0)
10415aa80c72SThomas Hellstrom 		return 0;
10425aa80c72SThomas Hellstrom 
10431da177e4SLinus Torvalds 	temp = bridge->current_size;
10441da177e4SLinus Torvalds 
10451da177e4SLinus Torvalds 	switch (bridge->driver->size_type) {
10461da177e4SLinus Torvalds 	case U8_APER_SIZE:
10471da177e4SLinus Torvalds 		num_entries = A_SIZE_8(temp)->num_entries;
10481da177e4SLinus Torvalds 		break;
10491da177e4SLinus Torvalds 	case U16_APER_SIZE:
10501da177e4SLinus Torvalds 		num_entries = A_SIZE_16(temp)->num_entries;
10511da177e4SLinus Torvalds 		break;
10521da177e4SLinus Torvalds 	case U32_APER_SIZE:
10531da177e4SLinus Torvalds 		num_entries = A_SIZE_32(temp)->num_entries;
10541da177e4SLinus Torvalds 		break;
10551da177e4SLinus Torvalds 	case FIXED_APER_SIZE:
10561da177e4SLinus Torvalds 		num_entries = A_SIZE_FIX(temp)->num_entries;
10571da177e4SLinus Torvalds 		break;
10581da177e4SLinus Torvalds 	case LVL2_APER_SIZE:
10591da177e4SLinus Torvalds 		/* The generic routines can't deal with 2 level gatt's */
10601da177e4SLinus Torvalds 		return -EINVAL;
10611da177e4SLinus Torvalds 		break;
10621da177e4SLinus Torvalds 	default:
10631da177e4SLinus Torvalds 		num_entries = 0;
10641da177e4SLinus Torvalds 		break;
10651da177e4SLinus Torvalds 	}
10661da177e4SLinus Torvalds 
10671da177e4SLinus Torvalds 	num_entries -= agp_memory_reserved/PAGE_SIZE;
10681da177e4SLinus Torvalds 	if (num_entries < 0) num_entries = 0;
10691da177e4SLinus Torvalds 
1070*1c14cfbbSAndrew Morton 	if (type != mem->type)
1071a030ce44SThomas Hellstrom 		return -EINVAL;
1072a030ce44SThomas Hellstrom 
1073a030ce44SThomas Hellstrom 	mask_type = bridge->driver->agp_type_to_mask_type(bridge, type);
1074a030ce44SThomas Hellstrom 	if (mask_type != 0) {
10751da177e4SLinus Torvalds 		/* The generic routines know nothing of memory types */
10761da177e4SLinus Torvalds 		return -EINVAL;
10771da177e4SLinus Torvalds 	}
10781da177e4SLinus Torvalds 
10791da177e4SLinus Torvalds 	/* AK: could wrap */
10801da177e4SLinus Torvalds 	if ((pg_start + mem->page_count) > num_entries)
10811da177e4SLinus Torvalds 		return -EINVAL;
10821da177e4SLinus Torvalds 
10831da177e4SLinus Torvalds 	j = pg_start;
10841da177e4SLinus Torvalds 
10851da177e4SLinus Torvalds 	while (j < (pg_start + mem->page_count)) {
10861da177e4SLinus Torvalds 		if (!PGE_EMPTY(bridge, readl(bridge->gatt_table+j)))
10871da177e4SLinus Torvalds 			return -EBUSY;
10881da177e4SLinus Torvalds 		j++;
10891da177e4SLinus Torvalds 	}
10901da177e4SLinus Torvalds 
10911da177e4SLinus Torvalds 	if (mem->is_flushed == FALSE) {
10921da177e4SLinus Torvalds 		bridge->driver->cache_flush();
10931da177e4SLinus Torvalds 		mem->is_flushed = TRUE;
10941da177e4SLinus Torvalds 	}
10951da177e4SLinus Torvalds 
10961da177e4SLinus Torvalds 	for (i = 0, j = pg_start; i < mem->page_count; i++, j++) {
1097a030ce44SThomas Hellstrom 		writel(bridge->driver->mask_memory(bridge, mem->memory[i], mask_type),
1098a030ce44SThomas Hellstrom 		       bridge->gatt_table+j);
10991da177e4SLinus Torvalds 	}
11005aa80c72SThomas Hellstrom 	readl(bridge->gatt_table+j-1);	/* PCI Posting. */
11011da177e4SLinus Torvalds 
11021da177e4SLinus Torvalds 	bridge->driver->tlb_flush(mem);
11031da177e4SLinus Torvalds 	return 0;
11041da177e4SLinus Torvalds }
11051da177e4SLinus Torvalds EXPORT_SYMBOL(agp_generic_insert_memory);
11061da177e4SLinus Torvalds 
11071da177e4SLinus Torvalds 
11081da177e4SLinus Torvalds int agp_generic_remove_memory(struct agp_memory *mem, off_t pg_start, int type)
11091da177e4SLinus Torvalds {
11101da177e4SLinus Torvalds 	size_t i;
11111da177e4SLinus Torvalds 	struct agp_bridge_data *bridge;
1112a030ce44SThomas Hellstrom 	int mask_type;
11131da177e4SLinus Torvalds 
11141da177e4SLinus Torvalds 	bridge = mem->bridge;
11151da177e4SLinus Torvalds 	if (!bridge)
11161da177e4SLinus Torvalds 		return -EINVAL;
11171da177e4SLinus Torvalds 
11185aa80c72SThomas Hellstrom 	if (mem->page_count == 0)
11195aa80c72SThomas Hellstrom 		return 0;
11205aa80c72SThomas Hellstrom 
1121a030ce44SThomas Hellstrom 	if (type != mem->type)
1122a030ce44SThomas Hellstrom 		return -EINVAL;
1123a030ce44SThomas Hellstrom 
1124a030ce44SThomas Hellstrom 	mask_type = bridge->driver->agp_type_to_mask_type(bridge, type);
1125a030ce44SThomas Hellstrom 	if (mask_type != 0) {
11261da177e4SLinus Torvalds 		/* The generic routines know nothing of memory types */
11271da177e4SLinus Torvalds 		return -EINVAL;
11281da177e4SLinus Torvalds 	}
11291da177e4SLinus Torvalds 
11301da177e4SLinus Torvalds 	/* AK: bogus, should encode addresses > 4GB */
11311da177e4SLinus Torvalds 	for (i = pg_start; i < (mem->page_count + pg_start); i++) {
11321da177e4SLinus Torvalds 		writel(bridge->scratch_page, bridge->gatt_table+i);
11331da177e4SLinus Torvalds 	}
11345aa80c72SThomas Hellstrom 	readl(bridge->gatt_table+i-1);	/* PCI Posting. */
11351da177e4SLinus Torvalds 
11361da177e4SLinus Torvalds 	bridge->driver->tlb_flush(mem);
11371da177e4SLinus Torvalds 	return 0;
11381da177e4SLinus Torvalds }
11391da177e4SLinus Torvalds EXPORT_SYMBOL(agp_generic_remove_memory);
11401da177e4SLinus Torvalds 
11411da177e4SLinus Torvalds struct agp_memory *agp_generic_alloc_by_type(size_t page_count, int type)
11421da177e4SLinus Torvalds {
11431da177e4SLinus Torvalds 	return NULL;
11441da177e4SLinus Torvalds }
11451da177e4SLinus Torvalds EXPORT_SYMBOL(agp_generic_alloc_by_type);
11461da177e4SLinus Torvalds 
11471da177e4SLinus Torvalds void agp_generic_free_by_type(struct agp_memory *curr)
11481da177e4SLinus Torvalds {
1149a030ce44SThomas Hellstrom 	agp_free_page_array(curr);
11501da177e4SLinus Torvalds 	agp_free_key(curr->key);
11511da177e4SLinus Torvalds 	kfree(curr);
11521da177e4SLinus Torvalds }
11531da177e4SLinus Torvalds EXPORT_SYMBOL(agp_generic_free_by_type);
11541da177e4SLinus Torvalds 
1155a030ce44SThomas Hellstrom struct agp_memory *agp_generic_alloc_user(size_t page_count, int type)
1156a030ce44SThomas Hellstrom {
1157a030ce44SThomas Hellstrom 	struct agp_memory *new;
1158a030ce44SThomas Hellstrom 	int i;
1159a030ce44SThomas Hellstrom 	int pages;
1160a030ce44SThomas Hellstrom 
1161a030ce44SThomas Hellstrom 	pages = (page_count + ENTRIES_PER_PAGE - 1) / ENTRIES_PER_PAGE;
1162a030ce44SThomas Hellstrom 	new = agp_create_user_memory(page_count);
1163a030ce44SThomas Hellstrom 	if (new == NULL)
1164a030ce44SThomas Hellstrom 		return NULL;
1165a030ce44SThomas Hellstrom 
1166*1c14cfbbSAndrew Morton 	for (i = 0; i < page_count; i++)
1167a030ce44SThomas Hellstrom 		new->memory[i] = 0;
1168a030ce44SThomas Hellstrom 	new->page_count = 0;
1169a030ce44SThomas Hellstrom 	new->type = type;
1170a030ce44SThomas Hellstrom 	new->num_scratch_pages = pages;
1171a030ce44SThomas Hellstrom 
1172a030ce44SThomas Hellstrom 	return new;
1173a030ce44SThomas Hellstrom }
1174a030ce44SThomas Hellstrom EXPORT_SYMBOL(agp_generic_alloc_user);
1175a030ce44SThomas Hellstrom 
11761da177e4SLinus Torvalds /*
11771da177e4SLinus Torvalds  * Basic Page Allocation Routines -
11781da177e4SLinus Torvalds  * These routines handle page allocation and by default they reserve the allocated
11791da177e4SLinus Torvalds  * memory.  They also handle incrementing the current_memory_agp value, Which is checked
11801da177e4SLinus Torvalds  * against a maximum value.
11811da177e4SLinus Torvalds  */
11821da177e4SLinus Torvalds 
11831da177e4SLinus Torvalds void *agp_generic_alloc_page(struct agp_bridge_data *bridge)
11841da177e4SLinus Torvalds {
11851da177e4SLinus Torvalds 	struct page * page;
11861da177e4SLinus Torvalds 
118766c669baSLinus Torvalds 	page = alloc_page(GFP_KERNEL | GFP_DMA32);
11881da177e4SLinus Torvalds 	if (page == NULL)
11891da177e4SLinus Torvalds 		return NULL;
11901da177e4SLinus Torvalds 
11911da177e4SLinus Torvalds 	map_page_into_agp(page);
11921da177e4SLinus Torvalds 
11931da177e4SLinus Torvalds 	get_page(page);
11941da177e4SLinus Torvalds 	SetPageLocked(page);
11951da177e4SLinus Torvalds 	atomic_inc(&agp_bridge->current_memory_agp);
11961da177e4SLinus Torvalds 	return page_address(page);
11971da177e4SLinus Torvalds }
11981da177e4SLinus Torvalds EXPORT_SYMBOL(agp_generic_alloc_page);
11991da177e4SLinus Torvalds 
12001da177e4SLinus Torvalds 
12011da177e4SLinus Torvalds void agp_generic_destroy_page(void *addr)
12021da177e4SLinus Torvalds {
12031da177e4SLinus Torvalds 	struct page *page;
12041da177e4SLinus Torvalds 
12051da177e4SLinus Torvalds 	if (addr == NULL)
12061da177e4SLinus Torvalds 		return;
12071da177e4SLinus Torvalds 
12081da177e4SLinus Torvalds 	page = virt_to_page(addr);
12091da177e4SLinus Torvalds 	unmap_page_from_agp(page);
12101da177e4SLinus Torvalds 	put_page(page);
12111da177e4SLinus Torvalds 	unlock_page(page);
12121da177e4SLinus Torvalds 	free_page((unsigned long)addr);
12131da177e4SLinus Torvalds 	atomic_dec(&agp_bridge->current_memory_agp);
12141da177e4SLinus Torvalds }
12151da177e4SLinus Torvalds EXPORT_SYMBOL(agp_generic_destroy_page);
12161da177e4SLinus Torvalds 
12171da177e4SLinus Torvalds /* End Basic Page Allocation Routines */
12181da177e4SLinus Torvalds 
12191da177e4SLinus Torvalds 
12201da177e4SLinus Torvalds /**
12211da177e4SLinus Torvalds  * agp_enable  -  initialise the agp point-to-point connection.
12221da177e4SLinus Torvalds  *
12231da177e4SLinus Torvalds  * @mode:	agp mode register value to configure with.
12241da177e4SLinus Torvalds  */
12251da177e4SLinus Torvalds void agp_enable(struct agp_bridge_data *bridge, u32 mode)
12261da177e4SLinus Torvalds {
12271da177e4SLinus Torvalds 	if (!bridge)
12281da177e4SLinus Torvalds 		return;
12291da177e4SLinus Torvalds 	bridge->driver->agp_enable(bridge, mode);
12301da177e4SLinus Torvalds }
12311da177e4SLinus Torvalds EXPORT_SYMBOL(agp_enable);
12321da177e4SLinus Torvalds 
12331da177e4SLinus Torvalds /* When we remove the global variable agp_bridge from all drivers
12341da177e4SLinus Torvalds  * then agp_alloc_bridge and agp_generic_find_bridge need to be updated
12351da177e4SLinus Torvalds  */
12361da177e4SLinus Torvalds 
12371da177e4SLinus Torvalds struct agp_bridge_data *agp_generic_find_bridge(struct pci_dev *pdev)
12381da177e4SLinus Torvalds {
12391da177e4SLinus Torvalds 	if (list_empty(&agp_bridges))
12401da177e4SLinus Torvalds 		return NULL;
12411da177e4SLinus Torvalds 
12421da177e4SLinus Torvalds 	return agp_bridge;
12431da177e4SLinus Torvalds }
12441da177e4SLinus Torvalds 
12451da177e4SLinus Torvalds static void ipi_handler(void *null)
12461da177e4SLinus Torvalds {
12471da177e4SLinus Torvalds 	flush_agp_cache();
12481da177e4SLinus Torvalds }
12491da177e4SLinus Torvalds 
12501da177e4SLinus Torvalds void global_cache_flush(void)
12511da177e4SLinus Torvalds {
12521da177e4SLinus Torvalds 	if (on_each_cpu(ipi_handler, NULL, 1, 1) != 0)
12531da177e4SLinus Torvalds 		panic(PFX "timed out waiting for the other CPUs!\n");
12541da177e4SLinus Torvalds }
12551da177e4SLinus Torvalds EXPORT_SYMBOL(global_cache_flush);
12561da177e4SLinus Torvalds 
12571da177e4SLinus Torvalds unsigned long agp_generic_mask_memory(struct agp_bridge_data *bridge,
12581da177e4SLinus Torvalds 	unsigned long addr, int type)
12591da177e4SLinus Torvalds {
12601da177e4SLinus Torvalds 	/* memory type is ignored in the generic routine */
12611da177e4SLinus Torvalds 	if (bridge->driver->masks)
12621da177e4SLinus Torvalds 		return addr | bridge->driver->masks[0].mask;
12631da177e4SLinus Torvalds 	else
12641da177e4SLinus Torvalds 		return addr;
12651da177e4SLinus Torvalds }
12661da177e4SLinus Torvalds EXPORT_SYMBOL(agp_generic_mask_memory);
12671da177e4SLinus Torvalds 
1268a030ce44SThomas Hellstrom int agp_generic_type_to_mask_type(struct agp_bridge_data *bridge,
1269a030ce44SThomas Hellstrom 				  int type)
1270a030ce44SThomas Hellstrom {
1271a030ce44SThomas Hellstrom 	if (type >= AGP_USER_TYPES)
1272a030ce44SThomas Hellstrom 		return 0;
1273a030ce44SThomas Hellstrom 	return type;
1274a030ce44SThomas Hellstrom }
1275a030ce44SThomas Hellstrom EXPORT_SYMBOL(agp_generic_type_to_mask_type);
1276a030ce44SThomas Hellstrom 
12771da177e4SLinus Torvalds /*
12781da177e4SLinus Torvalds  * These functions are implemented according to the AGPv3 spec,
12791da177e4SLinus Torvalds  * which covers implementation details that had previously been
12801da177e4SLinus Torvalds  * left open.
12811da177e4SLinus Torvalds  */
12821da177e4SLinus Torvalds 
12831da177e4SLinus Torvalds int agp3_generic_fetch_size(void)
12841da177e4SLinus Torvalds {
12851da177e4SLinus Torvalds 	u16 temp_size;
12861da177e4SLinus Torvalds 	int i;
12871da177e4SLinus Torvalds 	struct aper_size_info_16 *values;
12881da177e4SLinus Torvalds 
12891da177e4SLinus Torvalds 	pci_read_config_word(agp_bridge->dev, agp_bridge->capndx+AGPAPSIZE, &temp_size);
12901da177e4SLinus Torvalds 	values = A_SIZE_16(agp_bridge->driver->aperture_sizes);
12911da177e4SLinus Torvalds 
12921da177e4SLinus Torvalds 	for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++) {
12931da177e4SLinus Torvalds 		if (temp_size == values[i].size_value) {
12941da177e4SLinus Torvalds 			agp_bridge->previous_size =
12951da177e4SLinus Torvalds 				agp_bridge->current_size = (void *) (values + i);
12961da177e4SLinus Torvalds 
12971da177e4SLinus Torvalds 			agp_bridge->aperture_size_idx = i;
12981da177e4SLinus Torvalds 			return values[i].size;
12991da177e4SLinus Torvalds 		}
13001da177e4SLinus Torvalds 	}
13011da177e4SLinus Torvalds 	return 0;
13021da177e4SLinus Torvalds }
13031da177e4SLinus Torvalds EXPORT_SYMBOL(agp3_generic_fetch_size);
13041da177e4SLinus Torvalds 
13051da177e4SLinus Torvalds void agp3_generic_tlbflush(struct agp_memory *mem)
13061da177e4SLinus Torvalds {
13071da177e4SLinus Torvalds 	u32 ctrl;
13081da177e4SLinus Torvalds 	pci_read_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, &ctrl);
13091da177e4SLinus Torvalds 	pci_write_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, ctrl & ~AGPCTRL_GTLBEN);
13101da177e4SLinus Torvalds 	pci_write_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, ctrl);
13111da177e4SLinus Torvalds }
13121da177e4SLinus Torvalds EXPORT_SYMBOL(agp3_generic_tlbflush);
13131da177e4SLinus Torvalds 
13141da177e4SLinus Torvalds int agp3_generic_configure(void)
13151da177e4SLinus Torvalds {
13161da177e4SLinus Torvalds 	u32 temp;
13171da177e4SLinus Torvalds 	struct aper_size_info_16 *current_size;
13181da177e4SLinus Torvalds 
13191da177e4SLinus Torvalds 	current_size = A_SIZE_16(agp_bridge->current_size);
13201da177e4SLinus Torvalds 
13211da177e4SLinus Torvalds 	pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp);
13221da177e4SLinus Torvalds 	agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
13231da177e4SLinus Torvalds 
13241da177e4SLinus Torvalds 	/* set aperture size */
13251da177e4SLinus Torvalds 	pci_write_config_word(agp_bridge->dev, agp_bridge->capndx+AGPAPSIZE, current_size->size_value);
13261da177e4SLinus Torvalds 	/* set gart pointer */
13271da177e4SLinus Torvalds 	pci_write_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPGARTLO, agp_bridge->gatt_bus_addr);
13281da177e4SLinus Torvalds 	/* enable aperture and GTLB */
13291da177e4SLinus Torvalds 	pci_read_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, &temp);
13301da177e4SLinus Torvalds 	pci_write_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, temp | AGPCTRL_APERENB | AGPCTRL_GTLBEN);
13311da177e4SLinus Torvalds 	return 0;
13321da177e4SLinus Torvalds }
13331da177e4SLinus Torvalds EXPORT_SYMBOL(agp3_generic_configure);
13341da177e4SLinus Torvalds 
13351da177e4SLinus Torvalds void agp3_generic_cleanup(void)
13361da177e4SLinus Torvalds {
13371da177e4SLinus Torvalds 	u32 ctrl;
13381da177e4SLinus Torvalds 	pci_read_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, &ctrl);
13391da177e4SLinus Torvalds 	pci_write_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, ctrl & ~AGPCTRL_APERENB);
13401da177e4SLinus Torvalds }
13411da177e4SLinus Torvalds EXPORT_SYMBOL(agp3_generic_cleanup);
13421da177e4SLinus Torvalds 
13431da177e4SLinus Torvalds struct aper_size_info_16 agp3_generic_sizes[AGP_GENERIC_SIZES_ENTRIES] =
13441da177e4SLinus Torvalds {
13451da177e4SLinus Torvalds 	{4096, 1048576, 10,0x000},
13461da177e4SLinus Torvalds 	{2048,  524288, 9, 0x800},
13471da177e4SLinus Torvalds 	{1024,  262144, 8, 0xc00},
13481da177e4SLinus Torvalds 	{ 512,  131072, 7, 0xe00},
13491da177e4SLinus Torvalds 	{ 256,   65536, 6, 0xf00},
13501da177e4SLinus Torvalds 	{ 128,   32768, 5, 0xf20},
13511da177e4SLinus Torvalds 	{  64,   16384, 4, 0xf30},
13521da177e4SLinus Torvalds 	{  32,    8192, 3, 0xf38},
13531da177e4SLinus Torvalds 	{  16,    4096, 2, 0xf3c},
13541da177e4SLinus Torvalds 	{   8,    2048, 1, 0xf3e},
13551da177e4SLinus Torvalds 	{   4,    1024, 0, 0xf3f}
13561da177e4SLinus Torvalds };
13571da177e4SLinus Torvalds EXPORT_SYMBOL(agp3_generic_sizes);
13581da177e4SLinus Torvalds 
1359