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