162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * AGPGART driver. 362306a36Sopenharmony_ci * Copyright (C) 2004 Silicon Graphics, Inc. 462306a36Sopenharmony_ci * Copyright (C) 2002-2005 Dave Jones. 562306a36Sopenharmony_ci * Copyright (C) 1999 Jeff Hartmann. 662306a36Sopenharmony_ci * Copyright (C) 1999 Precision Insight, Inc. 762306a36Sopenharmony_ci * Copyright (C) 1999 Xi Graphics, Inc. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 1062306a36Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 1162306a36Sopenharmony_ci * to deal in the Software without restriction, including without limitation 1262306a36Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 1362306a36Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 1462306a36Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * The above copyright notice and this permission notice shall be included 1762306a36Sopenharmony_ci * in all copies or substantial portions of the Software. 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 2062306a36Sopenharmony_ci * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 2162306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 2262306a36Sopenharmony_ci * JEFF HARTMANN, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM, 2362306a36Sopenharmony_ci * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 2462306a36Sopenharmony_ci * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 2562306a36Sopenharmony_ci * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci * TODO: 2862306a36Sopenharmony_ci * - Allocate more than order 0 pages to avoid too much linear map splitting. 2962306a36Sopenharmony_ci */ 3062306a36Sopenharmony_ci#include <linux/module.h> 3162306a36Sopenharmony_ci#include <linux/pci.h> 3262306a36Sopenharmony_ci#include <linux/pagemap.h> 3362306a36Sopenharmony_ci#include <linux/miscdevice.h> 3462306a36Sopenharmony_ci#include <linux/pm.h> 3562306a36Sopenharmony_ci#include <linux/agp_backend.h> 3662306a36Sopenharmony_ci#include <linux/vmalloc.h> 3762306a36Sopenharmony_ci#include <linux/dma-mapping.h> 3862306a36Sopenharmony_ci#include <linux/mm.h> 3962306a36Sopenharmony_ci#include <linux/sched.h> 4062306a36Sopenharmony_ci#include <linux/slab.h> 4162306a36Sopenharmony_ci#include <asm/io.h> 4262306a36Sopenharmony_ci#ifdef CONFIG_X86 4362306a36Sopenharmony_ci#include <asm/set_memory.h> 4462306a36Sopenharmony_ci#endif 4562306a36Sopenharmony_ci#include "agp.h" 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci__u32 *agp_gatt_table; 4862306a36Sopenharmony_ciint agp_memory_reserved; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/* 5162306a36Sopenharmony_ci * Needed by the Nforce GART driver for the time being. Would be 5262306a36Sopenharmony_ci * nice to do this some other way instead of needing this export. 5362306a36Sopenharmony_ci */ 5462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(agp_memory_reserved); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci/* 5762306a36Sopenharmony_ci * Generic routines for handling agp_memory structures - 5862306a36Sopenharmony_ci * They use the basic page allocation routines to do the brunt of the work. 5962306a36Sopenharmony_ci */ 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_civoid agp_free_key(int key) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci if (key < 0) 6462306a36Sopenharmony_ci return; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci if (key < MAXKEY) 6762306a36Sopenharmony_ci clear_bit(key, agp_bridge->key_list); 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ciEXPORT_SYMBOL(agp_free_key); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic int agp_get_key(void) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci int bit; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci bit = find_first_zero_bit(agp_bridge->key_list, MAXKEY); 7762306a36Sopenharmony_ci if (bit < MAXKEY) { 7862306a36Sopenharmony_ci set_bit(bit, agp_bridge->key_list); 7962306a36Sopenharmony_ci return bit; 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci return -1; 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci/* 8562306a36Sopenharmony_ci * Use kmalloc if possible for the page list. Otherwise fall back to 8662306a36Sopenharmony_ci * vmalloc. This speeds things up and also saves memory for small AGP 8762306a36Sopenharmony_ci * regions. 8862306a36Sopenharmony_ci */ 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_civoid agp_alloc_page_array(size_t size, struct agp_memory *mem) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci mem->pages = kvmalloc(size, GFP_KERNEL); 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ciEXPORT_SYMBOL(agp_alloc_page_array); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic struct agp_memory *agp_create_user_memory(unsigned long num_agp_pages) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci struct agp_memory *new; 9962306a36Sopenharmony_ci unsigned long alloc_size = num_agp_pages*sizeof(struct page *); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if (INT_MAX/sizeof(struct page *) < num_agp_pages) 10262306a36Sopenharmony_ci return NULL; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci new = kzalloc(sizeof(struct agp_memory), GFP_KERNEL); 10562306a36Sopenharmony_ci if (new == NULL) 10662306a36Sopenharmony_ci return NULL; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci new->key = agp_get_key(); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci if (new->key < 0) { 11162306a36Sopenharmony_ci kfree(new); 11262306a36Sopenharmony_ci return NULL; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci agp_alloc_page_array(alloc_size, new); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci if (new->pages == NULL) { 11862306a36Sopenharmony_ci agp_free_key(new->key); 11962306a36Sopenharmony_ci kfree(new); 12062306a36Sopenharmony_ci return NULL; 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci new->num_scratch_pages = 0; 12362306a36Sopenharmony_ci return new; 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistruct agp_memory *agp_create_memory(int scratch_pages) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci struct agp_memory *new; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci new = kzalloc(sizeof(struct agp_memory), GFP_KERNEL); 13162306a36Sopenharmony_ci if (new == NULL) 13262306a36Sopenharmony_ci return NULL; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci new->key = agp_get_key(); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci if (new->key < 0) { 13762306a36Sopenharmony_ci kfree(new); 13862306a36Sopenharmony_ci return NULL; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci agp_alloc_page_array(PAGE_SIZE * scratch_pages, new); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci if (new->pages == NULL) { 14462306a36Sopenharmony_ci agp_free_key(new->key); 14562306a36Sopenharmony_ci kfree(new); 14662306a36Sopenharmony_ci return NULL; 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci new->num_scratch_pages = scratch_pages; 14962306a36Sopenharmony_ci new->type = AGP_NORMAL_MEMORY; 15062306a36Sopenharmony_ci return new; 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ciEXPORT_SYMBOL(agp_create_memory); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci/** 15562306a36Sopenharmony_ci * agp_free_memory - free memory associated with an agp_memory pointer. 15662306a36Sopenharmony_ci * 15762306a36Sopenharmony_ci * @curr: agp_memory pointer to be freed. 15862306a36Sopenharmony_ci * 15962306a36Sopenharmony_ci * It is the only function that can be called when the backend is not owned 16062306a36Sopenharmony_ci * by the caller. (So it can free memory on client death.) 16162306a36Sopenharmony_ci */ 16262306a36Sopenharmony_civoid agp_free_memory(struct agp_memory *curr) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci size_t i; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci if (curr == NULL) 16762306a36Sopenharmony_ci return; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci if (curr->is_bound) 17062306a36Sopenharmony_ci agp_unbind_memory(curr); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci if (curr->type >= AGP_USER_TYPES) { 17362306a36Sopenharmony_ci agp_generic_free_by_type(curr); 17462306a36Sopenharmony_ci return; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci if (curr->type != 0) { 17862306a36Sopenharmony_ci curr->bridge->driver->free_by_type(curr); 17962306a36Sopenharmony_ci return; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci if (curr->page_count != 0) { 18262306a36Sopenharmony_ci if (curr->bridge->driver->agp_destroy_pages) { 18362306a36Sopenharmony_ci curr->bridge->driver->agp_destroy_pages(curr); 18462306a36Sopenharmony_ci } else { 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci for (i = 0; i < curr->page_count; i++) { 18762306a36Sopenharmony_ci curr->bridge->driver->agp_destroy_page( 18862306a36Sopenharmony_ci curr->pages[i], 18962306a36Sopenharmony_ci AGP_PAGE_DESTROY_UNMAP); 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci for (i = 0; i < curr->page_count; i++) { 19262306a36Sopenharmony_ci curr->bridge->driver->agp_destroy_page( 19362306a36Sopenharmony_ci curr->pages[i], 19462306a36Sopenharmony_ci AGP_PAGE_DESTROY_FREE); 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci agp_free_key(curr->key); 19962306a36Sopenharmony_ci agp_free_page_array(curr); 20062306a36Sopenharmony_ci kfree(curr); 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ciEXPORT_SYMBOL(agp_free_memory); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci#define ENTRIES_PER_PAGE (PAGE_SIZE / sizeof(unsigned long)) 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci/** 20762306a36Sopenharmony_ci * agp_allocate_memory - allocate a group of pages of a certain type. 20862306a36Sopenharmony_ci * 20962306a36Sopenharmony_ci * @bridge: an agp_bridge_data struct allocated for the AGP host bridge. 21062306a36Sopenharmony_ci * @page_count: size_t argument of the number of pages 21162306a36Sopenharmony_ci * @type: u32 argument of the type of memory to be allocated. 21262306a36Sopenharmony_ci * 21362306a36Sopenharmony_ci * Every agp bridge device will allow you to allocate AGP_NORMAL_MEMORY which 21462306a36Sopenharmony_ci * maps to physical ram. Any other type is device dependent. 21562306a36Sopenharmony_ci * 21662306a36Sopenharmony_ci * It returns NULL whenever memory is unavailable. 21762306a36Sopenharmony_ci */ 21862306a36Sopenharmony_cistruct agp_memory *agp_allocate_memory(struct agp_bridge_data *bridge, 21962306a36Sopenharmony_ci size_t page_count, u32 type) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci int scratch_pages; 22262306a36Sopenharmony_ci struct agp_memory *new; 22362306a36Sopenharmony_ci size_t i; 22462306a36Sopenharmony_ci int cur_memory; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if (!bridge) 22762306a36Sopenharmony_ci return NULL; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci cur_memory = atomic_read(&bridge->current_memory_agp); 23062306a36Sopenharmony_ci if ((cur_memory + page_count > bridge->max_memory_agp) || 23162306a36Sopenharmony_ci (cur_memory + page_count < page_count)) 23262306a36Sopenharmony_ci return NULL; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci if (type >= AGP_USER_TYPES) { 23562306a36Sopenharmony_ci new = agp_generic_alloc_user(page_count, type); 23662306a36Sopenharmony_ci if (new) 23762306a36Sopenharmony_ci new->bridge = bridge; 23862306a36Sopenharmony_ci return new; 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci if (type != 0) { 24262306a36Sopenharmony_ci new = bridge->driver->alloc_by_type(page_count, type); 24362306a36Sopenharmony_ci if (new) 24462306a36Sopenharmony_ci new->bridge = bridge; 24562306a36Sopenharmony_ci return new; 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci scratch_pages = (page_count + ENTRIES_PER_PAGE - 1) / ENTRIES_PER_PAGE; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci new = agp_create_memory(scratch_pages); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci if (new == NULL) 25362306a36Sopenharmony_ci return NULL; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci if (bridge->driver->agp_alloc_pages) { 25662306a36Sopenharmony_ci if (bridge->driver->agp_alloc_pages(bridge, new, page_count)) { 25762306a36Sopenharmony_ci agp_free_memory(new); 25862306a36Sopenharmony_ci return NULL; 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci new->bridge = bridge; 26162306a36Sopenharmony_ci return new; 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci for (i = 0; i < page_count; i++) { 26562306a36Sopenharmony_ci struct page *page = bridge->driver->agp_alloc_page(bridge); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci if (page == NULL) { 26862306a36Sopenharmony_ci agp_free_memory(new); 26962306a36Sopenharmony_ci return NULL; 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci new->pages[i] = page; 27262306a36Sopenharmony_ci new->page_count++; 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci new->bridge = bridge; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci return new; 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ciEXPORT_SYMBOL(agp_allocate_memory); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci/* End - Generic routines for handling agp_memory structures */ 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cistatic int agp_return_size(void) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci int current_size; 28762306a36Sopenharmony_ci void *temp; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci temp = agp_bridge->current_size; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci switch (agp_bridge->driver->size_type) { 29262306a36Sopenharmony_ci case U8_APER_SIZE: 29362306a36Sopenharmony_ci current_size = A_SIZE_8(temp)->size; 29462306a36Sopenharmony_ci break; 29562306a36Sopenharmony_ci case U16_APER_SIZE: 29662306a36Sopenharmony_ci current_size = A_SIZE_16(temp)->size; 29762306a36Sopenharmony_ci break; 29862306a36Sopenharmony_ci case U32_APER_SIZE: 29962306a36Sopenharmony_ci current_size = A_SIZE_32(temp)->size; 30062306a36Sopenharmony_ci break; 30162306a36Sopenharmony_ci case LVL2_APER_SIZE: 30262306a36Sopenharmony_ci current_size = A_SIZE_LVL2(temp)->size; 30362306a36Sopenharmony_ci break; 30462306a36Sopenharmony_ci case FIXED_APER_SIZE: 30562306a36Sopenharmony_ci current_size = A_SIZE_FIX(temp)->size; 30662306a36Sopenharmony_ci break; 30762306a36Sopenharmony_ci default: 30862306a36Sopenharmony_ci current_size = 0; 30962306a36Sopenharmony_ci break; 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci current_size -= (agp_memory_reserved / (1024*1024)); 31362306a36Sopenharmony_ci if (current_size <0) 31462306a36Sopenharmony_ci current_size = 0; 31562306a36Sopenharmony_ci return current_size; 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ciint agp_num_entries(void) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci int num_entries; 32262306a36Sopenharmony_ci void *temp; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci temp = agp_bridge->current_size; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci switch (agp_bridge->driver->size_type) { 32762306a36Sopenharmony_ci case U8_APER_SIZE: 32862306a36Sopenharmony_ci num_entries = A_SIZE_8(temp)->num_entries; 32962306a36Sopenharmony_ci break; 33062306a36Sopenharmony_ci case U16_APER_SIZE: 33162306a36Sopenharmony_ci num_entries = A_SIZE_16(temp)->num_entries; 33262306a36Sopenharmony_ci break; 33362306a36Sopenharmony_ci case U32_APER_SIZE: 33462306a36Sopenharmony_ci num_entries = A_SIZE_32(temp)->num_entries; 33562306a36Sopenharmony_ci break; 33662306a36Sopenharmony_ci case LVL2_APER_SIZE: 33762306a36Sopenharmony_ci num_entries = A_SIZE_LVL2(temp)->num_entries; 33862306a36Sopenharmony_ci break; 33962306a36Sopenharmony_ci case FIXED_APER_SIZE: 34062306a36Sopenharmony_ci num_entries = A_SIZE_FIX(temp)->num_entries; 34162306a36Sopenharmony_ci break; 34262306a36Sopenharmony_ci default: 34362306a36Sopenharmony_ci num_entries = 0; 34462306a36Sopenharmony_ci break; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci num_entries -= agp_memory_reserved>>PAGE_SHIFT; 34862306a36Sopenharmony_ci if (num_entries<0) 34962306a36Sopenharmony_ci num_entries = 0; 35062306a36Sopenharmony_ci return num_entries; 35162306a36Sopenharmony_ci} 35262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(agp_num_entries); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci/** 35662306a36Sopenharmony_ci * agp_copy_info - copy bridge state information 35762306a36Sopenharmony_ci * 35862306a36Sopenharmony_ci * @bridge: an agp_bridge_data struct allocated for the AGP host bridge. 35962306a36Sopenharmony_ci * @info: agp_kern_info pointer. The caller should insure that this pointer is valid. 36062306a36Sopenharmony_ci * 36162306a36Sopenharmony_ci * This function copies information about the agp bridge device and the state of 36262306a36Sopenharmony_ci * the agp backend into an agp_kern_info pointer. 36362306a36Sopenharmony_ci */ 36462306a36Sopenharmony_ciint agp_copy_info(struct agp_bridge_data *bridge, struct agp_kern_info *info) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci memset(info, 0, sizeof(struct agp_kern_info)); 36762306a36Sopenharmony_ci if (!bridge) { 36862306a36Sopenharmony_ci info->chipset = NOT_SUPPORTED; 36962306a36Sopenharmony_ci return -EIO; 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci info->version.major = bridge->version->major; 37362306a36Sopenharmony_ci info->version.minor = bridge->version->minor; 37462306a36Sopenharmony_ci info->chipset = SUPPORTED; 37562306a36Sopenharmony_ci info->device = bridge->dev; 37662306a36Sopenharmony_ci if (bridge->mode & AGPSTAT_MODE_3_0) 37762306a36Sopenharmony_ci info->mode = bridge->mode & ~AGP3_RESERVED_MASK; 37862306a36Sopenharmony_ci else 37962306a36Sopenharmony_ci info->mode = bridge->mode & ~AGP2_RESERVED_MASK; 38062306a36Sopenharmony_ci info->aper_base = bridge->gart_bus_addr; 38162306a36Sopenharmony_ci info->aper_size = agp_return_size(); 38262306a36Sopenharmony_ci info->max_memory = bridge->max_memory_agp; 38362306a36Sopenharmony_ci info->current_memory = atomic_read(&bridge->current_memory_agp); 38462306a36Sopenharmony_ci info->cant_use_aperture = bridge->driver->cant_use_aperture; 38562306a36Sopenharmony_ci info->vm_ops = bridge->vm_ops; 38662306a36Sopenharmony_ci info->page_mask = ~0UL; 38762306a36Sopenharmony_ci return 0; 38862306a36Sopenharmony_ci} 38962306a36Sopenharmony_ciEXPORT_SYMBOL(agp_copy_info); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci/* End - Routine to copy over information structure */ 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci/* 39462306a36Sopenharmony_ci * Routines for handling swapping of agp_memory into the GATT - 39562306a36Sopenharmony_ci * These routines take agp_memory and insert them into the GATT. 39662306a36Sopenharmony_ci * They call device specific routines to actually write to the GATT. 39762306a36Sopenharmony_ci */ 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci/** 40062306a36Sopenharmony_ci * agp_bind_memory - Bind an agp_memory structure into the GATT. 40162306a36Sopenharmony_ci * 40262306a36Sopenharmony_ci * @curr: agp_memory pointer 40362306a36Sopenharmony_ci * @pg_start: an offset into the graphics aperture translation table 40462306a36Sopenharmony_ci * 40562306a36Sopenharmony_ci * It returns -EINVAL if the pointer == NULL. 40662306a36Sopenharmony_ci * It returns -EBUSY if the area of the table requested is already in use. 40762306a36Sopenharmony_ci */ 40862306a36Sopenharmony_ciint agp_bind_memory(struct agp_memory *curr, off_t pg_start) 40962306a36Sopenharmony_ci{ 41062306a36Sopenharmony_ci int ret_val; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci if (curr == NULL) 41362306a36Sopenharmony_ci return -EINVAL; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci if (curr->is_bound) { 41662306a36Sopenharmony_ci printk(KERN_INFO PFX "memory %p is already bound!\n", curr); 41762306a36Sopenharmony_ci return -EINVAL; 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci if (!curr->is_flushed) { 42062306a36Sopenharmony_ci curr->bridge->driver->cache_flush(); 42162306a36Sopenharmony_ci curr->is_flushed = true; 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci ret_val = curr->bridge->driver->insert_memory(curr, pg_start, curr->type); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci if (ret_val != 0) 42762306a36Sopenharmony_ci return ret_val; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci curr->is_bound = true; 43062306a36Sopenharmony_ci curr->pg_start = pg_start; 43162306a36Sopenharmony_ci spin_lock(&agp_bridge->mapped_lock); 43262306a36Sopenharmony_ci list_add(&curr->mapped_list, &agp_bridge->mapped_list); 43362306a36Sopenharmony_ci spin_unlock(&agp_bridge->mapped_lock); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci return 0; 43662306a36Sopenharmony_ci} 43762306a36Sopenharmony_ciEXPORT_SYMBOL(agp_bind_memory); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci/** 44162306a36Sopenharmony_ci * agp_unbind_memory - Removes an agp_memory structure from the GATT 44262306a36Sopenharmony_ci * 44362306a36Sopenharmony_ci * @curr: agp_memory pointer to be removed from the GATT. 44462306a36Sopenharmony_ci * 44562306a36Sopenharmony_ci * It returns -EINVAL if this piece of agp_memory is not currently bound to 44662306a36Sopenharmony_ci * the graphics aperture translation table or if the agp_memory pointer == NULL 44762306a36Sopenharmony_ci */ 44862306a36Sopenharmony_ciint agp_unbind_memory(struct agp_memory *curr) 44962306a36Sopenharmony_ci{ 45062306a36Sopenharmony_ci int ret_val; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci if (curr == NULL) 45362306a36Sopenharmony_ci return -EINVAL; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci if (!curr->is_bound) { 45662306a36Sopenharmony_ci printk(KERN_INFO PFX "memory %p was not bound!\n", curr); 45762306a36Sopenharmony_ci return -EINVAL; 45862306a36Sopenharmony_ci } 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci ret_val = curr->bridge->driver->remove_memory(curr, curr->pg_start, curr->type); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci if (ret_val != 0) 46362306a36Sopenharmony_ci return ret_val; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci curr->is_bound = false; 46662306a36Sopenharmony_ci curr->pg_start = 0; 46762306a36Sopenharmony_ci spin_lock(&curr->bridge->mapped_lock); 46862306a36Sopenharmony_ci list_del(&curr->mapped_list); 46962306a36Sopenharmony_ci spin_unlock(&curr->bridge->mapped_lock); 47062306a36Sopenharmony_ci return 0; 47162306a36Sopenharmony_ci} 47262306a36Sopenharmony_ciEXPORT_SYMBOL(agp_unbind_memory); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci/* End - Routines for handling swapping of agp_memory into the GATT */ 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci/* Generic Agp routines - Start */ 47962306a36Sopenharmony_cistatic void agp_v2_parse_one(u32 *requested_mode, u32 *bridge_agpstat, u32 *vga_agpstat) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci u32 tmp; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci if (*requested_mode & AGP2_RESERVED_MASK) { 48462306a36Sopenharmony_ci printk(KERN_INFO PFX "reserved bits set (%x) in mode 0x%x. Fixed.\n", 48562306a36Sopenharmony_ci *requested_mode & AGP2_RESERVED_MASK, *requested_mode); 48662306a36Sopenharmony_ci *requested_mode &= ~AGP2_RESERVED_MASK; 48762306a36Sopenharmony_ci } 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci /* 49062306a36Sopenharmony_ci * Some dumb bridges are programmed to disobey the AGP2 spec. 49162306a36Sopenharmony_ci * This is likely a BIOS misprogramming rather than poweron default, or 49262306a36Sopenharmony_ci * it would be a lot more common. 49362306a36Sopenharmony_ci * https://bugs.freedesktop.org/show_bug.cgi?id=8816 49462306a36Sopenharmony_ci * AGPv2 spec 6.1.9 states: 49562306a36Sopenharmony_ci * The RATE field indicates the data transfer rates supported by this 49662306a36Sopenharmony_ci * device. A.G.P. devices must report all that apply. 49762306a36Sopenharmony_ci * Fix them up as best we can. 49862306a36Sopenharmony_ci */ 49962306a36Sopenharmony_ci switch (*bridge_agpstat & 7) { 50062306a36Sopenharmony_ci case 4: 50162306a36Sopenharmony_ci *bridge_agpstat |= (AGPSTAT2_2X | AGPSTAT2_1X); 50262306a36Sopenharmony_ci printk(KERN_INFO PFX "BIOS bug. AGP bridge claims to only support x4 rate. " 50362306a36Sopenharmony_ci "Fixing up support for x2 & x1\n"); 50462306a36Sopenharmony_ci break; 50562306a36Sopenharmony_ci case 2: 50662306a36Sopenharmony_ci *bridge_agpstat |= AGPSTAT2_1X; 50762306a36Sopenharmony_ci printk(KERN_INFO PFX "BIOS bug. AGP bridge claims to only support x2 rate. " 50862306a36Sopenharmony_ci "Fixing up support for x1\n"); 50962306a36Sopenharmony_ci break; 51062306a36Sopenharmony_ci default: 51162306a36Sopenharmony_ci break; 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci /* Check the speed bits make sense. Only one should be set. */ 51562306a36Sopenharmony_ci tmp = *requested_mode & 7; 51662306a36Sopenharmony_ci switch (tmp) { 51762306a36Sopenharmony_ci case 0: 51862306a36Sopenharmony_ci printk(KERN_INFO PFX "%s tried to set rate=x0. Setting to x1 mode.\n", current->comm); 51962306a36Sopenharmony_ci *requested_mode |= AGPSTAT2_1X; 52062306a36Sopenharmony_ci break; 52162306a36Sopenharmony_ci case 1: 52262306a36Sopenharmony_ci case 2: 52362306a36Sopenharmony_ci break; 52462306a36Sopenharmony_ci case 3: 52562306a36Sopenharmony_ci *requested_mode &= ~(AGPSTAT2_1X); /* rate=2 */ 52662306a36Sopenharmony_ci break; 52762306a36Sopenharmony_ci case 4: 52862306a36Sopenharmony_ci break; 52962306a36Sopenharmony_ci case 5: 53062306a36Sopenharmony_ci case 6: 53162306a36Sopenharmony_ci case 7: 53262306a36Sopenharmony_ci *requested_mode &= ~(AGPSTAT2_1X|AGPSTAT2_2X); /* rate=4*/ 53362306a36Sopenharmony_ci break; 53462306a36Sopenharmony_ci } 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci /* disable SBA if it's not supported */ 53762306a36Sopenharmony_ci if (!((*bridge_agpstat & AGPSTAT_SBA) && (*vga_agpstat & AGPSTAT_SBA) && (*requested_mode & AGPSTAT_SBA))) 53862306a36Sopenharmony_ci *bridge_agpstat &= ~AGPSTAT_SBA; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci /* Set rate */ 54162306a36Sopenharmony_ci if (!((*bridge_agpstat & AGPSTAT2_4X) && (*vga_agpstat & AGPSTAT2_4X) && (*requested_mode & AGPSTAT2_4X))) 54262306a36Sopenharmony_ci *bridge_agpstat &= ~AGPSTAT2_4X; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci if (!((*bridge_agpstat & AGPSTAT2_2X) && (*vga_agpstat & AGPSTAT2_2X) && (*requested_mode & AGPSTAT2_2X))) 54562306a36Sopenharmony_ci *bridge_agpstat &= ~AGPSTAT2_2X; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci if (!((*bridge_agpstat & AGPSTAT2_1X) && (*vga_agpstat & AGPSTAT2_1X) && (*requested_mode & AGPSTAT2_1X))) 54862306a36Sopenharmony_ci *bridge_agpstat &= ~AGPSTAT2_1X; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci /* Now we know what mode it should be, clear out the unwanted bits. */ 55162306a36Sopenharmony_ci if (*bridge_agpstat & AGPSTAT2_4X) 55262306a36Sopenharmony_ci *bridge_agpstat &= ~(AGPSTAT2_1X | AGPSTAT2_2X); /* 4X */ 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci if (*bridge_agpstat & AGPSTAT2_2X) 55562306a36Sopenharmony_ci *bridge_agpstat &= ~(AGPSTAT2_1X | AGPSTAT2_4X); /* 2X */ 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci if (*bridge_agpstat & AGPSTAT2_1X) 55862306a36Sopenharmony_ci *bridge_agpstat &= ~(AGPSTAT2_2X | AGPSTAT2_4X); /* 1X */ 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci /* Apply any errata. */ 56162306a36Sopenharmony_ci if (agp_bridge->flags & AGP_ERRATA_FASTWRITES) 56262306a36Sopenharmony_ci *bridge_agpstat &= ~AGPSTAT_FW; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci if (agp_bridge->flags & AGP_ERRATA_SBA) 56562306a36Sopenharmony_ci *bridge_agpstat &= ~AGPSTAT_SBA; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci if (agp_bridge->flags & AGP_ERRATA_1X) { 56862306a36Sopenharmony_ci *bridge_agpstat &= ~(AGPSTAT2_2X | AGPSTAT2_4X); 56962306a36Sopenharmony_ci *bridge_agpstat |= AGPSTAT2_1X; 57062306a36Sopenharmony_ci } 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci /* If we've dropped down to 1X, disable fast writes. */ 57362306a36Sopenharmony_ci if (*bridge_agpstat & AGPSTAT2_1X) 57462306a36Sopenharmony_ci *bridge_agpstat &= ~AGPSTAT_FW; 57562306a36Sopenharmony_ci} 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci/* 57862306a36Sopenharmony_ci * requested_mode = Mode requested by (typically) X. 57962306a36Sopenharmony_ci * bridge_agpstat = PCI_AGP_STATUS from agp bridge. 58062306a36Sopenharmony_ci * vga_agpstat = PCI_AGP_STATUS from graphic card. 58162306a36Sopenharmony_ci */ 58262306a36Sopenharmony_cistatic void agp_v3_parse_one(u32 *requested_mode, u32 *bridge_agpstat, u32 *vga_agpstat) 58362306a36Sopenharmony_ci{ 58462306a36Sopenharmony_ci u32 origbridge=*bridge_agpstat, origvga=*vga_agpstat; 58562306a36Sopenharmony_ci u32 tmp; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci if (*requested_mode & AGP3_RESERVED_MASK) { 58862306a36Sopenharmony_ci printk(KERN_INFO PFX "reserved bits set (%x) in mode 0x%x. Fixed.\n", 58962306a36Sopenharmony_ci *requested_mode & AGP3_RESERVED_MASK, *requested_mode); 59062306a36Sopenharmony_ci *requested_mode &= ~AGP3_RESERVED_MASK; 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci /* Check the speed bits make sense. */ 59462306a36Sopenharmony_ci tmp = *requested_mode & 7; 59562306a36Sopenharmony_ci if (tmp == 0) { 59662306a36Sopenharmony_ci printk(KERN_INFO PFX "%s tried to set rate=x0. Setting to AGP3 x4 mode.\n", current->comm); 59762306a36Sopenharmony_ci *requested_mode |= AGPSTAT3_4X; 59862306a36Sopenharmony_ci } 59962306a36Sopenharmony_ci if (tmp >= 3) { 60062306a36Sopenharmony_ci printk(KERN_INFO PFX "%s tried to set rate=x%d. Setting to AGP3 x8 mode.\n", current->comm, tmp * 4); 60162306a36Sopenharmony_ci *requested_mode = (*requested_mode & ~7) | AGPSTAT3_8X; 60262306a36Sopenharmony_ci } 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci /* ARQSZ - Set the value to the maximum one. 60562306a36Sopenharmony_ci * Don't allow the mode register to override values. */ 60662306a36Sopenharmony_ci *bridge_agpstat = ((*bridge_agpstat & ~AGPSTAT_ARQSZ) | 60762306a36Sopenharmony_ci max_t(u32,(*bridge_agpstat & AGPSTAT_ARQSZ),(*vga_agpstat & AGPSTAT_ARQSZ))); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci /* Calibration cycle. 61062306a36Sopenharmony_ci * Don't allow the mode register to override values. */ 61162306a36Sopenharmony_ci *bridge_agpstat = ((*bridge_agpstat & ~AGPSTAT_CAL_MASK) | 61262306a36Sopenharmony_ci min_t(u32,(*bridge_agpstat & AGPSTAT_CAL_MASK),(*vga_agpstat & AGPSTAT_CAL_MASK))); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci /* SBA *must* be supported for AGP v3 */ 61562306a36Sopenharmony_ci *bridge_agpstat |= AGPSTAT_SBA; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci /* 61862306a36Sopenharmony_ci * Set speed. 61962306a36Sopenharmony_ci * Check for invalid speeds. This can happen when applications 62062306a36Sopenharmony_ci * written before the AGP 3.0 standard pass AGP2.x modes to AGP3 hardware 62162306a36Sopenharmony_ci */ 62262306a36Sopenharmony_ci if (*requested_mode & AGPSTAT_MODE_3_0) { 62362306a36Sopenharmony_ci /* 62462306a36Sopenharmony_ci * Caller hasn't a clue what it is doing. Bridge is in 3.0 mode, 62562306a36Sopenharmony_ci * have been passed a 3.0 mode, but with 2.x speed bits set. 62662306a36Sopenharmony_ci * AGP2.x 4x -> AGP3.0 4x. 62762306a36Sopenharmony_ci */ 62862306a36Sopenharmony_ci if (*requested_mode & AGPSTAT2_4X) { 62962306a36Sopenharmony_ci printk(KERN_INFO PFX "%s passes broken AGP3 flags (%x). Fixed.\n", 63062306a36Sopenharmony_ci current->comm, *requested_mode); 63162306a36Sopenharmony_ci *requested_mode &= ~AGPSTAT2_4X; 63262306a36Sopenharmony_ci *requested_mode |= AGPSTAT3_4X; 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci } else { 63562306a36Sopenharmony_ci /* 63662306a36Sopenharmony_ci * The caller doesn't know what they are doing. We are in 3.0 mode, 63762306a36Sopenharmony_ci * but have been passed an AGP 2.x mode. 63862306a36Sopenharmony_ci * Convert AGP 1x,2x,4x -> AGP 3.0 4x. 63962306a36Sopenharmony_ci */ 64062306a36Sopenharmony_ci printk(KERN_INFO PFX "%s passes broken AGP2 flags (%x) in AGP3 mode. Fixed.\n", 64162306a36Sopenharmony_ci current->comm, *requested_mode); 64262306a36Sopenharmony_ci *requested_mode &= ~(AGPSTAT2_4X | AGPSTAT2_2X | AGPSTAT2_1X); 64362306a36Sopenharmony_ci *requested_mode |= AGPSTAT3_4X; 64462306a36Sopenharmony_ci } 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci if (*requested_mode & AGPSTAT3_8X) { 64762306a36Sopenharmony_ci if (!(*bridge_agpstat & AGPSTAT3_8X)) { 64862306a36Sopenharmony_ci *bridge_agpstat &= ~(AGPSTAT3_8X | AGPSTAT3_RSVD); 64962306a36Sopenharmony_ci *bridge_agpstat |= AGPSTAT3_4X; 65062306a36Sopenharmony_ci printk(KERN_INFO PFX "%s requested AGPx8 but bridge not capable.\n", current->comm); 65162306a36Sopenharmony_ci return; 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci if (!(*vga_agpstat & AGPSTAT3_8X)) { 65462306a36Sopenharmony_ci *bridge_agpstat &= ~(AGPSTAT3_8X | AGPSTAT3_RSVD); 65562306a36Sopenharmony_ci *bridge_agpstat |= AGPSTAT3_4X; 65662306a36Sopenharmony_ci printk(KERN_INFO PFX "%s requested AGPx8 but graphic card not capable.\n", current->comm); 65762306a36Sopenharmony_ci return; 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci /* All set, bridge & device can do AGP x8*/ 66062306a36Sopenharmony_ci *bridge_agpstat &= ~(AGPSTAT3_4X | AGPSTAT3_RSVD); 66162306a36Sopenharmony_ci goto done; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci } else if (*requested_mode & AGPSTAT3_4X) { 66462306a36Sopenharmony_ci *bridge_agpstat &= ~(AGPSTAT3_8X | AGPSTAT3_RSVD); 66562306a36Sopenharmony_ci *bridge_agpstat |= AGPSTAT3_4X; 66662306a36Sopenharmony_ci goto done; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci } else { 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci /* 67162306a36Sopenharmony_ci * If we didn't specify an AGP mode, we see if both 67262306a36Sopenharmony_ci * the graphics card, and the bridge can do x8, and use if so. 67362306a36Sopenharmony_ci * If not, we fall back to x4 mode. 67462306a36Sopenharmony_ci */ 67562306a36Sopenharmony_ci if ((*bridge_agpstat & AGPSTAT3_8X) && (*vga_agpstat & AGPSTAT3_8X)) { 67662306a36Sopenharmony_ci printk(KERN_INFO PFX "No AGP mode specified. Setting to highest mode " 67762306a36Sopenharmony_ci "supported by bridge & card (x8).\n"); 67862306a36Sopenharmony_ci *bridge_agpstat &= ~(AGPSTAT3_4X | AGPSTAT3_RSVD); 67962306a36Sopenharmony_ci *vga_agpstat &= ~(AGPSTAT3_4X | AGPSTAT3_RSVD); 68062306a36Sopenharmony_ci } else { 68162306a36Sopenharmony_ci printk(KERN_INFO PFX "Fell back to AGPx4 mode because "); 68262306a36Sopenharmony_ci if (!(*bridge_agpstat & AGPSTAT3_8X)) { 68362306a36Sopenharmony_ci printk(KERN_INFO PFX "bridge couldn't do x8. bridge_agpstat:%x (orig=%x)\n", 68462306a36Sopenharmony_ci *bridge_agpstat, origbridge); 68562306a36Sopenharmony_ci *bridge_agpstat &= ~(AGPSTAT3_8X | AGPSTAT3_RSVD); 68662306a36Sopenharmony_ci *bridge_agpstat |= AGPSTAT3_4X; 68762306a36Sopenharmony_ci } 68862306a36Sopenharmony_ci if (!(*vga_agpstat & AGPSTAT3_8X)) { 68962306a36Sopenharmony_ci printk(KERN_INFO PFX "graphics card couldn't do x8. vga_agpstat:%x (orig=%x)\n", 69062306a36Sopenharmony_ci *vga_agpstat, origvga); 69162306a36Sopenharmony_ci *vga_agpstat &= ~(AGPSTAT3_8X | AGPSTAT3_RSVD); 69262306a36Sopenharmony_ci *vga_agpstat |= AGPSTAT3_4X; 69362306a36Sopenharmony_ci } 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ci } 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_cidone: 69862306a36Sopenharmony_ci /* Apply any errata. */ 69962306a36Sopenharmony_ci if (agp_bridge->flags & AGP_ERRATA_FASTWRITES) 70062306a36Sopenharmony_ci *bridge_agpstat &= ~AGPSTAT_FW; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci if (agp_bridge->flags & AGP_ERRATA_SBA) 70362306a36Sopenharmony_ci *bridge_agpstat &= ~AGPSTAT_SBA; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci if (agp_bridge->flags & AGP_ERRATA_1X) { 70662306a36Sopenharmony_ci *bridge_agpstat &= ~(AGPSTAT2_2X | AGPSTAT2_4X); 70762306a36Sopenharmony_ci *bridge_agpstat |= AGPSTAT2_1X; 70862306a36Sopenharmony_ci } 70962306a36Sopenharmony_ci} 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci/** 71362306a36Sopenharmony_ci * agp_collect_device_status - determine correct agp_cmd from various agp_stat's 71462306a36Sopenharmony_ci * @bridge: an agp_bridge_data struct allocated for the AGP host bridge. 71562306a36Sopenharmony_ci * @requested_mode: requested agp_stat from userspace (Typically from X) 71662306a36Sopenharmony_ci * @bridge_agpstat: current agp_stat from AGP bridge. 71762306a36Sopenharmony_ci * 71862306a36Sopenharmony_ci * This function will hunt for an AGP graphics card, and try to match 71962306a36Sopenharmony_ci * the requested mode to the capabilities of both the bridge and the card. 72062306a36Sopenharmony_ci */ 72162306a36Sopenharmony_ciu32 agp_collect_device_status(struct agp_bridge_data *bridge, u32 requested_mode, u32 bridge_agpstat) 72262306a36Sopenharmony_ci{ 72362306a36Sopenharmony_ci struct pci_dev *device = NULL; 72462306a36Sopenharmony_ci u32 vga_agpstat; 72562306a36Sopenharmony_ci u8 cap_ptr; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci for (;;) { 72862306a36Sopenharmony_ci device = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, device); 72962306a36Sopenharmony_ci if (!device) { 73062306a36Sopenharmony_ci printk(KERN_INFO PFX "Couldn't find an AGP VGA controller.\n"); 73162306a36Sopenharmony_ci return 0; 73262306a36Sopenharmony_ci } 73362306a36Sopenharmony_ci cap_ptr = pci_find_capability(device, PCI_CAP_ID_AGP); 73462306a36Sopenharmony_ci if (cap_ptr) 73562306a36Sopenharmony_ci break; 73662306a36Sopenharmony_ci } 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci /* 73962306a36Sopenharmony_ci * Ok, here we have a AGP device. Disable impossible 74062306a36Sopenharmony_ci * settings, and adjust the readqueue to the minimum. 74162306a36Sopenharmony_ci */ 74262306a36Sopenharmony_ci pci_read_config_dword(device, cap_ptr+PCI_AGP_STATUS, &vga_agpstat); 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci /* adjust RQ depth */ 74562306a36Sopenharmony_ci bridge_agpstat = ((bridge_agpstat & ~AGPSTAT_RQ_DEPTH) | 74662306a36Sopenharmony_ci min_t(u32, (requested_mode & AGPSTAT_RQ_DEPTH), 74762306a36Sopenharmony_ci min_t(u32, (bridge_agpstat & AGPSTAT_RQ_DEPTH), (vga_agpstat & AGPSTAT_RQ_DEPTH)))); 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci /* disable FW if it's not supported */ 75062306a36Sopenharmony_ci if (!((bridge_agpstat & AGPSTAT_FW) && 75162306a36Sopenharmony_ci (vga_agpstat & AGPSTAT_FW) && 75262306a36Sopenharmony_ci (requested_mode & AGPSTAT_FW))) 75362306a36Sopenharmony_ci bridge_agpstat &= ~AGPSTAT_FW; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci /* Check to see if we are operating in 3.0 mode */ 75662306a36Sopenharmony_ci if (agp_bridge->mode & AGPSTAT_MODE_3_0) 75762306a36Sopenharmony_ci agp_v3_parse_one(&requested_mode, &bridge_agpstat, &vga_agpstat); 75862306a36Sopenharmony_ci else 75962306a36Sopenharmony_ci agp_v2_parse_one(&requested_mode, &bridge_agpstat, &vga_agpstat); 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci pci_dev_put(device); 76262306a36Sopenharmony_ci return bridge_agpstat; 76362306a36Sopenharmony_ci} 76462306a36Sopenharmony_ciEXPORT_SYMBOL(agp_collect_device_status); 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_civoid agp_device_command(u32 bridge_agpstat, bool agp_v3) 76862306a36Sopenharmony_ci{ 76962306a36Sopenharmony_ci struct pci_dev *device = NULL; 77062306a36Sopenharmony_ci int mode; 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci mode = bridge_agpstat & 0x7; 77362306a36Sopenharmony_ci if (agp_v3) 77462306a36Sopenharmony_ci mode *= 4; 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci for_each_pci_dev(device) { 77762306a36Sopenharmony_ci u8 agp = pci_find_capability(device, PCI_CAP_ID_AGP); 77862306a36Sopenharmony_ci if (!agp) 77962306a36Sopenharmony_ci continue; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci dev_info(&device->dev, "putting AGP V%d device into %dx mode\n", 78262306a36Sopenharmony_ci agp_v3 ? 3 : 2, mode); 78362306a36Sopenharmony_ci pci_write_config_dword(device, agp + PCI_AGP_COMMAND, bridge_agpstat); 78462306a36Sopenharmony_ci } 78562306a36Sopenharmony_ci} 78662306a36Sopenharmony_ciEXPORT_SYMBOL(agp_device_command); 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_civoid get_agp_version(struct agp_bridge_data *bridge) 79062306a36Sopenharmony_ci{ 79162306a36Sopenharmony_ci u32 ncapid; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci /* Exit early if already set by errata workarounds. */ 79462306a36Sopenharmony_ci if (bridge->major_version != 0) 79562306a36Sopenharmony_ci return; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci pci_read_config_dword(bridge->dev, bridge->capndx, &ncapid); 79862306a36Sopenharmony_ci bridge->major_version = (ncapid >> AGP_MAJOR_VERSION_SHIFT) & 0xf; 79962306a36Sopenharmony_ci bridge->minor_version = (ncapid >> AGP_MINOR_VERSION_SHIFT) & 0xf; 80062306a36Sopenharmony_ci} 80162306a36Sopenharmony_ciEXPORT_SYMBOL(get_agp_version); 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_civoid agp_generic_enable(struct agp_bridge_data *bridge, u32 requested_mode) 80562306a36Sopenharmony_ci{ 80662306a36Sopenharmony_ci u32 bridge_agpstat, temp; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci get_agp_version(agp_bridge); 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci dev_info(&agp_bridge->dev->dev, "AGP %d.%d bridge\n", 81162306a36Sopenharmony_ci agp_bridge->major_version, agp_bridge->minor_version); 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci pci_read_config_dword(agp_bridge->dev, 81462306a36Sopenharmony_ci agp_bridge->capndx + PCI_AGP_STATUS, &bridge_agpstat); 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci bridge_agpstat = agp_collect_device_status(agp_bridge, requested_mode, bridge_agpstat); 81762306a36Sopenharmony_ci if (bridge_agpstat == 0) 81862306a36Sopenharmony_ci /* Something bad happened. FIXME: Return error code? */ 81962306a36Sopenharmony_ci return; 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci bridge_agpstat |= AGPSTAT_AGP_ENABLE; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci /* Do AGP version specific frobbing. */ 82462306a36Sopenharmony_ci if (bridge->major_version >= 3) { 82562306a36Sopenharmony_ci if (bridge->mode & AGPSTAT_MODE_3_0) { 82662306a36Sopenharmony_ci /* If we have 3.5, we can do the isoch stuff. */ 82762306a36Sopenharmony_ci if (bridge->minor_version >= 5) 82862306a36Sopenharmony_ci agp_3_5_enable(bridge); 82962306a36Sopenharmony_ci agp_device_command(bridge_agpstat, true); 83062306a36Sopenharmony_ci return; 83162306a36Sopenharmony_ci } else { 83262306a36Sopenharmony_ci /* Disable calibration cycle in RX91<1> when not in AGP3.0 mode of operation.*/ 83362306a36Sopenharmony_ci bridge_agpstat &= ~(7<<10) ; 83462306a36Sopenharmony_ci pci_read_config_dword(bridge->dev, 83562306a36Sopenharmony_ci bridge->capndx+AGPCTRL, &temp); 83662306a36Sopenharmony_ci temp |= (1<<9); 83762306a36Sopenharmony_ci pci_write_config_dword(bridge->dev, 83862306a36Sopenharmony_ci bridge->capndx+AGPCTRL, temp); 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci dev_info(&bridge->dev->dev, "bridge is in legacy mode, falling back to 2.x\n"); 84162306a36Sopenharmony_ci } 84262306a36Sopenharmony_ci } 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci /* AGP v<3 */ 84562306a36Sopenharmony_ci agp_device_command(bridge_agpstat, false); 84662306a36Sopenharmony_ci} 84762306a36Sopenharmony_ciEXPORT_SYMBOL(agp_generic_enable); 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ciint agp_generic_create_gatt_table(struct agp_bridge_data *bridge) 85162306a36Sopenharmony_ci{ 85262306a36Sopenharmony_ci char *table; 85362306a36Sopenharmony_ci char *table_end; 85462306a36Sopenharmony_ci int page_order; 85562306a36Sopenharmony_ci int num_entries; 85662306a36Sopenharmony_ci int i; 85762306a36Sopenharmony_ci void *temp; 85862306a36Sopenharmony_ci struct page *page; 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci /* The generic routines can't handle 2 level gatt's */ 86162306a36Sopenharmony_ci if (bridge->driver->size_type == LVL2_APER_SIZE) 86262306a36Sopenharmony_ci return -EINVAL; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci table = NULL; 86562306a36Sopenharmony_ci i = bridge->aperture_size_idx; 86662306a36Sopenharmony_ci temp = bridge->current_size; 86762306a36Sopenharmony_ci page_order = num_entries = 0; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci if (bridge->driver->size_type != FIXED_APER_SIZE) { 87062306a36Sopenharmony_ci do { 87162306a36Sopenharmony_ci switch (bridge->driver->size_type) { 87262306a36Sopenharmony_ci case U8_APER_SIZE: 87362306a36Sopenharmony_ci page_order = 87462306a36Sopenharmony_ci A_SIZE_8(temp)->page_order; 87562306a36Sopenharmony_ci num_entries = 87662306a36Sopenharmony_ci A_SIZE_8(temp)->num_entries; 87762306a36Sopenharmony_ci break; 87862306a36Sopenharmony_ci case U16_APER_SIZE: 87962306a36Sopenharmony_ci page_order = A_SIZE_16(temp)->page_order; 88062306a36Sopenharmony_ci num_entries = A_SIZE_16(temp)->num_entries; 88162306a36Sopenharmony_ci break; 88262306a36Sopenharmony_ci case U32_APER_SIZE: 88362306a36Sopenharmony_ci page_order = A_SIZE_32(temp)->page_order; 88462306a36Sopenharmony_ci num_entries = A_SIZE_32(temp)->num_entries; 88562306a36Sopenharmony_ci break; 88662306a36Sopenharmony_ci /* This case will never really happen. */ 88762306a36Sopenharmony_ci case FIXED_APER_SIZE: 88862306a36Sopenharmony_ci case LVL2_APER_SIZE: 88962306a36Sopenharmony_ci default: 89062306a36Sopenharmony_ci page_order = num_entries = 0; 89162306a36Sopenharmony_ci break; 89262306a36Sopenharmony_ci } 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci table = alloc_gatt_pages(page_order); 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci if (table == NULL) { 89762306a36Sopenharmony_ci i++; 89862306a36Sopenharmony_ci switch (bridge->driver->size_type) { 89962306a36Sopenharmony_ci case U8_APER_SIZE: 90062306a36Sopenharmony_ci bridge->current_size = A_IDX8(bridge); 90162306a36Sopenharmony_ci break; 90262306a36Sopenharmony_ci case U16_APER_SIZE: 90362306a36Sopenharmony_ci bridge->current_size = A_IDX16(bridge); 90462306a36Sopenharmony_ci break; 90562306a36Sopenharmony_ci case U32_APER_SIZE: 90662306a36Sopenharmony_ci bridge->current_size = A_IDX32(bridge); 90762306a36Sopenharmony_ci break; 90862306a36Sopenharmony_ci /* These cases will never really happen. */ 90962306a36Sopenharmony_ci case FIXED_APER_SIZE: 91062306a36Sopenharmony_ci case LVL2_APER_SIZE: 91162306a36Sopenharmony_ci default: 91262306a36Sopenharmony_ci break; 91362306a36Sopenharmony_ci } 91462306a36Sopenharmony_ci temp = bridge->current_size; 91562306a36Sopenharmony_ci } else { 91662306a36Sopenharmony_ci bridge->aperture_size_idx = i; 91762306a36Sopenharmony_ci } 91862306a36Sopenharmony_ci } while (!table && (i < bridge->driver->num_aperture_sizes)); 91962306a36Sopenharmony_ci } else { 92062306a36Sopenharmony_ci page_order = ((struct aper_size_info_fixed *) temp)->page_order; 92162306a36Sopenharmony_ci num_entries = ((struct aper_size_info_fixed *) temp)->num_entries; 92262306a36Sopenharmony_ci table = alloc_gatt_pages(page_order); 92362306a36Sopenharmony_ci } 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci if (table == NULL) 92662306a36Sopenharmony_ci return -ENOMEM; 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1); 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci for (page = virt_to_page(table); page <= virt_to_page(table_end); page++) 93162306a36Sopenharmony_ci SetPageReserved(page); 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci bridge->gatt_table_real = (u32 *) table; 93462306a36Sopenharmony_ci agp_gatt_table = (void *)table; 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci bridge->driver->cache_flush(); 93762306a36Sopenharmony_ci#ifdef CONFIG_X86 93862306a36Sopenharmony_ci if (set_memory_uc((unsigned long)table, 1 << page_order)) 93962306a36Sopenharmony_ci printk(KERN_WARNING "Could not set GATT table memory to UC!\n"); 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci bridge->gatt_table = (u32 __iomem *)table; 94262306a36Sopenharmony_ci#else 94362306a36Sopenharmony_ci bridge->gatt_table = ioremap(virt_to_phys(table), 94462306a36Sopenharmony_ci (PAGE_SIZE * (1 << page_order))); 94562306a36Sopenharmony_ci bridge->driver->cache_flush(); 94662306a36Sopenharmony_ci#endif 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci if (bridge->gatt_table == NULL) { 94962306a36Sopenharmony_ci for (page = virt_to_page(table); page <= virt_to_page(table_end); page++) 95062306a36Sopenharmony_ci ClearPageReserved(page); 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci free_gatt_pages(table, page_order); 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci return -ENOMEM; 95562306a36Sopenharmony_ci } 95662306a36Sopenharmony_ci bridge->gatt_bus_addr = virt_to_phys(bridge->gatt_table_real); 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci /* AK: bogus, should encode addresses > 4GB */ 95962306a36Sopenharmony_ci for (i = 0; i < num_entries; i++) { 96062306a36Sopenharmony_ci writel(bridge->scratch_page, bridge->gatt_table+i); 96162306a36Sopenharmony_ci readl(bridge->gatt_table+i); /* PCI Posting. */ 96262306a36Sopenharmony_ci } 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci return 0; 96562306a36Sopenharmony_ci} 96662306a36Sopenharmony_ciEXPORT_SYMBOL(agp_generic_create_gatt_table); 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ciint agp_generic_free_gatt_table(struct agp_bridge_data *bridge) 96962306a36Sopenharmony_ci{ 97062306a36Sopenharmony_ci int page_order; 97162306a36Sopenharmony_ci char *table, *table_end; 97262306a36Sopenharmony_ci void *temp; 97362306a36Sopenharmony_ci struct page *page; 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci temp = bridge->current_size; 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci switch (bridge->driver->size_type) { 97862306a36Sopenharmony_ci case U8_APER_SIZE: 97962306a36Sopenharmony_ci page_order = A_SIZE_8(temp)->page_order; 98062306a36Sopenharmony_ci break; 98162306a36Sopenharmony_ci case U16_APER_SIZE: 98262306a36Sopenharmony_ci page_order = A_SIZE_16(temp)->page_order; 98362306a36Sopenharmony_ci break; 98462306a36Sopenharmony_ci case U32_APER_SIZE: 98562306a36Sopenharmony_ci page_order = A_SIZE_32(temp)->page_order; 98662306a36Sopenharmony_ci break; 98762306a36Sopenharmony_ci case FIXED_APER_SIZE: 98862306a36Sopenharmony_ci page_order = A_SIZE_FIX(temp)->page_order; 98962306a36Sopenharmony_ci break; 99062306a36Sopenharmony_ci case LVL2_APER_SIZE: 99162306a36Sopenharmony_ci /* The generic routines can't deal with 2 level gatt's */ 99262306a36Sopenharmony_ci return -EINVAL; 99362306a36Sopenharmony_ci default: 99462306a36Sopenharmony_ci page_order = 0; 99562306a36Sopenharmony_ci break; 99662306a36Sopenharmony_ci } 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci /* Do not worry about freeing memory, because if this is 99962306a36Sopenharmony_ci * called, then all agp memory is deallocated and removed 100062306a36Sopenharmony_ci * from the table. */ 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci#ifdef CONFIG_X86 100362306a36Sopenharmony_ci set_memory_wb((unsigned long)bridge->gatt_table, 1 << page_order); 100462306a36Sopenharmony_ci#else 100562306a36Sopenharmony_ci iounmap(bridge->gatt_table); 100662306a36Sopenharmony_ci#endif 100762306a36Sopenharmony_ci table = (char *) bridge->gatt_table_real; 100862306a36Sopenharmony_ci table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1); 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci for (page = virt_to_page(table); page <= virt_to_page(table_end); page++) 101162306a36Sopenharmony_ci ClearPageReserved(page); 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci free_gatt_pages(bridge->gatt_table_real, page_order); 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci agp_gatt_table = NULL; 101662306a36Sopenharmony_ci bridge->gatt_table = NULL; 101762306a36Sopenharmony_ci bridge->gatt_table_real = NULL; 101862306a36Sopenharmony_ci bridge->gatt_bus_addr = 0; 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci return 0; 102162306a36Sopenharmony_ci} 102262306a36Sopenharmony_ciEXPORT_SYMBOL(agp_generic_free_gatt_table); 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ciint agp_generic_insert_memory(struct agp_memory * mem, off_t pg_start, int type) 102662306a36Sopenharmony_ci{ 102762306a36Sopenharmony_ci int num_entries; 102862306a36Sopenharmony_ci size_t i; 102962306a36Sopenharmony_ci off_t j; 103062306a36Sopenharmony_ci void *temp; 103162306a36Sopenharmony_ci struct agp_bridge_data *bridge; 103262306a36Sopenharmony_ci int mask_type; 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci bridge = mem->bridge; 103562306a36Sopenharmony_ci if (!bridge) 103662306a36Sopenharmony_ci return -EINVAL; 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci if (mem->page_count == 0) 103962306a36Sopenharmony_ci return 0; 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci temp = bridge->current_size; 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci switch (bridge->driver->size_type) { 104462306a36Sopenharmony_ci case U8_APER_SIZE: 104562306a36Sopenharmony_ci num_entries = A_SIZE_8(temp)->num_entries; 104662306a36Sopenharmony_ci break; 104762306a36Sopenharmony_ci case U16_APER_SIZE: 104862306a36Sopenharmony_ci num_entries = A_SIZE_16(temp)->num_entries; 104962306a36Sopenharmony_ci break; 105062306a36Sopenharmony_ci case U32_APER_SIZE: 105162306a36Sopenharmony_ci num_entries = A_SIZE_32(temp)->num_entries; 105262306a36Sopenharmony_ci break; 105362306a36Sopenharmony_ci case FIXED_APER_SIZE: 105462306a36Sopenharmony_ci num_entries = A_SIZE_FIX(temp)->num_entries; 105562306a36Sopenharmony_ci break; 105662306a36Sopenharmony_ci case LVL2_APER_SIZE: 105762306a36Sopenharmony_ci /* The generic routines can't deal with 2 level gatt's */ 105862306a36Sopenharmony_ci return -EINVAL; 105962306a36Sopenharmony_ci default: 106062306a36Sopenharmony_ci num_entries = 0; 106162306a36Sopenharmony_ci break; 106262306a36Sopenharmony_ci } 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci num_entries -= agp_memory_reserved/PAGE_SIZE; 106562306a36Sopenharmony_ci if (num_entries < 0) num_entries = 0; 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci if (type != mem->type) 106862306a36Sopenharmony_ci return -EINVAL; 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci mask_type = bridge->driver->agp_type_to_mask_type(bridge, type); 107162306a36Sopenharmony_ci if (mask_type != 0) { 107262306a36Sopenharmony_ci /* The generic routines know nothing of memory types */ 107362306a36Sopenharmony_ci return -EINVAL; 107462306a36Sopenharmony_ci } 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci if (((pg_start + mem->page_count) > num_entries) || 107762306a36Sopenharmony_ci ((pg_start + mem->page_count) < pg_start)) 107862306a36Sopenharmony_ci return -EINVAL; 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci j = pg_start; 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci while (j < (pg_start + mem->page_count)) { 108362306a36Sopenharmony_ci if (!PGE_EMPTY(bridge, readl(bridge->gatt_table+j))) 108462306a36Sopenharmony_ci return -EBUSY; 108562306a36Sopenharmony_ci j++; 108662306a36Sopenharmony_ci } 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci if (!mem->is_flushed) { 108962306a36Sopenharmony_ci bridge->driver->cache_flush(); 109062306a36Sopenharmony_ci mem->is_flushed = true; 109162306a36Sopenharmony_ci } 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { 109462306a36Sopenharmony_ci writel(bridge->driver->mask_memory(bridge, 109562306a36Sopenharmony_ci page_to_phys(mem->pages[i]), 109662306a36Sopenharmony_ci mask_type), 109762306a36Sopenharmony_ci bridge->gatt_table+j); 109862306a36Sopenharmony_ci } 109962306a36Sopenharmony_ci readl(bridge->gatt_table+j-1); /* PCI Posting. */ 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci bridge->driver->tlb_flush(mem); 110262306a36Sopenharmony_ci return 0; 110362306a36Sopenharmony_ci} 110462306a36Sopenharmony_ciEXPORT_SYMBOL(agp_generic_insert_memory); 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ciint agp_generic_remove_memory(struct agp_memory *mem, off_t pg_start, int type) 110862306a36Sopenharmony_ci{ 110962306a36Sopenharmony_ci size_t i; 111062306a36Sopenharmony_ci struct agp_bridge_data *bridge; 111162306a36Sopenharmony_ci int mask_type, num_entries; 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci bridge = mem->bridge; 111462306a36Sopenharmony_ci if (!bridge) 111562306a36Sopenharmony_ci return -EINVAL; 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci if (mem->page_count == 0) 111862306a36Sopenharmony_ci return 0; 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci if (type != mem->type) 112162306a36Sopenharmony_ci return -EINVAL; 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci num_entries = agp_num_entries(); 112462306a36Sopenharmony_ci if (((pg_start + mem->page_count) > num_entries) || 112562306a36Sopenharmony_ci ((pg_start + mem->page_count) < pg_start)) 112662306a36Sopenharmony_ci return -EINVAL; 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci mask_type = bridge->driver->agp_type_to_mask_type(bridge, type); 112962306a36Sopenharmony_ci if (mask_type != 0) { 113062306a36Sopenharmony_ci /* The generic routines know nothing of memory types */ 113162306a36Sopenharmony_ci return -EINVAL; 113262306a36Sopenharmony_ci } 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci /* AK: bogus, should encode addresses > 4GB */ 113562306a36Sopenharmony_ci for (i = pg_start; i < (mem->page_count + pg_start); i++) { 113662306a36Sopenharmony_ci writel(bridge->scratch_page, bridge->gatt_table+i); 113762306a36Sopenharmony_ci } 113862306a36Sopenharmony_ci readl(bridge->gatt_table+i-1); /* PCI Posting. */ 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci bridge->driver->tlb_flush(mem); 114162306a36Sopenharmony_ci return 0; 114262306a36Sopenharmony_ci} 114362306a36Sopenharmony_ciEXPORT_SYMBOL(agp_generic_remove_memory); 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_cistruct agp_memory *agp_generic_alloc_by_type(size_t page_count, int type) 114662306a36Sopenharmony_ci{ 114762306a36Sopenharmony_ci return NULL; 114862306a36Sopenharmony_ci} 114962306a36Sopenharmony_ciEXPORT_SYMBOL(agp_generic_alloc_by_type); 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_civoid agp_generic_free_by_type(struct agp_memory *curr) 115262306a36Sopenharmony_ci{ 115362306a36Sopenharmony_ci agp_free_page_array(curr); 115462306a36Sopenharmony_ci agp_free_key(curr->key); 115562306a36Sopenharmony_ci kfree(curr); 115662306a36Sopenharmony_ci} 115762306a36Sopenharmony_ciEXPORT_SYMBOL(agp_generic_free_by_type); 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_cistruct agp_memory *agp_generic_alloc_user(size_t page_count, int type) 116062306a36Sopenharmony_ci{ 116162306a36Sopenharmony_ci struct agp_memory *new; 116262306a36Sopenharmony_ci int i; 116362306a36Sopenharmony_ci int pages; 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci pages = (page_count + ENTRIES_PER_PAGE - 1) / ENTRIES_PER_PAGE; 116662306a36Sopenharmony_ci new = agp_create_user_memory(page_count); 116762306a36Sopenharmony_ci if (new == NULL) 116862306a36Sopenharmony_ci return NULL; 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci for (i = 0; i < page_count; i++) 117162306a36Sopenharmony_ci new->pages[i] = NULL; 117262306a36Sopenharmony_ci new->page_count = 0; 117362306a36Sopenharmony_ci new->type = type; 117462306a36Sopenharmony_ci new->num_scratch_pages = pages; 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_ci return new; 117762306a36Sopenharmony_ci} 117862306a36Sopenharmony_ciEXPORT_SYMBOL(agp_generic_alloc_user); 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci/* 118162306a36Sopenharmony_ci * Basic Page Allocation Routines - 118262306a36Sopenharmony_ci * These routines handle page allocation and by default they reserve the allocated 118362306a36Sopenharmony_ci * memory. They also handle incrementing the current_memory_agp value, Which is checked 118462306a36Sopenharmony_ci * against a maximum value. 118562306a36Sopenharmony_ci */ 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ciint agp_generic_alloc_pages(struct agp_bridge_data *bridge, struct agp_memory *mem, size_t num_pages) 118862306a36Sopenharmony_ci{ 118962306a36Sopenharmony_ci struct page * page; 119062306a36Sopenharmony_ci int i, ret = -ENOMEM; 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci for (i = 0; i < num_pages; i++) { 119362306a36Sopenharmony_ci page = alloc_page(GFP_KERNEL | GFP_DMA32 | __GFP_ZERO); 119462306a36Sopenharmony_ci /* agp_free_memory() needs gart address */ 119562306a36Sopenharmony_ci if (page == NULL) 119662306a36Sopenharmony_ci goto out; 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci#ifndef CONFIG_X86 119962306a36Sopenharmony_ci map_page_into_agp(page); 120062306a36Sopenharmony_ci#endif 120162306a36Sopenharmony_ci get_page(page); 120262306a36Sopenharmony_ci atomic_inc(&agp_bridge->current_memory_agp); 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci mem->pages[i] = page; 120562306a36Sopenharmony_ci mem->page_count++; 120662306a36Sopenharmony_ci } 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci#ifdef CONFIG_X86 120962306a36Sopenharmony_ci set_pages_array_uc(mem->pages, num_pages); 121062306a36Sopenharmony_ci#endif 121162306a36Sopenharmony_ci ret = 0; 121262306a36Sopenharmony_ciout: 121362306a36Sopenharmony_ci return ret; 121462306a36Sopenharmony_ci} 121562306a36Sopenharmony_ciEXPORT_SYMBOL(agp_generic_alloc_pages); 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_cistruct page *agp_generic_alloc_page(struct agp_bridge_data *bridge) 121862306a36Sopenharmony_ci{ 121962306a36Sopenharmony_ci struct page * page; 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci page = alloc_page(GFP_KERNEL | GFP_DMA32 | __GFP_ZERO); 122262306a36Sopenharmony_ci if (page == NULL) 122362306a36Sopenharmony_ci return NULL; 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci map_page_into_agp(page); 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci get_page(page); 122862306a36Sopenharmony_ci atomic_inc(&agp_bridge->current_memory_agp); 122962306a36Sopenharmony_ci return page; 123062306a36Sopenharmony_ci} 123162306a36Sopenharmony_ciEXPORT_SYMBOL(agp_generic_alloc_page); 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_civoid agp_generic_destroy_pages(struct agp_memory *mem) 123462306a36Sopenharmony_ci{ 123562306a36Sopenharmony_ci int i; 123662306a36Sopenharmony_ci struct page *page; 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci if (!mem) 123962306a36Sopenharmony_ci return; 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci#ifdef CONFIG_X86 124262306a36Sopenharmony_ci set_pages_array_wb(mem->pages, mem->page_count); 124362306a36Sopenharmony_ci#endif 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci for (i = 0; i < mem->page_count; i++) { 124662306a36Sopenharmony_ci page = mem->pages[i]; 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci#ifndef CONFIG_X86 124962306a36Sopenharmony_ci unmap_page_from_agp(page); 125062306a36Sopenharmony_ci#endif 125162306a36Sopenharmony_ci put_page(page); 125262306a36Sopenharmony_ci __free_page(page); 125362306a36Sopenharmony_ci atomic_dec(&agp_bridge->current_memory_agp); 125462306a36Sopenharmony_ci mem->pages[i] = NULL; 125562306a36Sopenharmony_ci } 125662306a36Sopenharmony_ci} 125762306a36Sopenharmony_ciEXPORT_SYMBOL(agp_generic_destroy_pages); 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_civoid agp_generic_destroy_page(struct page *page, int flags) 126062306a36Sopenharmony_ci{ 126162306a36Sopenharmony_ci if (page == NULL) 126262306a36Sopenharmony_ci return; 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci if (flags & AGP_PAGE_DESTROY_UNMAP) 126562306a36Sopenharmony_ci unmap_page_from_agp(page); 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_ci if (flags & AGP_PAGE_DESTROY_FREE) { 126862306a36Sopenharmony_ci put_page(page); 126962306a36Sopenharmony_ci __free_page(page); 127062306a36Sopenharmony_ci atomic_dec(&agp_bridge->current_memory_agp); 127162306a36Sopenharmony_ci } 127262306a36Sopenharmony_ci} 127362306a36Sopenharmony_ciEXPORT_SYMBOL(agp_generic_destroy_page); 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_ci/* End Basic Page Allocation Routines */ 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci/** 127962306a36Sopenharmony_ci * agp_enable - initialise the agp point-to-point connection. 128062306a36Sopenharmony_ci * 128162306a36Sopenharmony_ci * @bridge: an agp_bridge_data struct allocated for the AGP host bridge. 128262306a36Sopenharmony_ci * @mode: agp mode register value to configure with. 128362306a36Sopenharmony_ci */ 128462306a36Sopenharmony_civoid agp_enable(struct agp_bridge_data *bridge, u32 mode) 128562306a36Sopenharmony_ci{ 128662306a36Sopenharmony_ci if (!bridge) 128762306a36Sopenharmony_ci return; 128862306a36Sopenharmony_ci bridge->driver->agp_enable(bridge, mode); 128962306a36Sopenharmony_ci} 129062306a36Sopenharmony_ciEXPORT_SYMBOL(agp_enable); 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci/* When we remove the global variable agp_bridge from all drivers 129362306a36Sopenharmony_ci * then agp_alloc_bridge and agp_generic_find_bridge need to be updated 129462306a36Sopenharmony_ci */ 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_cistruct agp_bridge_data *agp_generic_find_bridge(struct pci_dev *pdev) 129762306a36Sopenharmony_ci{ 129862306a36Sopenharmony_ci if (list_empty(&agp_bridges)) 129962306a36Sopenharmony_ci return NULL; 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci return agp_bridge; 130262306a36Sopenharmony_ci} 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_cistatic void ipi_handler(void *null) 130562306a36Sopenharmony_ci{ 130662306a36Sopenharmony_ci flush_agp_cache(); 130762306a36Sopenharmony_ci} 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_civoid global_cache_flush(void) 131062306a36Sopenharmony_ci{ 131162306a36Sopenharmony_ci on_each_cpu(ipi_handler, NULL, 1); 131262306a36Sopenharmony_ci} 131362306a36Sopenharmony_ciEXPORT_SYMBOL(global_cache_flush); 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ciunsigned long agp_generic_mask_memory(struct agp_bridge_data *bridge, 131662306a36Sopenharmony_ci dma_addr_t addr, int type) 131762306a36Sopenharmony_ci{ 131862306a36Sopenharmony_ci /* memory type is ignored in the generic routine */ 131962306a36Sopenharmony_ci if (bridge->driver->masks) 132062306a36Sopenharmony_ci return addr | bridge->driver->masks[0].mask; 132162306a36Sopenharmony_ci else 132262306a36Sopenharmony_ci return addr; 132362306a36Sopenharmony_ci} 132462306a36Sopenharmony_ciEXPORT_SYMBOL(agp_generic_mask_memory); 132562306a36Sopenharmony_ci 132662306a36Sopenharmony_ciint agp_generic_type_to_mask_type(struct agp_bridge_data *bridge, 132762306a36Sopenharmony_ci int type) 132862306a36Sopenharmony_ci{ 132962306a36Sopenharmony_ci if (type >= AGP_USER_TYPES) 133062306a36Sopenharmony_ci return 0; 133162306a36Sopenharmony_ci return type; 133262306a36Sopenharmony_ci} 133362306a36Sopenharmony_ciEXPORT_SYMBOL(agp_generic_type_to_mask_type); 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci/* 133662306a36Sopenharmony_ci * These functions are implemented according to the AGPv3 spec, 133762306a36Sopenharmony_ci * which covers implementation details that had previously been 133862306a36Sopenharmony_ci * left open. 133962306a36Sopenharmony_ci */ 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_ciint agp3_generic_fetch_size(void) 134262306a36Sopenharmony_ci{ 134362306a36Sopenharmony_ci u16 temp_size; 134462306a36Sopenharmony_ci int i; 134562306a36Sopenharmony_ci struct aper_size_info_16 *values; 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_ci pci_read_config_word(agp_bridge->dev, agp_bridge->capndx+AGPAPSIZE, &temp_size); 134862306a36Sopenharmony_ci values = A_SIZE_16(agp_bridge->driver->aperture_sizes); 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++) { 135162306a36Sopenharmony_ci if (temp_size == values[i].size_value) { 135262306a36Sopenharmony_ci agp_bridge->previous_size = 135362306a36Sopenharmony_ci agp_bridge->current_size = (void *) (values + i); 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci agp_bridge->aperture_size_idx = i; 135662306a36Sopenharmony_ci return values[i].size; 135762306a36Sopenharmony_ci } 135862306a36Sopenharmony_ci } 135962306a36Sopenharmony_ci return 0; 136062306a36Sopenharmony_ci} 136162306a36Sopenharmony_ciEXPORT_SYMBOL(agp3_generic_fetch_size); 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_civoid agp3_generic_tlbflush(struct agp_memory *mem) 136462306a36Sopenharmony_ci{ 136562306a36Sopenharmony_ci u32 ctrl; 136662306a36Sopenharmony_ci pci_read_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, &ctrl); 136762306a36Sopenharmony_ci pci_write_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, ctrl & ~AGPCTRL_GTLBEN); 136862306a36Sopenharmony_ci pci_write_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, ctrl); 136962306a36Sopenharmony_ci} 137062306a36Sopenharmony_ciEXPORT_SYMBOL(agp3_generic_tlbflush); 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_ciint agp3_generic_configure(void) 137362306a36Sopenharmony_ci{ 137462306a36Sopenharmony_ci u32 temp; 137562306a36Sopenharmony_ci struct aper_size_info_16 *current_size; 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci current_size = A_SIZE_16(agp_bridge->current_size); 137862306a36Sopenharmony_ci 137962306a36Sopenharmony_ci agp_bridge->gart_bus_addr = pci_bus_address(agp_bridge->dev, 138062306a36Sopenharmony_ci AGP_APERTURE_BAR); 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_ci /* set aperture size */ 138362306a36Sopenharmony_ci pci_write_config_word(agp_bridge->dev, agp_bridge->capndx+AGPAPSIZE, current_size->size_value); 138462306a36Sopenharmony_ci /* set gart pointer */ 138562306a36Sopenharmony_ci pci_write_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPGARTLO, agp_bridge->gatt_bus_addr); 138662306a36Sopenharmony_ci /* enable aperture and GTLB */ 138762306a36Sopenharmony_ci pci_read_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, &temp); 138862306a36Sopenharmony_ci pci_write_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, temp | AGPCTRL_APERENB | AGPCTRL_GTLBEN); 138962306a36Sopenharmony_ci return 0; 139062306a36Sopenharmony_ci} 139162306a36Sopenharmony_ciEXPORT_SYMBOL(agp3_generic_configure); 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_civoid agp3_generic_cleanup(void) 139462306a36Sopenharmony_ci{ 139562306a36Sopenharmony_ci u32 ctrl; 139662306a36Sopenharmony_ci pci_read_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, &ctrl); 139762306a36Sopenharmony_ci pci_write_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, ctrl & ~AGPCTRL_APERENB); 139862306a36Sopenharmony_ci} 139962306a36Sopenharmony_ciEXPORT_SYMBOL(agp3_generic_cleanup); 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_ciconst struct aper_size_info_16 agp3_generic_sizes[AGP_GENERIC_SIZES_ENTRIES] = 140262306a36Sopenharmony_ci{ 140362306a36Sopenharmony_ci {4096, 1048576, 10,0x000}, 140462306a36Sopenharmony_ci {2048, 524288, 9, 0x800}, 140562306a36Sopenharmony_ci {1024, 262144, 8, 0xc00}, 140662306a36Sopenharmony_ci { 512, 131072, 7, 0xe00}, 140762306a36Sopenharmony_ci { 256, 65536, 6, 0xf00}, 140862306a36Sopenharmony_ci { 128, 32768, 5, 0xf20}, 140962306a36Sopenharmony_ci { 64, 16384, 4, 0xf30}, 141062306a36Sopenharmony_ci { 32, 8192, 3, 0xf38}, 141162306a36Sopenharmony_ci { 16, 4096, 2, 0xf3c}, 141262306a36Sopenharmony_ci { 8, 2048, 1, 0xf3e}, 141362306a36Sopenharmony_ci { 4, 1024, 0, 0xf3f} 141462306a36Sopenharmony_ci}; 141562306a36Sopenharmony_ciEXPORT_SYMBOL(agp3_generic_sizes); 141662306a36Sopenharmony_ci 1417