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> 40e8edc6e0SAlexey Dobriyan #include <linux/sched.h> 411da177e4SLinus Torvalds #include <asm/io.h> 421da177e4SLinus Torvalds #include <asm/cacheflush.h> 431da177e4SLinus Torvalds #include <asm/pgtable.h> 441da177e4SLinus Torvalds #include "agp.h" 451da177e4SLinus Torvalds 461da177e4SLinus Torvalds __u32 *agp_gatt_table; 471da177e4SLinus Torvalds int agp_memory_reserved; 481da177e4SLinus Torvalds 491da177e4SLinus Torvalds /* 501da177e4SLinus Torvalds * Needed by the Nforce GART driver for the time being. Would be 511da177e4SLinus Torvalds * nice to do this some other way instead of needing this export. 521da177e4SLinus Torvalds */ 531da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(agp_memory_reserved); 541da177e4SLinus Torvalds 551da177e4SLinus Torvalds /* 561da177e4SLinus Torvalds * Generic routines for handling agp_memory structures - 571da177e4SLinus Torvalds * They use the basic page allocation routines to do the brunt of the work. 581da177e4SLinus Torvalds */ 591da177e4SLinus Torvalds 601da177e4SLinus Torvalds void agp_free_key(int key) 611da177e4SLinus Torvalds { 621da177e4SLinus Torvalds if (key < 0) 631da177e4SLinus Torvalds return; 641da177e4SLinus Torvalds 651da177e4SLinus Torvalds if (key < MAXKEY) 661da177e4SLinus Torvalds clear_bit(key, agp_bridge->key_list); 671da177e4SLinus Torvalds } 681da177e4SLinus Torvalds EXPORT_SYMBOL(agp_free_key); 691da177e4SLinus Torvalds 701da177e4SLinus Torvalds 711da177e4SLinus Torvalds static int agp_get_key(void) 721da177e4SLinus Torvalds { 731da177e4SLinus Torvalds int bit; 741da177e4SLinus Torvalds 751da177e4SLinus Torvalds bit = find_first_zero_bit(agp_bridge->key_list, MAXKEY); 761da177e4SLinus Torvalds if (bit < MAXKEY) { 771da177e4SLinus Torvalds set_bit(bit, agp_bridge->key_list); 781da177e4SLinus Torvalds return bit; 791da177e4SLinus Torvalds } 801da177e4SLinus Torvalds return -1; 811da177e4SLinus Torvalds } 821da177e4SLinus Torvalds 83a13af4b4SDave Airlie void agp_flush_chipset(struct agp_bridge_data *bridge) 84a13af4b4SDave Airlie { 85a13af4b4SDave Airlie if (bridge->driver->chipset_flush) 86a13af4b4SDave Airlie bridge->driver->chipset_flush(bridge); 87a13af4b4SDave Airlie } 88a13af4b4SDave Airlie EXPORT_SYMBOL(agp_flush_chipset); 89a13af4b4SDave Airlie 90a030ce44SThomas Hellstrom /* 91a030ce44SThomas Hellstrom * Use kmalloc if possible for the page list. Otherwise fall back to 92a030ce44SThomas Hellstrom * vmalloc. This speeds things up and also saves memory for small AGP 93a030ce44SThomas Hellstrom * regions. 94a030ce44SThomas Hellstrom */ 95a030ce44SThomas Hellstrom 96a030ce44SThomas Hellstrom void agp_alloc_page_array(size_t size, struct agp_memory *mem) 97a030ce44SThomas Hellstrom { 98a030ce44SThomas Hellstrom mem->memory = NULL; 999516b030SDave Airlie mem->vmalloc_flag = false; 100a030ce44SThomas Hellstrom 1011c14cfbbSAndrew Morton if (size <= 2*PAGE_SIZE) 102a030ce44SThomas Hellstrom mem->memory = kmalloc(size, GFP_KERNEL | __GFP_NORETRY); 103a030ce44SThomas Hellstrom if (mem->memory == NULL) { 104a030ce44SThomas Hellstrom mem->memory = vmalloc(size); 1059516b030SDave Airlie mem->vmalloc_flag = true; 106a030ce44SThomas Hellstrom } 107a030ce44SThomas Hellstrom } 108a030ce44SThomas Hellstrom EXPORT_SYMBOL(agp_alloc_page_array); 109a030ce44SThomas Hellstrom 110a030ce44SThomas Hellstrom void agp_free_page_array(struct agp_memory *mem) 111a030ce44SThomas Hellstrom { 112a030ce44SThomas Hellstrom if (mem->vmalloc_flag) { 113a030ce44SThomas Hellstrom vfree(mem->memory); 114a030ce44SThomas Hellstrom } else { 115a030ce44SThomas Hellstrom kfree(mem->memory); 116a030ce44SThomas Hellstrom } 117a030ce44SThomas Hellstrom } 118a030ce44SThomas Hellstrom EXPORT_SYMBOL(agp_free_page_array); 119a030ce44SThomas Hellstrom 120a030ce44SThomas Hellstrom 121a030ce44SThomas Hellstrom static struct agp_memory *agp_create_user_memory(unsigned long num_agp_pages) 122a030ce44SThomas Hellstrom { 123a030ce44SThomas Hellstrom struct agp_memory *new; 124a030ce44SThomas Hellstrom unsigned long alloc_size = num_agp_pages*sizeof(struct page *); 125a030ce44SThomas Hellstrom 1261c14cfbbSAndrew Morton new = kzalloc(sizeof(struct agp_memory), GFP_KERNEL); 127a030ce44SThomas Hellstrom if (new == NULL) 128a030ce44SThomas Hellstrom return NULL; 129a030ce44SThomas Hellstrom 130a030ce44SThomas Hellstrom new->key = agp_get_key(); 131a030ce44SThomas Hellstrom 132a030ce44SThomas Hellstrom if (new->key < 0) { 133a030ce44SThomas Hellstrom kfree(new); 134a030ce44SThomas Hellstrom return NULL; 135a030ce44SThomas Hellstrom } 136a030ce44SThomas Hellstrom 137a030ce44SThomas Hellstrom agp_alloc_page_array(alloc_size, new); 138a030ce44SThomas Hellstrom 139a030ce44SThomas Hellstrom if (new->memory == NULL) { 140a030ce44SThomas Hellstrom agp_free_key(new->key); 141a030ce44SThomas Hellstrom kfree(new); 142a030ce44SThomas Hellstrom return NULL; 143a030ce44SThomas Hellstrom } 144a030ce44SThomas Hellstrom new->num_scratch_pages = 0; 145a030ce44SThomas Hellstrom return new; 146a030ce44SThomas Hellstrom } 147a030ce44SThomas Hellstrom 1481da177e4SLinus Torvalds struct agp_memory *agp_create_memory(int scratch_pages) 1491da177e4SLinus Torvalds { 1501da177e4SLinus Torvalds struct agp_memory *new; 1511da177e4SLinus Torvalds 1520ea27d9fSDave Jones new = kzalloc(sizeof(struct agp_memory), GFP_KERNEL); 1531da177e4SLinus Torvalds if (new == NULL) 1541da177e4SLinus Torvalds return NULL; 1551da177e4SLinus Torvalds 1561da177e4SLinus Torvalds new->key = agp_get_key(); 1571da177e4SLinus Torvalds 1581da177e4SLinus Torvalds if (new->key < 0) { 1591da177e4SLinus Torvalds kfree(new); 1601da177e4SLinus Torvalds return NULL; 1611da177e4SLinus Torvalds } 162a030ce44SThomas Hellstrom 163a030ce44SThomas Hellstrom agp_alloc_page_array(PAGE_SIZE * scratch_pages, new); 1641da177e4SLinus Torvalds 1651da177e4SLinus Torvalds if (new->memory == NULL) { 1661da177e4SLinus Torvalds agp_free_key(new->key); 1671da177e4SLinus Torvalds kfree(new); 1681da177e4SLinus Torvalds return NULL; 1691da177e4SLinus Torvalds } 1701da177e4SLinus Torvalds new->num_scratch_pages = scratch_pages; 171a030ce44SThomas Hellstrom new->type = AGP_NORMAL_MEMORY; 1721da177e4SLinus Torvalds return new; 1731da177e4SLinus Torvalds } 1741da177e4SLinus Torvalds EXPORT_SYMBOL(agp_create_memory); 1751da177e4SLinus Torvalds 1761da177e4SLinus Torvalds /** 1771da177e4SLinus Torvalds * agp_free_memory - free memory associated with an agp_memory pointer. 1781da177e4SLinus Torvalds * 1791da177e4SLinus Torvalds * @curr: agp_memory pointer to be freed. 1801da177e4SLinus Torvalds * 1811da177e4SLinus Torvalds * It is the only function that can be called when the backend is not owned 1821da177e4SLinus Torvalds * by the caller. (So it can free memory on client death.) 1831da177e4SLinus Torvalds */ 1841da177e4SLinus Torvalds void agp_free_memory(struct agp_memory *curr) 1851da177e4SLinus Torvalds { 1861da177e4SLinus Torvalds size_t i; 1871da177e4SLinus Torvalds 1881da177e4SLinus Torvalds if (curr == NULL) 1891da177e4SLinus Torvalds return; 1901da177e4SLinus Torvalds 191c7258012SJoe Perches if (curr->is_bound) 1921da177e4SLinus Torvalds agp_unbind_memory(curr); 1931da177e4SLinus Torvalds 194a030ce44SThomas Hellstrom if (curr->type >= AGP_USER_TYPES) { 195a030ce44SThomas Hellstrom agp_generic_free_by_type(curr); 196a030ce44SThomas Hellstrom return; 197a030ce44SThomas Hellstrom } 198a030ce44SThomas Hellstrom 1991da177e4SLinus Torvalds if (curr->type != 0) { 2001da177e4SLinus Torvalds curr->bridge->driver->free_by_type(curr); 2011da177e4SLinus Torvalds return; 2021da177e4SLinus Torvalds } 2031da177e4SLinus Torvalds if (curr->page_count != 0) { 2041da177e4SLinus Torvalds for (i = 0; i < curr->page_count; i++) { 205da503fa6SJan Beulich curr->memory[i] = (unsigned long)gart_to_virt(curr->memory[i]); 206da503fa6SJan Beulich curr->bridge->driver->agp_destroy_page((void *)curr->memory[i], 207da503fa6SJan Beulich AGP_PAGE_DESTROY_UNMAP); 2081da177e4SLinus Torvalds } 209a2721e99SDave Airlie for (i = 0; i < curr->page_count; i++) { 210da503fa6SJan Beulich curr->bridge->driver->agp_destroy_page((void *)curr->memory[i], 211da503fa6SJan Beulich AGP_PAGE_DESTROY_FREE); 212a2721e99SDave Airlie } 2131da177e4SLinus Torvalds } 2141da177e4SLinus Torvalds agp_free_key(curr->key); 215a030ce44SThomas Hellstrom agp_free_page_array(curr); 2161da177e4SLinus Torvalds kfree(curr); 2171da177e4SLinus Torvalds } 2181da177e4SLinus Torvalds EXPORT_SYMBOL(agp_free_memory); 2191da177e4SLinus Torvalds 2201da177e4SLinus Torvalds #define ENTRIES_PER_PAGE (PAGE_SIZE / sizeof(unsigned long)) 2211da177e4SLinus Torvalds 2221da177e4SLinus Torvalds /** 2231da177e4SLinus Torvalds * agp_allocate_memory - allocate a group of pages of a certain type. 2241da177e4SLinus Torvalds * 2251da177e4SLinus Torvalds * @page_count: size_t argument of the number of pages 2261da177e4SLinus Torvalds * @type: u32 argument of the type of memory to be allocated. 2271da177e4SLinus Torvalds * 2281da177e4SLinus Torvalds * Every agp bridge device will allow you to allocate AGP_NORMAL_MEMORY which 2291da177e4SLinus Torvalds * maps to physical ram. Any other type is device dependent. 2301da177e4SLinus Torvalds * 2311da177e4SLinus Torvalds * It returns NULL whenever memory is unavailable. 2321da177e4SLinus Torvalds */ 2331da177e4SLinus Torvalds struct agp_memory *agp_allocate_memory(struct agp_bridge_data *bridge, 2341da177e4SLinus Torvalds size_t page_count, u32 type) 2351da177e4SLinus Torvalds { 2361da177e4SLinus Torvalds int scratch_pages; 2371da177e4SLinus Torvalds struct agp_memory *new; 2381da177e4SLinus Torvalds size_t i; 2391da177e4SLinus Torvalds 2401da177e4SLinus Torvalds if (!bridge) 2411da177e4SLinus Torvalds return NULL; 2421da177e4SLinus Torvalds 2431da177e4SLinus Torvalds if ((atomic_read(&bridge->current_memory_agp) + page_count) > bridge->max_memory_agp) 2441da177e4SLinus Torvalds return NULL; 2451da177e4SLinus Torvalds 246a030ce44SThomas Hellstrom if (type >= AGP_USER_TYPES) { 247a030ce44SThomas Hellstrom new = agp_generic_alloc_user(page_count, type); 248a030ce44SThomas Hellstrom if (new) 249a030ce44SThomas Hellstrom new->bridge = bridge; 250a030ce44SThomas Hellstrom return new; 251a030ce44SThomas Hellstrom } 252a030ce44SThomas Hellstrom 2531da177e4SLinus Torvalds if (type != 0) { 2541da177e4SLinus Torvalds new = bridge->driver->alloc_by_type(page_count, type); 2551da177e4SLinus Torvalds if (new) 2561da177e4SLinus Torvalds new->bridge = bridge; 2571da177e4SLinus Torvalds return new; 2581da177e4SLinus Torvalds } 2591da177e4SLinus Torvalds 2601da177e4SLinus Torvalds scratch_pages = (page_count + ENTRIES_PER_PAGE - 1) / ENTRIES_PER_PAGE; 2611da177e4SLinus Torvalds 2621da177e4SLinus Torvalds new = agp_create_memory(scratch_pages); 2631da177e4SLinus Torvalds 2641da177e4SLinus Torvalds if (new == NULL) 2651da177e4SLinus Torvalds return NULL; 2661da177e4SLinus Torvalds 2671da177e4SLinus Torvalds for (i = 0; i < page_count; i++) { 2681da177e4SLinus Torvalds void *addr = bridge->driver->agp_alloc_page(bridge); 2691da177e4SLinus Torvalds 2701da177e4SLinus Torvalds if (addr == NULL) { 2711da177e4SLinus Torvalds agp_free_memory(new); 2721da177e4SLinus Torvalds return NULL; 2731da177e4SLinus Torvalds } 27407eee78eSKeir Fraser new->memory[i] = virt_to_gart(addr); 2751da177e4SLinus Torvalds new->page_count++; 2761da177e4SLinus Torvalds } 2771da177e4SLinus Torvalds new->bridge = bridge; 2781da177e4SLinus Torvalds 2791da177e4SLinus Torvalds return new; 2801da177e4SLinus Torvalds } 2811da177e4SLinus Torvalds EXPORT_SYMBOL(agp_allocate_memory); 2821da177e4SLinus Torvalds 2831da177e4SLinus Torvalds 2841da177e4SLinus Torvalds /* End - Generic routines for handling agp_memory structures */ 2851da177e4SLinus Torvalds 2861da177e4SLinus Torvalds 2871da177e4SLinus Torvalds static int agp_return_size(void) 2881da177e4SLinus Torvalds { 2891da177e4SLinus Torvalds int current_size; 2901da177e4SLinus Torvalds void *temp; 2911da177e4SLinus Torvalds 2921da177e4SLinus Torvalds temp = agp_bridge->current_size; 2931da177e4SLinus Torvalds 2941da177e4SLinus Torvalds switch (agp_bridge->driver->size_type) { 2951da177e4SLinus Torvalds case U8_APER_SIZE: 2961da177e4SLinus Torvalds current_size = A_SIZE_8(temp)->size; 2971da177e4SLinus Torvalds break; 2981da177e4SLinus Torvalds case U16_APER_SIZE: 2991da177e4SLinus Torvalds current_size = A_SIZE_16(temp)->size; 3001da177e4SLinus Torvalds break; 3011da177e4SLinus Torvalds case U32_APER_SIZE: 3021da177e4SLinus Torvalds current_size = A_SIZE_32(temp)->size; 3031da177e4SLinus Torvalds break; 3041da177e4SLinus Torvalds case LVL2_APER_SIZE: 3051da177e4SLinus Torvalds current_size = A_SIZE_LVL2(temp)->size; 3061da177e4SLinus Torvalds break; 3071da177e4SLinus Torvalds case FIXED_APER_SIZE: 3081da177e4SLinus Torvalds current_size = A_SIZE_FIX(temp)->size; 3091da177e4SLinus Torvalds break; 3101da177e4SLinus Torvalds default: 3111da177e4SLinus Torvalds current_size = 0; 3121da177e4SLinus Torvalds break; 3131da177e4SLinus Torvalds } 3141da177e4SLinus Torvalds 3151da177e4SLinus Torvalds current_size -= (agp_memory_reserved / (1024*1024)); 3161da177e4SLinus Torvalds if (current_size <0) 3171da177e4SLinus Torvalds current_size = 0; 3181da177e4SLinus Torvalds return current_size; 3191da177e4SLinus Torvalds } 3201da177e4SLinus Torvalds 3211da177e4SLinus Torvalds 3221da177e4SLinus Torvalds int agp_num_entries(void) 3231da177e4SLinus Torvalds { 3241da177e4SLinus Torvalds int num_entries; 3251da177e4SLinus Torvalds void *temp; 3261da177e4SLinus Torvalds 3271da177e4SLinus Torvalds temp = agp_bridge->current_size; 3281da177e4SLinus Torvalds 3291da177e4SLinus Torvalds switch (agp_bridge->driver->size_type) { 3301da177e4SLinus Torvalds case U8_APER_SIZE: 3311da177e4SLinus Torvalds num_entries = A_SIZE_8(temp)->num_entries; 3321da177e4SLinus Torvalds break; 3331da177e4SLinus Torvalds case U16_APER_SIZE: 3341da177e4SLinus Torvalds num_entries = A_SIZE_16(temp)->num_entries; 3351da177e4SLinus Torvalds break; 3361da177e4SLinus Torvalds case U32_APER_SIZE: 3371da177e4SLinus Torvalds num_entries = A_SIZE_32(temp)->num_entries; 3381da177e4SLinus Torvalds break; 3391da177e4SLinus Torvalds case LVL2_APER_SIZE: 3401da177e4SLinus Torvalds num_entries = A_SIZE_LVL2(temp)->num_entries; 3411da177e4SLinus Torvalds break; 3421da177e4SLinus Torvalds case FIXED_APER_SIZE: 3431da177e4SLinus Torvalds num_entries = A_SIZE_FIX(temp)->num_entries; 3441da177e4SLinus Torvalds break; 3451da177e4SLinus Torvalds default: 3461da177e4SLinus Torvalds num_entries = 0; 3471da177e4SLinus Torvalds break; 3481da177e4SLinus Torvalds } 3491da177e4SLinus Torvalds 3501da177e4SLinus Torvalds num_entries -= agp_memory_reserved>>PAGE_SHIFT; 3511da177e4SLinus Torvalds if (num_entries<0) 3521da177e4SLinus Torvalds num_entries = 0; 3531da177e4SLinus Torvalds return num_entries; 3541da177e4SLinus Torvalds } 3551da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(agp_num_entries); 3561da177e4SLinus Torvalds 3571da177e4SLinus Torvalds 3581da177e4SLinus Torvalds /** 3591da177e4SLinus Torvalds * agp_copy_info - copy bridge state information 3601da177e4SLinus Torvalds * 3611da177e4SLinus Torvalds * @info: agp_kern_info pointer. The caller should insure that this pointer is valid. 3621da177e4SLinus Torvalds * 3631da177e4SLinus Torvalds * This function copies information about the agp bridge device and the state of 3641da177e4SLinus Torvalds * the agp backend into an agp_kern_info pointer. 3651da177e4SLinus Torvalds */ 3661da177e4SLinus Torvalds int agp_copy_info(struct agp_bridge_data *bridge, struct agp_kern_info *info) 3671da177e4SLinus Torvalds { 3681da177e4SLinus Torvalds memset(info, 0, sizeof(struct agp_kern_info)); 3691da177e4SLinus Torvalds if (!bridge) { 3701da177e4SLinus Torvalds info->chipset = NOT_SUPPORTED; 3711da177e4SLinus Torvalds return -EIO; 3721da177e4SLinus Torvalds } 3731da177e4SLinus Torvalds 3741da177e4SLinus Torvalds info->version.major = bridge->version->major; 3751da177e4SLinus Torvalds info->version.minor = bridge->version->minor; 3761da177e4SLinus Torvalds info->chipset = SUPPORTED; 3771da177e4SLinus Torvalds info->device = bridge->dev; 37866bb8bf8SDavid Mosberger if (bridge->mode & AGPSTAT_MODE_3_0) 3791da177e4SLinus Torvalds info->mode = bridge->mode & ~AGP3_RESERVED_MASK; 3801da177e4SLinus Torvalds else 3811da177e4SLinus Torvalds info->mode = bridge->mode & ~AGP2_RESERVED_MASK; 3821da177e4SLinus Torvalds info->aper_base = bridge->gart_bus_addr; 3831da177e4SLinus Torvalds info->aper_size = agp_return_size(); 3841da177e4SLinus Torvalds info->max_memory = bridge->max_memory_agp; 3851da177e4SLinus Torvalds info->current_memory = atomic_read(&bridge->current_memory_agp); 3861da177e4SLinus Torvalds info->cant_use_aperture = bridge->driver->cant_use_aperture; 3871da177e4SLinus Torvalds info->vm_ops = bridge->vm_ops; 3881da177e4SLinus Torvalds info->page_mask = ~0UL; 3891da177e4SLinus Torvalds return 0; 3901da177e4SLinus Torvalds } 3911da177e4SLinus Torvalds EXPORT_SYMBOL(agp_copy_info); 3921da177e4SLinus Torvalds 3931da177e4SLinus Torvalds /* End - Routine to copy over information structure */ 3941da177e4SLinus Torvalds 3951da177e4SLinus Torvalds /* 3961da177e4SLinus Torvalds * Routines for handling swapping of agp_memory into the GATT - 3971da177e4SLinus Torvalds * These routines take agp_memory and insert them into the GATT. 3981da177e4SLinus Torvalds * They call device specific routines to actually write to the GATT. 3991da177e4SLinus Torvalds */ 4001da177e4SLinus Torvalds 4011da177e4SLinus Torvalds /** 4021da177e4SLinus Torvalds * agp_bind_memory - Bind an agp_memory structure into the GATT. 4031da177e4SLinus Torvalds * 4041da177e4SLinus Torvalds * @curr: agp_memory pointer 4051da177e4SLinus Torvalds * @pg_start: an offset into the graphics aperture translation table 4061da177e4SLinus Torvalds * 4071da177e4SLinus Torvalds * It returns -EINVAL if the pointer == NULL. 4081da177e4SLinus Torvalds * It returns -EBUSY if the area of the table requested is already in use. 4091da177e4SLinus Torvalds */ 4101da177e4SLinus Torvalds int agp_bind_memory(struct agp_memory *curr, off_t pg_start) 4111da177e4SLinus Torvalds { 4121da177e4SLinus Torvalds int ret_val; 4131da177e4SLinus Torvalds 4141da177e4SLinus Torvalds if (curr == NULL) 4151da177e4SLinus Torvalds return -EINVAL; 4161da177e4SLinus Torvalds 417c7258012SJoe Perches if (curr->is_bound) { 4181da177e4SLinus Torvalds printk(KERN_INFO PFX "memory %p is already bound!\n", curr); 4191da177e4SLinus Torvalds return -EINVAL; 4201da177e4SLinus Torvalds } 421c7258012SJoe Perches if (!curr->is_flushed) { 4221da177e4SLinus Torvalds curr->bridge->driver->cache_flush(); 423c7258012SJoe Perches curr->is_flushed = true; 4241da177e4SLinus Torvalds } 4251da177e4SLinus Torvalds ret_val = curr->bridge->driver->insert_memory(curr, pg_start, curr->type); 4261da177e4SLinus Torvalds 4271da177e4SLinus Torvalds if (ret_val != 0) 4281da177e4SLinus Torvalds return ret_val; 4291da177e4SLinus Torvalds 430c7258012SJoe Perches curr->is_bound = true; 4311da177e4SLinus Torvalds curr->pg_start = pg_start; 4321da177e4SLinus Torvalds return 0; 4331da177e4SLinus Torvalds } 4341da177e4SLinus Torvalds EXPORT_SYMBOL(agp_bind_memory); 4351da177e4SLinus Torvalds 4361da177e4SLinus Torvalds 4371da177e4SLinus Torvalds /** 4381da177e4SLinus Torvalds * agp_unbind_memory - Removes an agp_memory structure from the GATT 4391da177e4SLinus Torvalds * 4401da177e4SLinus Torvalds * @curr: agp_memory pointer to be removed from the GATT. 4411da177e4SLinus Torvalds * 4421da177e4SLinus Torvalds * It returns -EINVAL if this piece of agp_memory is not currently bound to 4431da177e4SLinus Torvalds * the graphics aperture translation table or if the agp_memory pointer == NULL 4441da177e4SLinus Torvalds */ 4451da177e4SLinus Torvalds int agp_unbind_memory(struct agp_memory *curr) 4461da177e4SLinus Torvalds { 4471da177e4SLinus Torvalds int ret_val; 4481da177e4SLinus Torvalds 4491da177e4SLinus Torvalds if (curr == NULL) 4501da177e4SLinus Torvalds return -EINVAL; 4511da177e4SLinus Torvalds 452c7258012SJoe Perches if (!curr->is_bound) { 4531da177e4SLinus Torvalds printk(KERN_INFO PFX "memory %p was not bound!\n", curr); 4541da177e4SLinus Torvalds return -EINVAL; 4551da177e4SLinus Torvalds } 4561da177e4SLinus Torvalds 4571da177e4SLinus Torvalds ret_val = curr->bridge->driver->remove_memory(curr, curr->pg_start, curr->type); 4581da177e4SLinus Torvalds 4591da177e4SLinus Torvalds if (ret_val != 0) 4601da177e4SLinus Torvalds return ret_val; 4611da177e4SLinus Torvalds 462c7258012SJoe Perches curr->is_bound = false; 4631da177e4SLinus Torvalds curr->pg_start = 0; 4641da177e4SLinus Torvalds return 0; 4651da177e4SLinus Torvalds } 4661da177e4SLinus Torvalds EXPORT_SYMBOL(agp_unbind_memory); 4671da177e4SLinus Torvalds 4681da177e4SLinus Torvalds /* End - Routines for handling swapping of agp_memory into the GATT */ 4691da177e4SLinus Torvalds 4701da177e4SLinus Torvalds 4711da177e4SLinus Torvalds /* Generic Agp routines - Start */ 4721da177e4SLinus Torvalds static void agp_v2_parse_one(u32 *requested_mode, u32 *bridge_agpstat, u32 *vga_agpstat) 4731da177e4SLinus Torvalds { 4741da177e4SLinus Torvalds u32 tmp; 4751da177e4SLinus Torvalds 4761da177e4SLinus Torvalds if (*requested_mode & AGP2_RESERVED_MASK) { 477c4dd4582SDave Jones printk(KERN_INFO PFX "reserved bits set (%x) in mode 0x%x. Fixed.\n", 478c4dd4582SDave Jones *requested_mode & AGP2_RESERVED_MASK, *requested_mode); 4791da177e4SLinus Torvalds *requested_mode &= ~AGP2_RESERVED_MASK; 4801da177e4SLinus Torvalds } 4811da177e4SLinus Torvalds 48228af24bbSDave Jones /* 48328af24bbSDave Jones * Some dumb bridges are programmed to disobey the AGP2 spec. 48428af24bbSDave Jones * This is likely a BIOS misprogramming rather than poweron default, or 48528af24bbSDave Jones * it would be a lot more common. 48628af24bbSDave Jones * https://bugs.freedesktop.org/show_bug.cgi?id=8816 48728af24bbSDave Jones * AGPv2 spec 6.1.9 states: 48828af24bbSDave Jones * The RATE field indicates the data transfer rates supported by this 48928af24bbSDave Jones * device. A.G.P. devices must report all that apply. 49028af24bbSDave Jones * Fix them up as best we can. 49128af24bbSDave Jones */ 49228af24bbSDave Jones switch (*bridge_agpstat & 7) { 49328af24bbSDave Jones case 4: 49428af24bbSDave Jones *bridge_agpstat |= (AGPSTAT2_2X | AGPSTAT2_1X); 49528af24bbSDave Jones printk(KERN_INFO PFX "BIOS bug. AGP bridge claims to only support x4 rate" 49628af24bbSDave Jones "Fixing up support for x2 & x1\n"); 49728af24bbSDave Jones break; 49828af24bbSDave Jones case 2: 49928af24bbSDave Jones *bridge_agpstat |= AGPSTAT2_1X; 50028af24bbSDave Jones printk(KERN_INFO PFX "BIOS bug. AGP bridge claims to only support x2 rate" 50128af24bbSDave Jones "Fixing up support for x1\n"); 50228af24bbSDave Jones break; 50328af24bbSDave Jones default: 50428af24bbSDave Jones break; 50528af24bbSDave Jones } 50628af24bbSDave Jones 5071da177e4SLinus Torvalds /* Check the speed bits make sense. Only one should be set. */ 5081da177e4SLinus Torvalds tmp = *requested_mode & 7; 5091da177e4SLinus Torvalds switch (tmp) { 5101da177e4SLinus Torvalds case 0: 5111da177e4SLinus Torvalds printk(KERN_INFO PFX "%s tried to set rate=x0. Setting to x1 mode.\n", current->comm); 5121da177e4SLinus Torvalds *requested_mode |= AGPSTAT2_1X; 5131da177e4SLinus Torvalds break; 5141da177e4SLinus Torvalds case 1: 5151da177e4SLinus Torvalds case 2: 5161da177e4SLinus Torvalds break; 5171da177e4SLinus Torvalds case 3: 5181da177e4SLinus Torvalds *requested_mode &= ~(AGPSTAT2_1X); /* rate=2 */ 5191da177e4SLinus Torvalds break; 5201da177e4SLinus Torvalds case 4: 5211da177e4SLinus Torvalds break; 5221da177e4SLinus Torvalds case 5: 5231da177e4SLinus Torvalds case 6: 5241da177e4SLinus Torvalds case 7: 5251da177e4SLinus Torvalds *requested_mode &= ~(AGPSTAT2_1X|AGPSTAT2_2X); /* rate=4*/ 5261da177e4SLinus Torvalds break; 5271da177e4SLinus Torvalds } 5281da177e4SLinus Torvalds 5291da177e4SLinus Torvalds /* disable SBA if it's not supported */ 5301da177e4SLinus Torvalds if (!((*bridge_agpstat & AGPSTAT_SBA) && (*vga_agpstat & AGPSTAT_SBA) && (*requested_mode & AGPSTAT_SBA))) 5311da177e4SLinus Torvalds *bridge_agpstat &= ~AGPSTAT_SBA; 5321da177e4SLinus Torvalds 5331da177e4SLinus Torvalds /* Set rate */ 5341da177e4SLinus Torvalds if (!((*bridge_agpstat & AGPSTAT2_4X) && (*vga_agpstat & AGPSTAT2_4X) && (*requested_mode & AGPSTAT2_4X))) 5351da177e4SLinus Torvalds *bridge_agpstat &= ~AGPSTAT2_4X; 5361da177e4SLinus Torvalds 5371da177e4SLinus Torvalds if (!((*bridge_agpstat & AGPSTAT2_2X) && (*vga_agpstat & AGPSTAT2_2X) && (*requested_mode & AGPSTAT2_2X))) 5381da177e4SLinus Torvalds *bridge_agpstat &= ~AGPSTAT2_2X; 5391da177e4SLinus Torvalds 5401da177e4SLinus Torvalds if (!((*bridge_agpstat & AGPSTAT2_1X) && (*vga_agpstat & AGPSTAT2_1X) && (*requested_mode & AGPSTAT2_1X))) 5411da177e4SLinus Torvalds *bridge_agpstat &= ~AGPSTAT2_1X; 5421da177e4SLinus Torvalds 5431da177e4SLinus Torvalds /* Now we know what mode it should be, clear out the unwanted bits. */ 5441da177e4SLinus Torvalds if (*bridge_agpstat & AGPSTAT2_4X) 5451da177e4SLinus Torvalds *bridge_agpstat &= ~(AGPSTAT2_1X | AGPSTAT2_2X); /* 4X */ 5461da177e4SLinus Torvalds 5471da177e4SLinus Torvalds if (*bridge_agpstat & AGPSTAT2_2X) 5481da177e4SLinus Torvalds *bridge_agpstat &= ~(AGPSTAT2_1X | AGPSTAT2_4X); /* 2X */ 5491da177e4SLinus Torvalds 5501da177e4SLinus Torvalds if (*bridge_agpstat & AGPSTAT2_1X) 5511da177e4SLinus Torvalds *bridge_agpstat &= ~(AGPSTAT2_2X | AGPSTAT2_4X); /* 1X */ 5521da177e4SLinus Torvalds 5531da177e4SLinus Torvalds /* Apply any errata. */ 5541da177e4SLinus Torvalds if (agp_bridge->flags & AGP_ERRATA_FASTWRITES) 5551da177e4SLinus Torvalds *bridge_agpstat &= ~AGPSTAT_FW; 5561da177e4SLinus Torvalds 5571da177e4SLinus Torvalds if (agp_bridge->flags & AGP_ERRATA_SBA) 5581da177e4SLinus Torvalds *bridge_agpstat &= ~AGPSTAT_SBA; 5591da177e4SLinus Torvalds 5601da177e4SLinus Torvalds if (agp_bridge->flags & AGP_ERRATA_1X) { 5611da177e4SLinus Torvalds *bridge_agpstat &= ~(AGPSTAT2_2X | AGPSTAT2_4X); 5621da177e4SLinus Torvalds *bridge_agpstat |= AGPSTAT2_1X; 5631da177e4SLinus Torvalds } 5641da177e4SLinus Torvalds 5651da177e4SLinus Torvalds /* If we've dropped down to 1X, disable fast writes. */ 5661da177e4SLinus Torvalds if (*bridge_agpstat & AGPSTAT2_1X) 5671da177e4SLinus Torvalds *bridge_agpstat &= ~AGPSTAT_FW; 5681da177e4SLinus Torvalds } 5691da177e4SLinus Torvalds 5701da177e4SLinus Torvalds /* 5711da177e4SLinus Torvalds * requested_mode = Mode requested by (typically) X. 5721da177e4SLinus Torvalds * bridge_agpstat = PCI_AGP_STATUS from agp bridge. 5731da177e4SLinus Torvalds * vga_agpstat = PCI_AGP_STATUS from graphic card. 5741da177e4SLinus Torvalds */ 5751da177e4SLinus Torvalds static void agp_v3_parse_one(u32 *requested_mode, u32 *bridge_agpstat, u32 *vga_agpstat) 5761da177e4SLinus Torvalds { 5771da177e4SLinus Torvalds u32 origbridge=*bridge_agpstat, origvga=*vga_agpstat; 5781da177e4SLinus Torvalds u32 tmp; 5791da177e4SLinus Torvalds 5801da177e4SLinus Torvalds if (*requested_mode & AGP3_RESERVED_MASK) { 581c4dd4582SDave Jones printk(KERN_INFO PFX "reserved bits set (%x) in mode 0x%x. Fixed.\n", 582c4dd4582SDave Jones *requested_mode & AGP3_RESERVED_MASK, *requested_mode); 5831da177e4SLinus Torvalds *requested_mode &= ~AGP3_RESERVED_MASK; 5841da177e4SLinus Torvalds } 5851da177e4SLinus Torvalds 5861da177e4SLinus Torvalds /* Check the speed bits make sense. */ 5871da177e4SLinus Torvalds tmp = *requested_mode & 7; 5881da177e4SLinus Torvalds if (tmp == 0) { 5891da177e4SLinus Torvalds printk(KERN_INFO PFX "%s tried to set rate=x0. Setting to AGP3 x4 mode.\n", current->comm); 5901da177e4SLinus Torvalds *requested_mode |= AGPSTAT3_4X; 5911da177e4SLinus Torvalds } 5921da177e4SLinus Torvalds if (tmp >= 3) { 5931da177e4SLinus Torvalds printk(KERN_INFO PFX "%s tried to set rate=x%d. Setting to AGP3 x8 mode.\n", current->comm, tmp * 4); 5941da177e4SLinus Torvalds *requested_mode = (*requested_mode & ~7) | AGPSTAT3_8X; 5951da177e4SLinus Torvalds } 5961da177e4SLinus Torvalds 5971da177e4SLinus Torvalds /* ARQSZ - Set the value to the maximum one. 5981da177e4SLinus Torvalds * Don't allow the mode register to override values. */ 5991da177e4SLinus Torvalds *bridge_agpstat = ((*bridge_agpstat & ~AGPSTAT_ARQSZ) | 6001da177e4SLinus Torvalds max_t(u32,(*bridge_agpstat & AGPSTAT_ARQSZ),(*vga_agpstat & AGPSTAT_ARQSZ))); 6011da177e4SLinus Torvalds 6021da177e4SLinus Torvalds /* Calibration cycle. 6031da177e4SLinus Torvalds * Don't allow the mode register to override values. */ 6041da177e4SLinus Torvalds *bridge_agpstat = ((*bridge_agpstat & ~AGPSTAT_CAL_MASK) | 6051da177e4SLinus Torvalds min_t(u32,(*bridge_agpstat & AGPSTAT_CAL_MASK),(*vga_agpstat & AGPSTAT_CAL_MASK))); 6061da177e4SLinus Torvalds 6071da177e4SLinus Torvalds /* SBA *must* be supported for AGP v3 */ 6081da177e4SLinus Torvalds *bridge_agpstat |= AGPSTAT_SBA; 6091da177e4SLinus Torvalds 6101da177e4SLinus Torvalds /* 6111da177e4SLinus Torvalds * Set speed. 6121da177e4SLinus Torvalds * Check for invalid speeds. This can happen when applications 6131da177e4SLinus Torvalds * written before the AGP 3.0 standard pass AGP2.x modes to AGP3 hardware 6141da177e4SLinus Torvalds */ 6151da177e4SLinus Torvalds if (*requested_mode & AGPSTAT_MODE_3_0) { 6161da177e4SLinus Torvalds /* 6171da177e4SLinus Torvalds * Caller hasn't a clue what it is doing. Bridge is in 3.0 mode, 6181da177e4SLinus Torvalds * have been passed a 3.0 mode, but with 2.x speed bits set. 6191da177e4SLinus Torvalds * AGP2.x 4x -> AGP3.0 4x. 6201da177e4SLinus Torvalds */ 6211da177e4SLinus Torvalds if (*requested_mode & AGPSTAT2_4X) { 6221da177e4SLinus Torvalds printk(KERN_INFO PFX "%s passes broken AGP3 flags (%x). Fixed.\n", 6231da177e4SLinus Torvalds current->comm, *requested_mode); 6241da177e4SLinus Torvalds *requested_mode &= ~AGPSTAT2_4X; 6251da177e4SLinus Torvalds *requested_mode |= AGPSTAT3_4X; 6261da177e4SLinus Torvalds } 6271da177e4SLinus Torvalds } else { 6281da177e4SLinus Torvalds /* 6291da177e4SLinus Torvalds * The caller doesn't know what they are doing. We are in 3.0 mode, 6301da177e4SLinus Torvalds * but have been passed an AGP 2.x mode. 6311da177e4SLinus Torvalds * Convert AGP 1x,2x,4x -> AGP 3.0 4x. 6321da177e4SLinus Torvalds */ 6331da177e4SLinus Torvalds printk(KERN_INFO PFX "%s passes broken AGP2 flags (%x) in AGP3 mode. Fixed.\n", 6341da177e4SLinus Torvalds current->comm, *requested_mode); 6351da177e4SLinus Torvalds *requested_mode &= ~(AGPSTAT2_4X | AGPSTAT2_2X | AGPSTAT2_1X); 6361da177e4SLinus Torvalds *requested_mode |= AGPSTAT3_4X; 6371da177e4SLinus Torvalds } 6381da177e4SLinus Torvalds 6391da177e4SLinus Torvalds if (*requested_mode & AGPSTAT3_8X) { 6401da177e4SLinus Torvalds if (!(*bridge_agpstat & AGPSTAT3_8X)) { 6411da177e4SLinus Torvalds *bridge_agpstat &= ~(AGPSTAT3_8X | AGPSTAT3_RSVD); 6421da177e4SLinus Torvalds *bridge_agpstat |= AGPSTAT3_4X; 6438c8b8385SDave Jones printk(KERN_INFO PFX "%s requested AGPx8 but bridge not capable.\n", current->comm); 6441da177e4SLinus Torvalds return; 6451da177e4SLinus Torvalds } 6461da177e4SLinus Torvalds if (!(*vga_agpstat & AGPSTAT3_8X)) { 6471da177e4SLinus Torvalds *bridge_agpstat &= ~(AGPSTAT3_8X | AGPSTAT3_RSVD); 6481da177e4SLinus Torvalds *bridge_agpstat |= AGPSTAT3_4X; 6498c8b8385SDave Jones printk(KERN_INFO PFX "%s requested AGPx8 but graphic card not capable.\n", current->comm); 6501da177e4SLinus Torvalds return; 6511da177e4SLinus Torvalds } 6521da177e4SLinus Torvalds /* All set, bridge & device can do AGP x8*/ 6531da177e4SLinus Torvalds *bridge_agpstat &= ~(AGPSTAT3_4X | AGPSTAT3_RSVD); 6541da177e4SLinus Torvalds goto done; 6551da177e4SLinus Torvalds 656edf03fb0SDave Jones } else if (*requested_mode & AGPSTAT3_4X) { 657edf03fb0SDave Jones *bridge_agpstat &= ~(AGPSTAT3_8X | AGPSTAT3_RSVD); 658edf03fb0SDave Jones *bridge_agpstat |= AGPSTAT3_4X; 659edf03fb0SDave Jones goto done; 660edf03fb0SDave Jones 6611da177e4SLinus Torvalds } else { 6621da177e4SLinus Torvalds 6631da177e4SLinus Torvalds /* 664edf03fb0SDave Jones * If we didn't specify an AGP mode, we see if both 665edf03fb0SDave Jones * the graphics card, and the bridge can do x8, and use if so. 666edf03fb0SDave Jones * If not, we fall back to x4 mode. 6671da177e4SLinus Torvalds */ 668edf03fb0SDave Jones if ((*bridge_agpstat & AGPSTAT3_8X) && (*vga_agpstat & AGPSTAT3_8X)) { 6692cc1a413SDave Jones printk(KERN_INFO PFX "No AGP mode specified. Setting to highest mode " 6702cc1a413SDave Jones "supported by bridge & card (x8).\n"); 671edf03fb0SDave Jones *bridge_agpstat &= ~(AGPSTAT3_4X | AGPSTAT3_RSVD); 672edf03fb0SDave Jones *vga_agpstat &= ~(AGPSTAT3_4X | AGPSTAT3_RSVD); 673edf03fb0SDave Jones } else { 674edf03fb0SDave Jones printk(KERN_INFO PFX "Fell back to AGPx4 mode because"); 675edf03fb0SDave Jones if (!(*bridge_agpstat & AGPSTAT3_8X)) { 6762cc1a413SDave Jones printk(KERN_INFO PFX "bridge couldn't do x8. bridge_agpstat:%x (orig=%x)\n", 6772cc1a413SDave Jones *bridge_agpstat, origbridge); 6781da177e4SLinus Torvalds *bridge_agpstat &= ~(AGPSTAT3_8X | AGPSTAT3_RSVD); 6791da177e4SLinus Torvalds *bridge_agpstat |= AGPSTAT3_4X; 680edf03fb0SDave Jones } 681edf03fb0SDave Jones if (!(*vga_agpstat & AGPSTAT3_8X)) { 6822cc1a413SDave Jones printk(KERN_INFO PFX "graphics card couldn't do x8. vga_agpstat:%x (orig=%x)\n", 6832cc1a413SDave Jones *vga_agpstat, origvga); 684edf03fb0SDave Jones *vga_agpstat &= ~(AGPSTAT3_8X | AGPSTAT3_RSVD); 685edf03fb0SDave Jones *vga_agpstat |= AGPSTAT3_4X; 686edf03fb0SDave Jones } 6871da177e4SLinus Torvalds } 6881da177e4SLinus Torvalds } 6891da177e4SLinus Torvalds 6901da177e4SLinus Torvalds done: 6911da177e4SLinus Torvalds /* Apply any errata. */ 6921da177e4SLinus Torvalds if (agp_bridge->flags & AGP_ERRATA_FASTWRITES) 6931da177e4SLinus Torvalds *bridge_agpstat &= ~AGPSTAT_FW; 6941da177e4SLinus Torvalds 6951da177e4SLinus Torvalds if (agp_bridge->flags & AGP_ERRATA_SBA) 6961da177e4SLinus Torvalds *bridge_agpstat &= ~AGPSTAT_SBA; 6971da177e4SLinus Torvalds 6981da177e4SLinus Torvalds if (agp_bridge->flags & AGP_ERRATA_1X) { 6991da177e4SLinus Torvalds *bridge_agpstat &= ~(AGPSTAT2_2X | AGPSTAT2_4X); 7001da177e4SLinus Torvalds *bridge_agpstat |= AGPSTAT2_1X; 7011da177e4SLinus Torvalds } 7021da177e4SLinus Torvalds } 7031da177e4SLinus Torvalds 7041da177e4SLinus Torvalds 7051da177e4SLinus Torvalds /** 7061da177e4SLinus Torvalds * agp_collect_device_status - determine correct agp_cmd from various agp_stat's 7071da177e4SLinus Torvalds * @bridge: an agp_bridge_data struct allocated for the AGP host bridge. 7081da177e4SLinus Torvalds * @requested_mode: requested agp_stat from userspace (Typically from X) 7091da177e4SLinus Torvalds * @bridge_agpstat: current agp_stat from AGP bridge. 7101da177e4SLinus Torvalds * 7111da177e4SLinus Torvalds * This function will hunt for an AGP graphics card, and try to match 7121da177e4SLinus Torvalds * the requested mode to the capabilities of both the bridge and the card. 7131da177e4SLinus Torvalds */ 7141da177e4SLinus Torvalds u32 agp_collect_device_status(struct agp_bridge_data *bridge, u32 requested_mode, u32 bridge_agpstat) 7151da177e4SLinus Torvalds { 7161da177e4SLinus Torvalds struct pci_dev *device = NULL; 7171da177e4SLinus Torvalds u32 vga_agpstat; 7181da177e4SLinus Torvalds u8 cap_ptr; 7191da177e4SLinus Torvalds 7201da177e4SLinus Torvalds for (;;) { 7211da177e4SLinus Torvalds device = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, device); 7221da177e4SLinus Torvalds if (!device) { 7231da177e4SLinus Torvalds printk(KERN_INFO PFX "Couldn't find an AGP VGA controller.\n"); 7241da177e4SLinus Torvalds return 0; 7251da177e4SLinus Torvalds } 7261da177e4SLinus Torvalds cap_ptr = pci_find_capability(device, PCI_CAP_ID_AGP); 7271da177e4SLinus Torvalds if (cap_ptr) 7281da177e4SLinus Torvalds break; 7291da177e4SLinus Torvalds } 7301da177e4SLinus Torvalds 7311da177e4SLinus Torvalds /* 7321da177e4SLinus Torvalds * Ok, here we have a AGP device. Disable impossible 7331da177e4SLinus Torvalds * settings, and adjust the readqueue to the minimum. 7341da177e4SLinus Torvalds */ 7351da177e4SLinus Torvalds pci_read_config_dword(device, cap_ptr+PCI_AGP_STATUS, &vga_agpstat); 7361da177e4SLinus Torvalds 7371da177e4SLinus Torvalds /* adjust RQ depth */ 7381da177e4SLinus Torvalds bridge_agpstat = ((bridge_agpstat & ~AGPSTAT_RQ_DEPTH) | 7391da177e4SLinus Torvalds min_t(u32, (requested_mode & AGPSTAT_RQ_DEPTH), 7401da177e4SLinus Torvalds min_t(u32, (bridge_agpstat & AGPSTAT_RQ_DEPTH), (vga_agpstat & AGPSTAT_RQ_DEPTH)))); 7411da177e4SLinus Torvalds 7421da177e4SLinus Torvalds /* disable FW if it's not supported */ 7431da177e4SLinus Torvalds if (!((bridge_agpstat & AGPSTAT_FW) && 7441da177e4SLinus Torvalds (vga_agpstat & AGPSTAT_FW) && 7451da177e4SLinus Torvalds (requested_mode & AGPSTAT_FW))) 7461da177e4SLinus Torvalds bridge_agpstat &= ~AGPSTAT_FW; 7471da177e4SLinus Torvalds 7481da177e4SLinus Torvalds /* Check to see if we are operating in 3.0 mode */ 74966bb8bf8SDavid Mosberger if (agp_bridge->mode & AGPSTAT_MODE_3_0) 7501da177e4SLinus Torvalds agp_v3_parse_one(&requested_mode, &bridge_agpstat, &vga_agpstat); 7511da177e4SLinus Torvalds else 7521da177e4SLinus Torvalds agp_v2_parse_one(&requested_mode, &bridge_agpstat, &vga_agpstat); 7531da177e4SLinus Torvalds 7541da177e4SLinus Torvalds pci_dev_put(device); 7551da177e4SLinus Torvalds return bridge_agpstat; 7561da177e4SLinus Torvalds } 7571da177e4SLinus Torvalds EXPORT_SYMBOL(agp_collect_device_status); 7581da177e4SLinus Torvalds 7591da177e4SLinus Torvalds 760c7258012SJoe Perches void agp_device_command(u32 bridge_agpstat, bool agp_v3) 7611da177e4SLinus Torvalds { 7621da177e4SLinus Torvalds struct pci_dev *device = NULL; 7631da177e4SLinus Torvalds int mode; 7641da177e4SLinus Torvalds 7651da177e4SLinus Torvalds mode = bridge_agpstat & 0x7; 7661da177e4SLinus Torvalds if (agp_v3) 7671da177e4SLinus Torvalds mode *= 4; 7681da177e4SLinus Torvalds 7691da177e4SLinus Torvalds for_each_pci_dev(device) { 7701da177e4SLinus Torvalds u8 agp = pci_find_capability(device, PCI_CAP_ID_AGP); 7711da177e4SLinus Torvalds if (!agp) 7721da177e4SLinus Torvalds continue; 7731da177e4SLinus Torvalds 774*e3cf6951SBjorn Helgaas dev_info(&device->dev, "putting AGP V%d device into %dx mode\n", 775*e3cf6951SBjorn Helgaas agp_v3 ? 3 : 2, mode); 7761da177e4SLinus Torvalds pci_write_config_dword(device, agp + PCI_AGP_COMMAND, bridge_agpstat); 7771da177e4SLinus Torvalds } 7781da177e4SLinus Torvalds } 7791da177e4SLinus Torvalds EXPORT_SYMBOL(agp_device_command); 7801da177e4SLinus Torvalds 7811da177e4SLinus Torvalds 7821da177e4SLinus Torvalds void get_agp_version(struct agp_bridge_data *bridge) 7831da177e4SLinus Torvalds { 7841da177e4SLinus Torvalds u32 ncapid; 7851da177e4SLinus Torvalds 7861da177e4SLinus Torvalds /* Exit early if already set by errata workarounds. */ 7871da177e4SLinus Torvalds if (bridge->major_version != 0) 7881da177e4SLinus Torvalds return; 7891da177e4SLinus Torvalds 7901da177e4SLinus Torvalds pci_read_config_dword(bridge->dev, bridge->capndx, &ncapid); 7911da177e4SLinus Torvalds bridge->major_version = (ncapid >> AGP_MAJOR_VERSION_SHIFT) & 0xf; 7921da177e4SLinus Torvalds bridge->minor_version = (ncapid >> AGP_MINOR_VERSION_SHIFT) & 0xf; 7931da177e4SLinus Torvalds } 7941da177e4SLinus Torvalds EXPORT_SYMBOL(get_agp_version); 7951da177e4SLinus Torvalds 7961da177e4SLinus Torvalds 7971da177e4SLinus Torvalds void agp_generic_enable(struct agp_bridge_data *bridge, u32 requested_mode) 7981da177e4SLinus Torvalds { 7991da177e4SLinus Torvalds u32 bridge_agpstat, temp; 8001da177e4SLinus Torvalds 8011da177e4SLinus Torvalds get_agp_version(agp_bridge); 8021da177e4SLinus Torvalds 803*e3cf6951SBjorn Helgaas dev_info(&agp_bridge->dev->dev, "AGP %d.%d bridge\n", 804*e3cf6951SBjorn Helgaas agp_bridge->major_version, agp_bridge->minor_version); 8051da177e4SLinus Torvalds 8061da177e4SLinus Torvalds pci_read_config_dword(agp_bridge->dev, 8071da177e4SLinus Torvalds agp_bridge->capndx + PCI_AGP_STATUS, &bridge_agpstat); 8081da177e4SLinus Torvalds 8091da177e4SLinus Torvalds bridge_agpstat = agp_collect_device_status(agp_bridge, requested_mode, bridge_agpstat); 8101da177e4SLinus Torvalds if (bridge_agpstat == 0) 8111da177e4SLinus Torvalds /* Something bad happened. FIXME: Return error code? */ 8121da177e4SLinus Torvalds return; 8131da177e4SLinus Torvalds 8141da177e4SLinus Torvalds bridge_agpstat |= AGPSTAT_AGP_ENABLE; 8151da177e4SLinus Torvalds 8161da177e4SLinus Torvalds /* Do AGP version specific frobbing. */ 8171da177e4SLinus Torvalds if (bridge->major_version >= 3) { 81866bb8bf8SDavid Mosberger if (bridge->mode & AGPSTAT_MODE_3_0) { 8191da177e4SLinus Torvalds /* If we have 3.5, we can do the isoch stuff. */ 8201da177e4SLinus Torvalds if (bridge->minor_version >= 5) 8211da177e4SLinus Torvalds agp_3_5_enable(bridge); 822c7258012SJoe Perches agp_device_command(bridge_agpstat, true); 8231da177e4SLinus Torvalds return; 8241da177e4SLinus Torvalds } else { 8251da177e4SLinus Torvalds /* Disable calibration cycle in RX91<1> when not in AGP3.0 mode of operation.*/ 8261da177e4SLinus Torvalds bridge_agpstat &= ~(7<<10) ; 8271da177e4SLinus Torvalds pci_read_config_dword(bridge->dev, 8281da177e4SLinus Torvalds bridge->capndx+AGPCTRL, &temp); 8291da177e4SLinus Torvalds temp |= (1<<9); 8301da177e4SLinus Torvalds pci_write_config_dword(bridge->dev, 8311da177e4SLinus Torvalds bridge->capndx+AGPCTRL, temp); 8321da177e4SLinus Torvalds 833*e3cf6951SBjorn Helgaas dev_info(&bridge->dev->dev, "bridge is in legacy mode, falling back to 2.x\n"); 8341da177e4SLinus Torvalds } 8351da177e4SLinus Torvalds } 8361da177e4SLinus Torvalds 8371da177e4SLinus Torvalds /* AGP v<3 */ 838c7258012SJoe Perches agp_device_command(bridge_agpstat, false); 8391da177e4SLinus Torvalds } 8401da177e4SLinus Torvalds EXPORT_SYMBOL(agp_generic_enable); 8411da177e4SLinus Torvalds 8421da177e4SLinus Torvalds 8431da177e4SLinus Torvalds int agp_generic_create_gatt_table(struct agp_bridge_data *bridge) 8441da177e4SLinus Torvalds { 8451da177e4SLinus Torvalds char *table; 8461da177e4SLinus Torvalds char *table_end; 8471da177e4SLinus Torvalds int size; 8481da177e4SLinus Torvalds int page_order; 8491da177e4SLinus Torvalds int num_entries; 8501da177e4SLinus Torvalds int i; 8511da177e4SLinus Torvalds void *temp; 8521da177e4SLinus Torvalds struct page *page; 8531da177e4SLinus Torvalds 8541da177e4SLinus Torvalds /* The generic routines can't handle 2 level gatt's */ 8551da177e4SLinus Torvalds if (bridge->driver->size_type == LVL2_APER_SIZE) 8561da177e4SLinus Torvalds return -EINVAL; 8571da177e4SLinus Torvalds 8581da177e4SLinus Torvalds table = NULL; 8591da177e4SLinus Torvalds i = bridge->aperture_size_idx; 8601da177e4SLinus Torvalds temp = bridge->current_size; 8611da177e4SLinus Torvalds size = page_order = num_entries = 0; 8621da177e4SLinus Torvalds 8631da177e4SLinus Torvalds if (bridge->driver->size_type != FIXED_APER_SIZE) { 8641da177e4SLinus Torvalds do { 8651da177e4SLinus Torvalds switch (bridge->driver->size_type) { 8661da177e4SLinus Torvalds case U8_APER_SIZE: 8671da177e4SLinus Torvalds size = A_SIZE_8(temp)->size; 8681da177e4SLinus Torvalds page_order = 8691da177e4SLinus Torvalds A_SIZE_8(temp)->page_order; 8701da177e4SLinus Torvalds num_entries = 8711da177e4SLinus Torvalds A_SIZE_8(temp)->num_entries; 8721da177e4SLinus Torvalds break; 8731da177e4SLinus Torvalds case U16_APER_SIZE: 8741da177e4SLinus Torvalds size = A_SIZE_16(temp)->size; 8751da177e4SLinus Torvalds page_order = A_SIZE_16(temp)->page_order; 8761da177e4SLinus Torvalds num_entries = A_SIZE_16(temp)->num_entries; 8771da177e4SLinus Torvalds break; 8781da177e4SLinus Torvalds case U32_APER_SIZE: 8791da177e4SLinus Torvalds size = A_SIZE_32(temp)->size; 8801da177e4SLinus Torvalds page_order = A_SIZE_32(temp)->page_order; 8811da177e4SLinus Torvalds num_entries = A_SIZE_32(temp)->num_entries; 8821da177e4SLinus Torvalds break; 8831da177e4SLinus Torvalds /* This case will never really happen. */ 8841da177e4SLinus Torvalds case FIXED_APER_SIZE: 8851da177e4SLinus Torvalds case LVL2_APER_SIZE: 8861da177e4SLinus Torvalds default: 8871da177e4SLinus Torvalds size = page_order = num_entries = 0; 8881da177e4SLinus Torvalds break; 8891da177e4SLinus Torvalds } 8901da177e4SLinus Torvalds 89107eee78eSKeir Fraser table = alloc_gatt_pages(page_order); 8921da177e4SLinus Torvalds 8931da177e4SLinus Torvalds if (table == NULL) { 8941da177e4SLinus Torvalds i++; 8951da177e4SLinus Torvalds switch (bridge->driver->size_type) { 8961da177e4SLinus Torvalds case U8_APER_SIZE: 8971da177e4SLinus Torvalds bridge->current_size = A_IDX8(bridge); 8981da177e4SLinus Torvalds break; 8991da177e4SLinus Torvalds case U16_APER_SIZE: 9001da177e4SLinus Torvalds bridge->current_size = A_IDX16(bridge); 9011da177e4SLinus Torvalds break; 9021da177e4SLinus Torvalds case U32_APER_SIZE: 9031da177e4SLinus Torvalds bridge->current_size = A_IDX32(bridge); 9041da177e4SLinus Torvalds break; 90589197e34SDave Jones /* These cases will never really happen. */ 9061da177e4SLinus Torvalds case FIXED_APER_SIZE: 9071da177e4SLinus Torvalds case LVL2_APER_SIZE: 9081da177e4SLinus Torvalds default: 9091da177e4SLinus Torvalds break; 9101da177e4SLinus Torvalds } 9111da177e4SLinus Torvalds temp = bridge->current_size; 9121da177e4SLinus Torvalds } else { 9131da177e4SLinus Torvalds bridge->aperture_size_idx = i; 9141da177e4SLinus Torvalds } 9151da177e4SLinus Torvalds } while (!table && (i < bridge->driver->num_aperture_sizes)); 9161da177e4SLinus Torvalds } else { 9171da177e4SLinus Torvalds size = ((struct aper_size_info_fixed *) temp)->size; 9181da177e4SLinus Torvalds page_order = ((struct aper_size_info_fixed *) temp)->page_order; 9191da177e4SLinus Torvalds num_entries = ((struct aper_size_info_fixed *) temp)->num_entries; 92007eee78eSKeir Fraser table = alloc_gatt_pages(page_order); 9211da177e4SLinus Torvalds } 9221da177e4SLinus Torvalds 9231da177e4SLinus Torvalds if (table == NULL) 9241da177e4SLinus Torvalds return -ENOMEM; 9251da177e4SLinus Torvalds 9261da177e4SLinus Torvalds table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1); 9271da177e4SLinus Torvalds 9281da177e4SLinus Torvalds for (page = virt_to_page(table); page <= virt_to_page(table_end); page++) 9291da177e4SLinus Torvalds SetPageReserved(page); 9301da177e4SLinus Torvalds 9311da177e4SLinus Torvalds bridge->gatt_table_real = (u32 *) table; 9321da177e4SLinus Torvalds agp_gatt_table = (void *)table; 9331da177e4SLinus Torvalds 9341da177e4SLinus Torvalds bridge->driver->cache_flush(); 935fcea424dSArjan van dev Ven #ifdef CONFIG_X86 936fcea424dSArjan van dev Ven set_memory_uc((unsigned long)table, 1 << page_order); 937fcea424dSArjan van dev Ven bridge->gatt_table = (void *)table; 938fcea424dSArjan van dev Ven #else 93907eee78eSKeir Fraser bridge->gatt_table = ioremap_nocache(virt_to_gart(table), 9401da177e4SLinus Torvalds (PAGE_SIZE * (1 << page_order))); 9411da177e4SLinus Torvalds bridge->driver->cache_flush(); 942fcea424dSArjan van dev Ven #endif 9431da177e4SLinus Torvalds 9441da177e4SLinus Torvalds if (bridge->gatt_table == NULL) { 9451da177e4SLinus Torvalds for (page = virt_to_page(table); page <= virt_to_page(table_end); page++) 9461da177e4SLinus Torvalds ClearPageReserved(page); 9471da177e4SLinus Torvalds 94807eee78eSKeir Fraser free_gatt_pages(table, page_order); 9491da177e4SLinus Torvalds 9501da177e4SLinus Torvalds return -ENOMEM; 9511da177e4SLinus Torvalds } 95207eee78eSKeir Fraser bridge->gatt_bus_addr = virt_to_gart(bridge->gatt_table_real); 9531da177e4SLinus Torvalds 9541da177e4SLinus Torvalds /* AK: bogus, should encode addresses > 4GB */ 9551da177e4SLinus Torvalds for (i = 0; i < num_entries; i++) { 9561da177e4SLinus Torvalds writel(bridge->scratch_page, bridge->gatt_table+i); 9571da177e4SLinus Torvalds readl(bridge->gatt_table+i); /* PCI Posting. */ 9581da177e4SLinus Torvalds } 9591da177e4SLinus Torvalds 9601da177e4SLinus Torvalds return 0; 9611da177e4SLinus Torvalds } 9621da177e4SLinus Torvalds EXPORT_SYMBOL(agp_generic_create_gatt_table); 9631da177e4SLinus Torvalds 9641da177e4SLinus Torvalds int agp_generic_free_gatt_table(struct agp_bridge_data *bridge) 9651da177e4SLinus Torvalds { 9661da177e4SLinus Torvalds int page_order; 9671da177e4SLinus Torvalds char *table, *table_end; 9681da177e4SLinus Torvalds void *temp; 9691da177e4SLinus Torvalds struct page *page; 9701da177e4SLinus Torvalds 9711da177e4SLinus Torvalds temp = bridge->current_size; 9721da177e4SLinus Torvalds 9731da177e4SLinus Torvalds switch (bridge->driver->size_type) { 9741da177e4SLinus Torvalds case U8_APER_SIZE: 9751da177e4SLinus Torvalds page_order = A_SIZE_8(temp)->page_order; 9761da177e4SLinus Torvalds break; 9771da177e4SLinus Torvalds case U16_APER_SIZE: 9781da177e4SLinus Torvalds page_order = A_SIZE_16(temp)->page_order; 9791da177e4SLinus Torvalds break; 9801da177e4SLinus Torvalds case U32_APER_SIZE: 9811da177e4SLinus Torvalds page_order = A_SIZE_32(temp)->page_order; 9821da177e4SLinus Torvalds break; 9831da177e4SLinus Torvalds case FIXED_APER_SIZE: 9841da177e4SLinus Torvalds page_order = A_SIZE_FIX(temp)->page_order; 9851da177e4SLinus Torvalds break; 9861da177e4SLinus Torvalds case LVL2_APER_SIZE: 9871da177e4SLinus Torvalds /* The generic routines can't deal with 2 level gatt's */ 9881da177e4SLinus Torvalds return -EINVAL; 9891da177e4SLinus Torvalds break; 9901da177e4SLinus Torvalds default: 9911da177e4SLinus Torvalds page_order = 0; 9921da177e4SLinus Torvalds break; 9931da177e4SLinus Torvalds } 9941da177e4SLinus Torvalds 9951da177e4SLinus Torvalds /* Do not worry about freeing memory, because if this is 9961da177e4SLinus Torvalds * called, then all agp memory is deallocated and removed 9971da177e4SLinus Torvalds * from the table. */ 9981da177e4SLinus Torvalds 999fcea424dSArjan van dev Ven #ifdef CONFIG_X86 1000fcea424dSArjan van dev Ven set_memory_wb((unsigned long)bridge->gatt_table, 1 << page_order); 1001fcea424dSArjan van dev Ven #else 10021da177e4SLinus Torvalds iounmap(bridge->gatt_table); 1003fcea424dSArjan van dev Ven #endif 10041da177e4SLinus Torvalds table = (char *) bridge->gatt_table_real; 10051da177e4SLinus Torvalds table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1); 10061da177e4SLinus Torvalds 10071da177e4SLinus Torvalds for (page = virt_to_page(table); page <= virt_to_page(table_end); page++) 10081da177e4SLinus Torvalds ClearPageReserved(page); 10091da177e4SLinus Torvalds 101007eee78eSKeir Fraser free_gatt_pages(bridge->gatt_table_real, page_order); 10111da177e4SLinus Torvalds 10121da177e4SLinus Torvalds agp_gatt_table = NULL; 10131da177e4SLinus Torvalds bridge->gatt_table = NULL; 10141da177e4SLinus Torvalds bridge->gatt_table_real = NULL; 10151da177e4SLinus Torvalds bridge->gatt_bus_addr = 0; 10161da177e4SLinus Torvalds 10171da177e4SLinus Torvalds return 0; 10181da177e4SLinus Torvalds } 10191da177e4SLinus Torvalds EXPORT_SYMBOL(agp_generic_free_gatt_table); 10201da177e4SLinus Torvalds 10211da177e4SLinus Torvalds 10221da177e4SLinus Torvalds int agp_generic_insert_memory(struct agp_memory * mem, off_t pg_start, int type) 10231da177e4SLinus Torvalds { 10241da177e4SLinus Torvalds int num_entries; 10251da177e4SLinus Torvalds size_t i; 10261da177e4SLinus Torvalds off_t j; 10271da177e4SLinus Torvalds void *temp; 10281da177e4SLinus Torvalds struct agp_bridge_data *bridge; 1029a030ce44SThomas Hellstrom int mask_type; 10301da177e4SLinus Torvalds 10311da177e4SLinus Torvalds bridge = mem->bridge; 10321da177e4SLinus Torvalds if (!bridge) 10331da177e4SLinus Torvalds return -EINVAL; 10341da177e4SLinus Torvalds 10355aa80c72SThomas Hellstrom if (mem->page_count == 0) 10365aa80c72SThomas Hellstrom return 0; 10375aa80c72SThomas Hellstrom 10381da177e4SLinus Torvalds temp = bridge->current_size; 10391da177e4SLinus Torvalds 10401da177e4SLinus Torvalds switch (bridge->driver->size_type) { 10411da177e4SLinus Torvalds case U8_APER_SIZE: 10421da177e4SLinus Torvalds num_entries = A_SIZE_8(temp)->num_entries; 10431da177e4SLinus Torvalds break; 10441da177e4SLinus Torvalds case U16_APER_SIZE: 10451da177e4SLinus Torvalds num_entries = A_SIZE_16(temp)->num_entries; 10461da177e4SLinus Torvalds break; 10471da177e4SLinus Torvalds case U32_APER_SIZE: 10481da177e4SLinus Torvalds num_entries = A_SIZE_32(temp)->num_entries; 10491da177e4SLinus Torvalds break; 10501da177e4SLinus Torvalds case FIXED_APER_SIZE: 10511da177e4SLinus Torvalds num_entries = A_SIZE_FIX(temp)->num_entries; 10521da177e4SLinus Torvalds break; 10531da177e4SLinus Torvalds case LVL2_APER_SIZE: 10541da177e4SLinus Torvalds /* The generic routines can't deal with 2 level gatt's */ 10551da177e4SLinus Torvalds return -EINVAL; 10561da177e4SLinus Torvalds break; 10571da177e4SLinus Torvalds default: 10581da177e4SLinus Torvalds num_entries = 0; 10591da177e4SLinus Torvalds break; 10601da177e4SLinus Torvalds } 10611da177e4SLinus Torvalds 10621da177e4SLinus Torvalds num_entries -= agp_memory_reserved/PAGE_SIZE; 10631da177e4SLinus Torvalds if (num_entries < 0) num_entries = 0; 10641da177e4SLinus Torvalds 10651c14cfbbSAndrew Morton if (type != mem->type) 1066a030ce44SThomas Hellstrom return -EINVAL; 1067a030ce44SThomas Hellstrom 1068a030ce44SThomas Hellstrom mask_type = bridge->driver->agp_type_to_mask_type(bridge, type); 1069a030ce44SThomas Hellstrom if (mask_type != 0) { 10701da177e4SLinus Torvalds /* The generic routines know nothing of memory types */ 10711da177e4SLinus Torvalds return -EINVAL; 10721da177e4SLinus Torvalds } 10731da177e4SLinus Torvalds 10741da177e4SLinus Torvalds /* AK: could wrap */ 10751da177e4SLinus Torvalds if ((pg_start + mem->page_count) > num_entries) 10761da177e4SLinus Torvalds return -EINVAL; 10771da177e4SLinus Torvalds 10781da177e4SLinus Torvalds j = pg_start; 10791da177e4SLinus Torvalds 10801da177e4SLinus Torvalds while (j < (pg_start + mem->page_count)) { 10811da177e4SLinus Torvalds if (!PGE_EMPTY(bridge, readl(bridge->gatt_table+j))) 10821da177e4SLinus Torvalds return -EBUSY; 10831da177e4SLinus Torvalds j++; 10841da177e4SLinus Torvalds } 10851da177e4SLinus Torvalds 1086c7258012SJoe Perches if (!mem->is_flushed) { 10871da177e4SLinus Torvalds bridge->driver->cache_flush(); 1088c7258012SJoe Perches mem->is_flushed = true; 10891da177e4SLinus Torvalds } 10901da177e4SLinus Torvalds 10911da177e4SLinus Torvalds for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { 1092a030ce44SThomas Hellstrom writel(bridge->driver->mask_memory(bridge, mem->memory[i], mask_type), 1093a030ce44SThomas Hellstrom bridge->gatt_table+j); 10941da177e4SLinus Torvalds } 10955aa80c72SThomas Hellstrom readl(bridge->gatt_table+j-1); /* PCI Posting. */ 10961da177e4SLinus Torvalds 10971da177e4SLinus Torvalds bridge->driver->tlb_flush(mem); 10981da177e4SLinus Torvalds return 0; 10991da177e4SLinus Torvalds } 11001da177e4SLinus Torvalds EXPORT_SYMBOL(agp_generic_insert_memory); 11011da177e4SLinus Torvalds 11021da177e4SLinus Torvalds 11031da177e4SLinus Torvalds int agp_generic_remove_memory(struct agp_memory *mem, off_t pg_start, int type) 11041da177e4SLinus Torvalds { 11051da177e4SLinus Torvalds size_t i; 11061da177e4SLinus Torvalds struct agp_bridge_data *bridge; 1107a030ce44SThomas Hellstrom int mask_type; 11081da177e4SLinus Torvalds 11091da177e4SLinus Torvalds bridge = mem->bridge; 11101da177e4SLinus Torvalds if (!bridge) 11111da177e4SLinus Torvalds return -EINVAL; 11121da177e4SLinus Torvalds 11135aa80c72SThomas Hellstrom if (mem->page_count == 0) 11145aa80c72SThomas Hellstrom return 0; 11155aa80c72SThomas Hellstrom 1116a030ce44SThomas Hellstrom if (type != mem->type) 1117a030ce44SThomas Hellstrom return -EINVAL; 1118a030ce44SThomas Hellstrom 1119a030ce44SThomas Hellstrom mask_type = bridge->driver->agp_type_to_mask_type(bridge, type); 1120a030ce44SThomas Hellstrom if (mask_type != 0) { 11211da177e4SLinus Torvalds /* The generic routines know nothing of memory types */ 11221da177e4SLinus Torvalds return -EINVAL; 11231da177e4SLinus Torvalds } 11241da177e4SLinus Torvalds 11251da177e4SLinus Torvalds /* AK: bogus, should encode addresses > 4GB */ 11261da177e4SLinus Torvalds for (i = pg_start; i < (mem->page_count + pg_start); i++) { 11271da177e4SLinus Torvalds writel(bridge->scratch_page, bridge->gatt_table+i); 11281da177e4SLinus Torvalds } 11295aa80c72SThomas Hellstrom readl(bridge->gatt_table+i-1); /* PCI Posting. */ 11301da177e4SLinus Torvalds 11311da177e4SLinus Torvalds bridge->driver->tlb_flush(mem); 11321da177e4SLinus Torvalds return 0; 11331da177e4SLinus Torvalds } 11341da177e4SLinus Torvalds EXPORT_SYMBOL(agp_generic_remove_memory); 11351da177e4SLinus Torvalds 11361da177e4SLinus Torvalds struct agp_memory *agp_generic_alloc_by_type(size_t page_count, int type) 11371da177e4SLinus Torvalds { 11381da177e4SLinus Torvalds return NULL; 11391da177e4SLinus Torvalds } 11401da177e4SLinus Torvalds EXPORT_SYMBOL(agp_generic_alloc_by_type); 11411da177e4SLinus Torvalds 11421da177e4SLinus Torvalds void agp_generic_free_by_type(struct agp_memory *curr) 11431da177e4SLinus Torvalds { 1144a030ce44SThomas Hellstrom agp_free_page_array(curr); 11451da177e4SLinus Torvalds agp_free_key(curr->key); 11461da177e4SLinus Torvalds kfree(curr); 11471da177e4SLinus Torvalds } 11481da177e4SLinus Torvalds EXPORT_SYMBOL(agp_generic_free_by_type); 11491da177e4SLinus Torvalds 1150a030ce44SThomas Hellstrom struct agp_memory *agp_generic_alloc_user(size_t page_count, int type) 1151a030ce44SThomas Hellstrom { 1152a030ce44SThomas Hellstrom struct agp_memory *new; 1153a030ce44SThomas Hellstrom int i; 1154a030ce44SThomas Hellstrom int pages; 1155a030ce44SThomas Hellstrom 1156a030ce44SThomas Hellstrom pages = (page_count + ENTRIES_PER_PAGE - 1) / ENTRIES_PER_PAGE; 1157a030ce44SThomas Hellstrom new = agp_create_user_memory(page_count); 1158a030ce44SThomas Hellstrom if (new == NULL) 1159a030ce44SThomas Hellstrom return NULL; 1160a030ce44SThomas Hellstrom 11611c14cfbbSAndrew Morton for (i = 0; i < page_count; i++) 1162a030ce44SThomas Hellstrom new->memory[i] = 0; 1163a030ce44SThomas Hellstrom new->page_count = 0; 1164a030ce44SThomas Hellstrom new->type = type; 1165a030ce44SThomas Hellstrom new->num_scratch_pages = pages; 1166a030ce44SThomas Hellstrom 1167a030ce44SThomas Hellstrom return new; 1168a030ce44SThomas Hellstrom } 1169a030ce44SThomas Hellstrom EXPORT_SYMBOL(agp_generic_alloc_user); 1170a030ce44SThomas Hellstrom 11711da177e4SLinus Torvalds /* 11721da177e4SLinus Torvalds * Basic Page Allocation Routines - 11731da177e4SLinus Torvalds * These routines handle page allocation and by default they reserve the allocated 11741da177e4SLinus Torvalds * memory. They also handle incrementing the current_memory_agp value, Which is checked 11751da177e4SLinus Torvalds * against a maximum value. 11761da177e4SLinus Torvalds */ 11771da177e4SLinus Torvalds 11781da177e4SLinus Torvalds void *agp_generic_alloc_page(struct agp_bridge_data *bridge) 11791da177e4SLinus Torvalds { 11801da177e4SLinus Torvalds struct page * page; 11811da177e4SLinus Torvalds 118266c669baSLinus Torvalds page = alloc_page(GFP_KERNEL | GFP_DMA32); 11831da177e4SLinus Torvalds if (page == NULL) 11841da177e4SLinus Torvalds return NULL; 11851da177e4SLinus Torvalds 11861da177e4SLinus Torvalds map_page_into_agp(page); 11871da177e4SLinus Torvalds 11881da177e4SLinus Torvalds get_page(page); 11891da177e4SLinus Torvalds atomic_inc(&agp_bridge->current_memory_agp); 11901da177e4SLinus Torvalds return page_address(page); 11911da177e4SLinus Torvalds } 11921da177e4SLinus Torvalds EXPORT_SYMBOL(agp_generic_alloc_page); 11931da177e4SLinus Torvalds 11941da177e4SLinus Torvalds 1195a2721e99SDave Airlie void agp_generic_destroy_page(void *addr, int flags) 11961da177e4SLinus Torvalds { 11971da177e4SLinus Torvalds struct page *page; 11981da177e4SLinus Torvalds 11991da177e4SLinus Torvalds if (addr == NULL) 12001da177e4SLinus Torvalds return; 12011da177e4SLinus Torvalds 12021da177e4SLinus Torvalds page = virt_to_page(addr); 1203a2721e99SDave Airlie if (flags & AGP_PAGE_DESTROY_UNMAP) 12041da177e4SLinus Torvalds unmap_page_from_agp(page); 1205a2721e99SDave Airlie 1206a2721e99SDave Airlie if (flags & AGP_PAGE_DESTROY_FREE) { 12071da177e4SLinus Torvalds put_page(page); 12081da177e4SLinus Torvalds free_page((unsigned long)addr); 12091da177e4SLinus Torvalds atomic_dec(&agp_bridge->current_memory_agp); 12101da177e4SLinus Torvalds } 1211a2721e99SDave Airlie } 12121da177e4SLinus Torvalds EXPORT_SYMBOL(agp_generic_destroy_page); 12131da177e4SLinus Torvalds 12141da177e4SLinus Torvalds /* End Basic Page Allocation Routines */ 12151da177e4SLinus Torvalds 12161da177e4SLinus Torvalds 12171da177e4SLinus Torvalds /** 12181da177e4SLinus Torvalds * agp_enable - initialise the agp point-to-point connection. 12191da177e4SLinus Torvalds * 12201da177e4SLinus Torvalds * @mode: agp mode register value to configure with. 12211da177e4SLinus Torvalds */ 12221da177e4SLinus Torvalds void agp_enable(struct agp_bridge_data *bridge, u32 mode) 12231da177e4SLinus Torvalds { 12241da177e4SLinus Torvalds if (!bridge) 12251da177e4SLinus Torvalds return; 12261da177e4SLinus Torvalds bridge->driver->agp_enable(bridge, mode); 12271da177e4SLinus Torvalds } 12281da177e4SLinus Torvalds EXPORT_SYMBOL(agp_enable); 12291da177e4SLinus Torvalds 12301da177e4SLinus Torvalds /* When we remove the global variable agp_bridge from all drivers 12311da177e4SLinus Torvalds * then agp_alloc_bridge and agp_generic_find_bridge need to be updated 12321da177e4SLinus Torvalds */ 12331da177e4SLinus Torvalds 12341da177e4SLinus Torvalds struct agp_bridge_data *agp_generic_find_bridge(struct pci_dev *pdev) 12351da177e4SLinus Torvalds { 12361da177e4SLinus Torvalds if (list_empty(&agp_bridges)) 12371da177e4SLinus Torvalds return NULL; 12381da177e4SLinus Torvalds 12391da177e4SLinus Torvalds return agp_bridge; 12401da177e4SLinus Torvalds } 12411da177e4SLinus Torvalds 12421da177e4SLinus Torvalds static void ipi_handler(void *null) 12431da177e4SLinus Torvalds { 12441da177e4SLinus Torvalds flush_agp_cache(); 12451da177e4SLinus Torvalds } 12461da177e4SLinus Torvalds 12471da177e4SLinus Torvalds void global_cache_flush(void) 12481da177e4SLinus Torvalds { 124915c8b6c1SJens Axboe if (on_each_cpu(ipi_handler, NULL, 1) != 0) 12501da177e4SLinus Torvalds panic(PFX "timed out waiting for the other CPUs!\n"); 12511da177e4SLinus Torvalds } 12521da177e4SLinus Torvalds EXPORT_SYMBOL(global_cache_flush); 12531da177e4SLinus Torvalds 12541da177e4SLinus Torvalds unsigned long agp_generic_mask_memory(struct agp_bridge_data *bridge, 12551da177e4SLinus Torvalds unsigned long addr, int type) 12561da177e4SLinus Torvalds { 12571da177e4SLinus Torvalds /* memory type is ignored in the generic routine */ 12581da177e4SLinus Torvalds if (bridge->driver->masks) 12591da177e4SLinus Torvalds return addr | bridge->driver->masks[0].mask; 12601da177e4SLinus Torvalds else 12611da177e4SLinus Torvalds return addr; 12621da177e4SLinus Torvalds } 12631da177e4SLinus Torvalds EXPORT_SYMBOL(agp_generic_mask_memory); 12641da177e4SLinus Torvalds 1265a030ce44SThomas Hellstrom int agp_generic_type_to_mask_type(struct agp_bridge_data *bridge, 1266a030ce44SThomas Hellstrom int type) 1267a030ce44SThomas Hellstrom { 1268a030ce44SThomas Hellstrom if (type >= AGP_USER_TYPES) 1269a030ce44SThomas Hellstrom return 0; 1270a030ce44SThomas Hellstrom return type; 1271a030ce44SThomas Hellstrom } 1272a030ce44SThomas Hellstrom EXPORT_SYMBOL(agp_generic_type_to_mask_type); 1273a030ce44SThomas Hellstrom 12741da177e4SLinus Torvalds /* 12751da177e4SLinus Torvalds * These functions are implemented according to the AGPv3 spec, 12761da177e4SLinus Torvalds * which covers implementation details that had previously been 12771da177e4SLinus Torvalds * left open. 12781da177e4SLinus Torvalds */ 12791da177e4SLinus Torvalds 12801da177e4SLinus Torvalds int agp3_generic_fetch_size(void) 12811da177e4SLinus Torvalds { 12821da177e4SLinus Torvalds u16 temp_size; 12831da177e4SLinus Torvalds int i; 12841da177e4SLinus Torvalds struct aper_size_info_16 *values; 12851da177e4SLinus Torvalds 12861da177e4SLinus Torvalds pci_read_config_word(agp_bridge->dev, agp_bridge->capndx+AGPAPSIZE, &temp_size); 12871da177e4SLinus Torvalds values = A_SIZE_16(agp_bridge->driver->aperture_sizes); 12881da177e4SLinus Torvalds 12891da177e4SLinus Torvalds for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++) { 12901da177e4SLinus Torvalds if (temp_size == values[i].size_value) { 12911da177e4SLinus Torvalds agp_bridge->previous_size = 12921da177e4SLinus Torvalds agp_bridge->current_size = (void *) (values + i); 12931da177e4SLinus Torvalds 12941da177e4SLinus Torvalds agp_bridge->aperture_size_idx = i; 12951da177e4SLinus Torvalds return values[i].size; 12961da177e4SLinus Torvalds } 12971da177e4SLinus Torvalds } 12981da177e4SLinus Torvalds return 0; 12991da177e4SLinus Torvalds } 13001da177e4SLinus Torvalds EXPORT_SYMBOL(agp3_generic_fetch_size); 13011da177e4SLinus Torvalds 13021da177e4SLinus Torvalds void agp3_generic_tlbflush(struct agp_memory *mem) 13031da177e4SLinus Torvalds { 13041da177e4SLinus Torvalds u32 ctrl; 13051da177e4SLinus Torvalds pci_read_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, &ctrl); 13061da177e4SLinus Torvalds pci_write_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, ctrl & ~AGPCTRL_GTLBEN); 13071da177e4SLinus Torvalds pci_write_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, ctrl); 13081da177e4SLinus Torvalds } 13091da177e4SLinus Torvalds EXPORT_SYMBOL(agp3_generic_tlbflush); 13101da177e4SLinus Torvalds 13111da177e4SLinus Torvalds int agp3_generic_configure(void) 13121da177e4SLinus Torvalds { 13131da177e4SLinus Torvalds u32 temp; 13141da177e4SLinus Torvalds struct aper_size_info_16 *current_size; 13151da177e4SLinus Torvalds 13161da177e4SLinus Torvalds current_size = A_SIZE_16(agp_bridge->current_size); 13171da177e4SLinus Torvalds 13181da177e4SLinus Torvalds pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp); 13191da177e4SLinus Torvalds agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); 13201da177e4SLinus Torvalds 13211da177e4SLinus Torvalds /* set aperture size */ 13221da177e4SLinus Torvalds pci_write_config_word(agp_bridge->dev, agp_bridge->capndx+AGPAPSIZE, current_size->size_value); 13231da177e4SLinus Torvalds /* set gart pointer */ 13241da177e4SLinus Torvalds pci_write_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPGARTLO, agp_bridge->gatt_bus_addr); 13251da177e4SLinus Torvalds /* enable aperture and GTLB */ 13261da177e4SLinus Torvalds pci_read_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, &temp); 13271da177e4SLinus Torvalds pci_write_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, temp | AGPCTRL_APERENB | AGPCTRL_GTLBEN); 13281da177e4SLinus Torvalds return 0; 13291da177e4SLinus Torvalds } 13301da177e4SLinus Torvalds EXPORT_SYMBOL(agp3_generic_configure); 13311da177e4SLinus Torvalds 13321da177e4SLinus Torvalds void agp3_generic_cleanup(void) 13331da177e4SLinus Torvalds { 13341da177e4SLinus Torvalds u32 ctrl; 13351da177e4SLinus Torvalds pci_read_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, &ctrl); 13361da177e4SLinus Torvalds pci_write_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, ctrl & ~AGPCTRL_APERENB); 13371da177e4SLinus Torvalds } 13381da177e4SLinus Torvalds EXPORT_SYMBOL(agp3_generic_cleanup); 13391da177e4SLinus Torvalds 1340e5524f35SDave Jones const struct aper_size_info_16 agp3_generic_sizes[AGP_GENERIC_SIZES_ENTRIES] = 13411da177e4SLinus Torvalds { 13421da177e4SLinus Torvalds {4096, 1048576, 10,0x000}, 13431da177e4SLinus Torvalds {2048, 524288, 9, 0x800}, 13441da177e4SLinus Torvalds {1024, 262144, 8, 0xc00}, 13451da177e4SLinus Torvalds { 512, 131072, 7, 0xe00}, 13461da177e4SLinus Torvalds { 256, 65536, 6, 0xf00}, 13471da177e4SLinus Torvalds { 128, 32768, 5, 0xf20}, 13481da177e4SLinus Torvalds { 64, 16384, 4, 0xf30}, 13491da177e4SLinus Torvalds { 32, 8192, 3, 0xf38}, 13501da177e4SLinus Torvalds { 16, 4096, 2, 0xf3c}, 13511da177e4SLinus Torvalds { 8, 2048, 1, 0xf3e}, 13521da177e4SLinus Torvalds { 4, 1024, 0, 0xf3f} 13531da177e4SLinus Torvalds }; 13541da177e4SLinus Torvalds EXPORT_SYMBOL(agp3_generic_sizes); 13551da177e4SLinus Torvalds 1356