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/pagemap.h> 331da177e4SLinus Torvalds #include <linux/miscdevice.h> 341da177e4SLinus Torvalds #include <linux/pm.h> 351da177e4SLinus Torvalds #include <linux/agp_backend.h> 361da177e4SLinus Torvalds #include <linux/vmalloc.h> 371da177e4SLinus Torvalds #include <linux/dma-mapping.h> 381da177e4SLinus Torvalds #include <linux/mm.h> 39e8edc6e0SAlexey Dobriyan #include <linux/sched.h> 405a0e3ad6STejun Heo #include <linux/slab.h> 411da177e4SLinus Torvalds #include <asm/io.h> 42*e47036b4SLaura Abbott #ifdef CONFIG_X86 43*e47036b4SLaura Abbott #include <asm/set_memory.h> 44*e47036b4SLaura Abbott #endif 451da177e4SLinus Torvalds #include <asm/pgtable.h> 461da177e4SLinus Torvalds #include "agp.h" 471da177e4SLinus Torvalds 481da177e4SLinus Torvalds __u32 *agp_gatt_table; 491da177e4SLinus Torvalds int agp_memory_reserved; 501da177e4SLinus Torvalds 511da177e4SLinus Torvalds /* 521da177e4SLinus Torvalds * Needed by the Nforce GART driver for the time being. Would be 531da177e4SLinus Torvalds * nice to do this some other way instead of needing this export. 541da177e4SLinus Torvalds */ 551da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(agp_memory_reserved); 561da177e4SLinus Torvalds 571da177e4SLinus Torvalds /* 581da177e4SLinus Torvalds * Generic routines for handling agp_memory structures - 591da177e4SLinus Torvalds * They use the basic page allocation routines to do the brunt of the work. 601da177e4SLinus Torvalds */ 611da177e4SLinus Torvalds 621da177e4SLinus Torvalds void agp_free_key(int key) 631da177e4SLinus Torvalds { 641da177e4SLinus Torvalds if (key < 0) 651da177e4SLinus Torvalds return; 661da177e4SLinus Torvalds 671da177e4SLinus Torvalds if (key < MAXKEY) 681da177e4SLinus Torvalds clear_bit(key, agp_bridge->key_list); 691da177e4SLinus Torvalds } 701da177e4SLinus Torvalds EXPORT_SYMBOL(agp_free_key); 711da177e4SLinus Torvalds 721da177e4SLinus Torvalds 731da177e4SLinus Torvalds static int agp_get_key(void) 741da177e4SLinus Torvalds { 751da177e4SLinus Torvalds int bit; 761da177e4SLinus Torvalds 771da177e4SLinus Torvalds bit = find_first_zero_bit(agp_bridge->key_list, MAXKEY); 781da177e4SLinus Torvalds if (bit < MAXKEY) { 791da177e4SLinus Torvalds set_bit(bit, agp_bridge->key_list); 801da177e4SLinus Torvalds return bit; 811da177e4SLinus Torvalds } 821da177e4SLinus Torvalds return -1; 831da177e4SLinus Torvalds } 841da177e4SLinus Torvalds 85a030ce44SThomas Hellstrom /* 86a030ce44SThomas Hellstrom * Use kmalloc if possible for the page list. Otherwise fall back to 87a030ce44SThomas Hellstrom * vmalloc. This speeds things up and also saves memory for small AGP 88a030ce44SThomas Hellstrom * regions. 89a030ce44SThomas Hellstrom */ 90a030ce44SThomas Hellstrom 91a030ce44SThomas Hellstrom void agp_alloc_page_array(size_t size, struct agp_memory *mem) 92a030ce44SThomas Hellstrom { 93752ade68SMichal Hocko mem->pages = kvmalloc(size, GFP_KERNEL); 94a030ce44SThomas Hellstrom } 95a030ce44SThomas Hellstrom EXPORT_SYMBOL(agp_alloc_page_array); 96a030ce44SThomas Hellstrom 97a030ce44SThomas Hellstrom static struct agp_memory *agp_create_user_memory(unsigned long num_agp_pages) 98a030ce44SThomas Hellstrom { 99a030ce44SThomas Hellstrom struct agp_memory *new; 100a030ce44SThomas Hellstrom unsigned long alloc_size = num_agp_pages*sizeof(struct page *); 101a030ce44SThomas Hellstrom 102b522f021SVasiliy Kulikov if (INT_MAX/sizeof(struct page *) < num_agp_pages) 103b522f021SVasiliy Kulikov return NULL; 104b522f021SVasiliy Kulikov 1051c14cfbbSAndrew Morton new = kzalloc(sizeof(struct agp_memory), GFP_KERNEL); 106a030ce44SThomas Hellstrom if (new == NULL) 107a030ce44SThomas Hellstrom return NULL; 108a030ce44SThomas Hellstrom 109a030ce44SThomas Hellstrom new->key = agp_get_key(); 110a030ce44SThomas Hellstrom 111a030ce44SThomas Hellstrom if (new->key < 0) { 112a030ce44SThomas Hellstrom kfree(new); 113a030ce44SThomas Hellstrom return NULL; 114a030ce44SThomas Hellstrom } 115a030ce44SThomas Hellstrom 116a030ce44SThomas Hellstrom agp_alloc_page_array(alloc_size, new); 117a030ce44SThomas Hellstrom 11807613ba2SDave Airlie if (new->pages == NULL) { 119a030ce44SThomas Hellstrom agp_free_key(new->key); 120a030ce44SThomas Hellstrom kfree(new); 121a030ce44SThomas Hellstrom return NULL; 122a030ce44SThomas Hellstrom } 123a030ce44SThomas Hellstrom new->num_scratch_pages = 0; 124a030ce44SThomas Hellstrom return new; 125a030ce44SThomas Hellstrom } 126a030ce44SThomas Hellstrom 1271da177e4SLinus Torvalds struct agp_memory *agp_create_memory(int scratch_pages) 1281da177e4SLinus Torvalds { 1291da177e4SLinus Torvalds struct agp_memory *new; 1301da177e4SLinus Torvalds 1310ea27d9fSDave Jones new = kzalloc(sizeof(struct agp_memory), GFP_KERNEL); 1321da177e4SLinus Torvalds if (new == NULL) 1331da177e4SLinus Torvalds return NULL; 1341da177e4SLinus Torvalds 1351da177e4SLinus Torvalds new->key = agp_get_key(); 1361da177e4SLinus Torvalds 1371da177e4SLinus Torvalds if (new->key < 0) { 1381da177e4SLinus Torvalds kfree(new); 1391da177e4SLinus Torvalds return NULL; 1401da177e4SLinus Torvalds } 141a030ce44SThomas Hellstrom 142a030ce44SThomas Hellstrom agp_alloc_page_array(PAGE_SIZE * scratch_pages, new); 1431da177e4SLinus Torvalds 14407613ba2SDave Airlie if (new->pages == NULL) { 1451da177e4SLinus Torvalds agp_free_key(new->key); 1461da177e4SLinus Torvalds kfree(new); 1471da177e4SLinus Torvalds return NULL; 1481da177e4SLinus Torvalds } 1491da177e4SLinus Torvalds new->num_scratch_pages = scratch_pages; 150a030ce44SThomas Hellstrom new->type = AGP_NORMAL_MEMORY; 1511da177e4SLinus Torvalds return new; 1521da177e4SLinus Torvalds } 1531da177e4SLinus Torvalds EXPORT_SYMBOL(agp_create_memory); 1541da177e4SLinus Torvalds 1551da177e4SLinus Torvalds /** 1561da177e4SLinus Torvalds * agp_free_memory - free memory associated with an agp_memory pointer. 1571da177e4SLinus Torvalds * 1581da177e4SLinus Torvalds * @curr: agp_memory pointer to be freed. 1591da177e4SLinus Torvalds * 1601da177e4SLinus Torvalds * It is the only function that can be called when the backend is not owned 1611da177e4SLinus Torvalds * by the caller. (So it can free memory on client death.) 1621da177e4SLinus Torvalds */ 1631da177e4SLinus Torvalds void agp_free_memory(struct agp_memory *curr) 1641da177e4SLinus Torvalds { 1651da177e4SLinus Torvalds size_t i; 1661da177e4SLinus Torvalds 1671da177e4SLinus Torvalds if (curr == NULL) 1681da177e4SLinus Torvalds return; 1691da177e4SLinus Torvalds 170c7258012SJoe Perches if (curr->is_bound) 1711da177e4SLinus Torvalds agp_unbind_memory(curr); 1721da177e4SLinus Torvalds 173a030ce44SThomas Hellstrom if (curr->type >= AGP_USER_TYPES) { 174a030ce44SThomas Hellstrom agp_generic_free_by_type(curr); 175a030ce44SThomas Hellstrom return; 176a030ce44SThomas Hellstrom } 177a030ce44SThomas Hellstrom 1781da177e4SLinus Torvalds if (curr->type != 0) { 1791da177e4SLinus Torvalds curr->bridge->driver->free_by_type(curr); 1801da177e4SLinus Torvalds return; 1811da177e4SLinus Torvalds } 1821da177e4SLinus Torvalds if (curr->page_count != 0) { 183bd07928cSShaohua Li if (curr->bridge->driver->agp_destroy_pages) { 184bd07928cSShaohua Li curr->bridge->driver->agp_destroy_pages(curr); 185bd07928cSShaohua Li } else { 186bd07928cSShaohua Li 1871da177e4SLinus Torvalds for (i = 0; i < curr->page_count; i++) { 188bd07928cSShaohua Li curr->bridge->driver->agp_destroy_page( 18907613ba2SDave Airlie curr->pages[i], 190da503fa6SJan Beulich AGP_PAGE_DESTROY_UNMAP); 1911da177e4SLinus Torvalds } 192a2721e99SDave Airlie for (i = 0; i < curr->page_count; i++) { 193bd07928cSShaohua Li curr->bridge->driver->agp_destroy_page( 19407613ba2SDave Airlie curr->pages[i], 195da503fa6SJan Beulich AGP_PAGE_DESTROY_FREE); 196a2721e99SDave Airlie } 1971da177e4SLinus Torvalds } 198bd07928cSShaohua Li } 1991da177e4SLinus Torvalds agp_free_key(curr->key); 200a030ce44SThomas Hellstrom agp_free_page_array(curr); 2011da177e4SLinus Torvalds kfree(curr); 2021da177e4SLinus Torvalds } 2031da177e4SLinus Torvalds EXPORT_SYMBOL(agp_free_memory); 2041da177e4SLinus Torvalds 2051da177e4SLinus Torvalds #define ENTRIES_PER_PAGE (PAGE_SIZE / sizeof(unsigned long)) 2061da177e4SLinus Torvalds 2071da177e4SLinus Torvalds /** 2081da177e4SLinus Torvalds * agp_allocate_memory - allocate a group of pages of a certain type. 2091da177e4SLinus Torvalds * 2101da177e4SLinus Torvalds * @page_count: size_t argument of the number of pages 2111da177e4SLinus Torvalds * @type: u32 argument of the type of memory to be allocated. 2121da177e4SLinus Torvalds * 2131da177e4SLinus Torvalds * Every agp bridge device will allow you to allocate AGP_NORMAL_MEMORY which 2141da177e4SLinus Torvalds * maps to physical ram. Any other type is device dependent. 2151da177e4SLinus Torvalds * 2161da177e4SLinus Torvalds * It returns NULL whenever memory is unavailable. 2171da177e4SLinus Torvalds */ 2181da177e4SLinus Torvalds struct agp_memory *agp_allocate_memory(struct agp_bridge_data *bridge, 2191da177e4SLinus Torvalds size_t page_count, u32 type) 2201da177e4SLinus Torvalds { 2211da177e4SLinus Torvalds int scratch_pages; 2221da177e4SLinus Torvalds struct agp_memory *new; 2231da177e4SLinus Torvalds size_t i; 224b522f021SVasiliy Kulikov int cur_memory; 2251da177e4SLinus Torvalds 2261da177e4SLinus Torvalds if (!bridge) 2271da177e4SLinus Torvalds return NULL; 2281da177e4SLinus Torvalds 229b522f021SVasiliy Kulikov cur_memory = atomic_read(&bridge->current_memory_agp); 230b522f021SVasiliy Kulikov if ((cur_memory + page_count > bridge->max_memory_agp) || 231b522f021SVasiliy Kulikov (cur_memory + page_count < page_count)) 2321da177e4SLinus Torvalds return NULL; 2331da177e4SLinus Torvalds 234a030ce44SThomas Hellstrom if (type >= AGP_USER_TYPES) { 235a030ce44SThomas Hellstrom new = agp_generic_alloc_user(page_count, type); 236a030ce44SThomas Hellstrom if (new) 237a030ce44SThomas Hellstrom new->bridge = bridge; 238a030ce44SThomas Hellstrom return new; 239a030ce44SThomas Hellstrom } 240a030ce44SThomas Hellstrom 2411da177e4SLinus Torvalds if (type != 0) { 2421da177e4SLinus Torvalds new = bridge->driver->alloc_by_type(page_count, type); 2431da177e4SLinus Torvalds if (new) 2441da177e4SLinus Torvalds new->bridge = bridge; 2451da177e4SLinus Torvalds return new; 2461da177e4SLinus Torvalds } 2471da177e4SLinus Torvalds 2481da177e4SLinus Torvalds scratch_pages = (page_count + ENTRIES_PER_PAGE - 1) / ENTRIES_PER_PAGE; 2491da177e4SLinus Torvalds 2501da177e4SLinus Torvalds new = agp_create_memory(scratch_pages); 2511da177e4SLinus Torvalds 2521da177e4SLinus Torvalds if (new == NULL) 2531da177e4SLinus Torvalds return NULL; 2541da177e4SLinus Torvalds 25537acee10SShaohua Li if (bridge->driver->agp_alloc_pages) { 25637acee10SShaohua Li if (bridge->driver->agp_alloc_pages(bridge, new, page_count)) { 25737acee10SShaohua Li agp_free_memory(new); 25837acee10SShaohua Li return NULL; 25937acee10SShaohua Li } 26037acee10SShaohua Li new->bridge = bridge; 26137acee10SShaohua Li return new; 26237acee10SShaohua Li } 26337acee10SShaohua Li 2641da177e4SLinus Torvalds for (i = 0; i < page_count; i++) { 26507613ba2SDave Airlie struct page *page = bridge->driver->agp_alloc_page(bridge); 2661da177e4SLinus Torvalds 26707613ba2SDave Airlie if (page == NULL) { 2681da177e4SLinus Torvalds agp_free_memory(new); 2691da177e4SLinus Torvalds return NULL; 2701da177e4SLinus Torvalds } 27107613ba2SDave Airlie new->pages[i] = page; 2721da177e4SLinus Torvalds new->page_count++; 2731da177e4SLinus Torvalds } 2741da177e4SLinus Torvalds new->bridge = bridge; 2751da177e4SLinus Torvalds 2761da177e4SLinus Torvalds return new; 2771da177e4SLinus Torvalds } 2781da177e4SLinus Torvalds EXPORT_SYMBOL(agp_allocate_memory); 2791da177e4SLinus Torvalds 2801da177e4SLinus Torvalds 2811da177e4SLinus Torvalds /* End - Generic routines for handling agp_memory structures */ 2821da177e4SLinus Torvalds 2831da177e4SLinus Torvalds 2841da177e4SLinus Torvalds static int agp_return_size(void) 2851da177e4SLinus Torvalds { 2861da177e4SLinus Torvalds int current_size; 2871da177e4SLinus Torvalds void *temp; 2881da177e4SLinus Torvalds 2891da177e4SLinus Torvalds temp = agp_bridge->current_size; 2901da177e4SLinus Torvalds 2911da177e4SLinus Torvalds switch (agp_bridge->driver->size_type) { 2921da177e4SLinus Torvalds case U8_APER_SIZE: 2931da177e4SLinus Torvalds current_size = A_SIZE_8(temp)->size; 2941da177e4SLinus Torvalds break; 2951da177e4SLinus Torvalds case U16_APER_SIZE: 2961da177e4SLinus Torvalds current_size = A_SIZE_16(temp)->size; 2971da177e4SLinus Torvalds break; 2981da177e4SLinus Torvalds case U32_APER_SIZE: 2991da177e4SLinus Torvalds current_size = A_SIZE_32(temp)->size; 3001da177e4SLinus Torvalds break; 3011da177e4SLinus Torvalds case LVL2_APER_SIZE: 3021da177e4SLinus Torvalds current_size = A_SIZE_LVL2(temp)->size; 3031da177e4SLinus Torvalds break; 3041da177e4SLinus Torvalds case FIXED_APER_SIZE: 3051da177e4SLinus Torvalds current_size = A_SIZE_FIX(temp)->size; 3061da177e4SLinus Torvalds break; 3071da177e4SLinus Torvalds default: 3081da177e4SLinus Torvalds current_size = 0; 3091da177e4SLinus Torvalds break; 3101da177e4SLinus Torvalds } 3111da177e4SLinus Torvalds 3121da177e4SLinus Torvalds current_size -= (agp_memory_reserved / (1024*1024)); 3131da177e4SLinus Torvalds if (current_size <0) 3141da177e4SLinus Torvalds current_size = 0; 3151da177e4SLinus Torvalds return current_size; 3161da177e4SLinus Torvalds } 3171da177e4SLinus Torvalds 3181da177e4SLinus Torvalds 3191da177e4SLinus Torvalds int agp_num_entries(void) 3201da177e4SLinus Torvalds { 3211da177e4SLinus Torvalds int num_entries; 3221da177e4SLinus Torvalds void *temp; 3231da177e4SLinus Torvalds 3241da177e4SLinus Torvalds temp = agp_bridge->current_size; 3251da177e4SLinus Torvalds 3261da177e4SLinus Torvalds switch (agp_bridge->driver->size_type) { 3271da177e4SLinus Torvalds case U8_APER_SIZE: 3281da177e4SLinus Torvalds num_entries = A_SIZE_8(temp)->num_entries; 3291da177e4SLinus Torvalds break; 3301da177e4SLinus Torvalds case U16_APER_SIZE: 3311da177e4SLinus Torvalds num_entries = A_SIZE_16(temp)->num_entries; 3321da177e4SLinus Torvalds break; 3331da177e4SLinus Torvalds case U32_APER_SIZE: 3341da177e4SLinus Torvalds num_entries = A_SIZE_32(temp)->num_entries; 3351da177e4SLinus Torvalds break; 3361da177e4SLinus Torvalds case LVL2_APER_SIZE: 3371da177e4SLinus Torvalds num_entries = A_SIZE_LVL2(temp)->num_entries; 3381da177e4SLinus Torvalds break; 3391da177e4SLinus Torvalds case FIXED_APER_SIZE: 3401da177e4SLinus Torvalds num_entries = A_SIZE_FIX(temp)->num_entries; 3411da177e4SLinus Torvalds break; 3421da177e4SLinus Torvalds default: 3431da177e4SLinus Torvalds num_entries = 0; 3441da177e4SLinus Torvalds break; 3451da177e4SLinus Torvalds } 3461da177e4SLinus Torvalds 3471da177e4SLinus Torvalds num_entries -= agp_memory_reserved>>PAGE_SHIFT; 3481da177e4SLinus Torvalds if (num_entries<0) 3491da177e4SLinus Torvalds num_entries = 0; 3501da177e4SLinus Torvalds return num_entries; 3511da177e4SLinus Torvalds } 3521da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(agp_num_entries); 3531da177e4SLinus Torvalds 3541da177e4SLinus Torvalds 3551da177e4SLinus Torvalds /** 3561da177e4SLinus Torvalds * agp_copy_info - copy bridge state information 3571da177e4SLinus Torvalds * 3581da177e4SLinus Torvalds * @info: agp_kern_info pointer. The caller should insure that this pointer is valid. 3591da177e4SLinus Torvalds * 3601da177e4SLinus Torvalds * This function copies information about the agp bridge device and the state of 3611da177e4SLinus Torvalds * the agp backend into an agp_kern_info pointer. 3621da177e4SLinus Torvalds */ 3631da177e4SLinus Torvalds int agp_copy_info(struct agp_bridge_data *bridge, struct agp_kern_info *info) 3641da177e4SLinus Torvalds { 3651da177e4SLinus Torvalds memset(info, 0, sizeof(struct agp_kern_info)); 3661da177e4SLinus Torvalds if (!bridge) { 3671da177e4SLinus Torvalds info->chipset = NOT_SUPPORTED; 3681da177e4SLinus Torvalds return -EIO; 3691da177e4SLinus Torvalds } 3701da177e4SLinus Torvalds 3711da177e4SLinus Torvalds info->version.major = bridge->version->major; 3721da177e4SLinus Torvalds info->version.minor = bridge->version->minor; 3731da177e4SLinus Torvalds info->chipset = SUPPORTED; 3741da177e4SLinus Torvalds info->device = bridge->dev; 37566bb8bf8SDavid Mosberger if (bridge->mode & AGPSTAT_MODE_3_0) 3761da177e4SLinus Torvalds info->mode = bridge->mode & ~AGP3_RESERVED_MASK; 3771da177e4SLinus Torvalds else 3781da177e4SLinus Torvalds info->mode = bridge->mode & ~AGP2_RESERVED_MASK; 3791da177e4SLinus Torvalds info->aper_base = bridge->gart_bus_addr; 3801da177e4SLinus Torvalds info->aper_size = agp_return_size(); 3811da177e4SLinus Torvalds info->max_memory = bridge->max_memory_agp; 3821da177e4SLinus Torvalds info->current_memory = atomic_read(&bridge->current_memory_agp); 3831da177e4SLinus Torvalds info->cant_use_aperture = bridge->driver->cant_use_aperture; 3841da177e4SLinus Torvalds info->vm_ops = bridge->vm_ops; 3851da177e4SLinus Torvalds info->page_mask = ~0UL; 3861da177e4SLinus Torvalds return 0; 3871da177e4SLinus Torvalds } 3881da177e4SLinus Torvalds EXPORT_SYMBOL(agp_copy_info); 3891da177e4SLinus Torvalds 3901da177e4SLinus Torvalds /* End - Routine to copy over information structure */ 3911da177e4SLinus Torvalds 3921da177e4SLinus Torvalds /* 3931da177e4SLinus Torvalds * Routines for handling swapping of agp_memory into the GATT - 3941da177e4SLinus Torvalds * These routines take agp_memory and insert them into the GATT. 3951da177e4SLinus Torvalds * They call device specific routines to actually write to the GATT. 3961da177e4SLinus Torvalds */ 3971da177e4SLinus Torvalds 3981da177e4SLinus Torvalds /** 3991da177e4SLinus Torvalds * agp_bind_memory - Bind an agp_memory structure into the GATT. 4001da177e4SLinus Torvalds * 4011da177e4SLinus Torvalds * @curr: agp_memory pointer 4021da177e4SLinus Torvalds * @pg_start: an offset into the graphics aperture translation table 4031da177e4SLinus Torvalds * 4041da177e4SLinus Torvalds * It returns -EINVAL if the pointer == NULL. 4051da177e4SLinus Torvalds * It returns -EBUSY if the area of the table requested is already in use. 4061da177e4SLinus Torvalds */ 4071da177e4SLinus Torvalds int agp_bind_memory(struct agp_memory *curr, off_t pg_start) 4081da177e4SLinus Torvalds { 4091da177e4SLinus Torvalds int ret_val; 4101da177e4SLinus Torvalds 4111da177e4SLinus Torvalds if (curr == NULL) 4121da177e4SLinus Torvalds return -EINVAL; 4131da177e4SLinus Torvalds 414c7258012SJoe Perches if (curr->is_bound) { 4151da177e4SLinus Torvalds printk(KERN_INFO PFX "memory %p is already bound!\n", curr); 4161da177e4SLinus Torvalds return -EINVAL; 4171da177e4SLinus Torvalds } 418c7258012SJoe Perches if (!curr->is_flushed) { 4191da177e4SLinus Torvalds curr->bridge->driver->cache_flush(); 420c7258012SJoe Perches curr->is_flushed = true; 4211da177e4SLinus Torvalds } 422ff663cf8SZhenyu Wang 4231da177e4SLinus Torvalds ret_val = curr->bridge->driver->insert_memory(curr, pg_start, curr->type); 4241da177e4SLinus Torvalds 4251da177e4SLinus Torvalds if (ret_val != 0) 4261da177e4SLinus Torvalds return ret_val; 4271da177e4SLinus Torvalds 428c7258012SJoe Perches curr->is_bound = true; 4291da177e4SLinus Torvalds curr->pg_start = pg_start; 430a8c84df9SKeith Packard spin_lock(&agp_bridge->mapped_lock); 431a8c84df9SKeith Packard list_add(&curr->mapped_list, &agp_bridge->mapped_list); 432a8c84df9SKeith Packard spin_unlock(&agp_bridge->mapped_lock); 433a8c84df9SKeith Packard 4341da177e4SLinus Torvalds return 0; 4351da177e4SLinus Torvalds } 4361da177e4SLinus Torvalds EXPORT_SYMBOL(agp_bind_memory); 4371da177e4SLinus Torvalds 4381da177e4SLinus Torvalds 4391da177e4SLinus Torvalds /** 4401da177e4SLinus Torvalds * agp_unbind_memory - Removes an agp_memory structure from the GATT 4411da177e4SLinus Torvalds * 4421da177e4SLinus Torvalds * @curr: agp_memory pointer to be removed from the GATT. 4431da177e4SLinus Torvalds * 4441da177e4SLinus Torvalds * It returns -EINVAL if this piece of agp_memory is not currently bound to 4451da177e4SLinus Torvalds * the graphics aperture translation table or if the agp_memory pointer == NULL 4461da177e4SLinus Torvalds */ 4471da177e4SLinus Torvalds int agp_unbind_memory(struct agp_memory *curr) 4481da177e4SLinus Torvalds { 4491da177e4SLinus Torvalds int ret_val; 4501da177e4SLinus Torvalds 4511da177e4SLinus Torvalds if (curr == NULL) 4521da177e4SLinus Torvalds return -EINVAL; 4531da177e4SLinus Torvalds 454c7258012SJoe Perches if (!curr->is_bound) { 4551da177e4SLinus Torvalds printk(KERN_INFO PFX "memory %p was not bound!\n", curr); 4561da177e4SLinus Torvalds return -EINVAL; 4571da177e4SLinus Torvalds } 4581da177e4SLinus Torvalds 4591da177e4SLinus Torvalds ret_val = curr->bridge->driver->remove_memory(curr, curr->pg_start, curr->type); 4601da177e4SLinus Torvalds 4611da177e4SLinus Torvalds if (ret_val != 0) 4621da177e4SLinus Torvalds return ret_val; 4631da177e4SLinus Torvalds 464c7258012SJoe Perches curr->is_bound = false; 4651da177e4SLinus Torvalds curr->pg_start = 0; 466a8c84df9SKeith Packard spin_lock(&curr->bridge->mapped_lock); 467a8c84df9SKeith Packard list_del(&curr->mapped_list); 468a8c84df9SKeith Packard spin_unlock(&curr->bridge->mapped_lock); 4691da177e4SLinus Torvalds return 0; 4701da177e4SLinus Torvalds } 4711da177e4SLinus Torvalds EXPORT_SYMBOL(agp_unbind_memory); 4721da177e4SLinus Torvalds 473a8c84df9SKeith Packard 4741da177e4SLinus Torvalds /* End - Routines for handling swapping of agp_memory into the GATT */ 4751da177e4SLinus Torvalds 4761da177e4SLinus Torvalds 4771da177e4SLinus Torvalds /* Generic Agp routines - Start */ 4781da177e4SLinus Torvalds static void agp_v2_parse_one(u32 *requested_mode, u32 *bridge_agpstat, u32 *vga_agpstat) 4791da177e4SLinus Torvalds { 4801da177e4SLinus Torvalds u32 tmp; 4811da177e4SLinus Torvalds 4821da177e4SLinus Torvalds if (*requested_mode & AGP2_RESERVED_MASK) { 483c4dd4582SDave Jones printk(KERN_INFO PFX "reserved bits set (%x) in mode 0x%x. Fixed.\n", 484c4dd4582SDave Jones *requested_mode & AGP2_RESERVED_MASK, *requested_mode); 4851da177e4SLinus Torvalds *requested_mode &= ~AGP2_RESERVED_MASK; 4861da177e4SLinus Torvalds } 4871da177e4SLinus Torvalds 48828af24bbSDave Jones /* 48928af24bbSDave Jones * Some dumb bridges are programmed to disobey the AGP2 spec. 49028af24bbSDave Jones * This is likely a BIOS misprogramming rather than poweron default, or 49128af24bbSDave Jones * it would be a lot more common. 49228af24bbSDave Jones * https://bugs.freedesktop.org/show_bug.cgi?id=8816 49328af24bbSDave Jones * AGPv2 spec 6.1.9 states: 49428af24bbSDave Jones * The RATE field indicates the data transfer rates supported by this 49528af24bbSDave Jones * device. A.G.P. devices must report all that apply. 49628af24bbSDave Jones * Fix them up as best we can. 49728af24bbSDave Jones */ 49828af24bbSDave Jones switch (*bridge_agpstat & 7) { 49928af24bbSDave Jones case 4: 50028af24bbSDave Jones *bridge_agpstat |= (AGPSTAT2_2X | AGPSTAT2_1X); 501e11d0b87STormod Volden printk(KERN_INFO PFX "BIOS bug. AGP bridge claims to only support x4 rate. " 50228af24bbSDave Jones "Fixing up support for x2 & x1\n"); 50328af24bbSDave Jones break; 50428af24bbSDave Jones case 2: 50528af24bbSDave Jones *bridge_agpstat |= AGPSTAT2_1X; 506e11d0b87STormod Volden printk(KERN_INFO PFX "BIOS bug. AGP bridge claims to only support x2 rate. " 50728af24bbSDave Jones "Fixing up support for x1\n"); 50828af24bbSDave Jones break; 50928af24bbSDave Jones default: 51028af24bbSDave Jones break; 51128af24bbSDave Jones } 51228af24bbSDave Jones 5131da177e4SLinus Torvalds /* Check the speed bits make sense. Only one should be set. */ 5141da177e4SLinus Torvalds tmp = *requested_mode & 7; 5151da177e4SLinus Torvalds switch (tmp) { 5161da177e4SLinus Torvalds case 0: 5171da177e4SLinus Torvalds printk(KERN_INFO PFX "%s tried to set rate=x0. Setting to x1 mode.\n", current->comm); 5181da177e4SLinus Torvalds *requested_mode |= AGPSTAT2_1X; 5191da177e4SLinus Torvalds break; 5201da177e4SLinus Torvalds case 1: 5211da177e4SLinus Torvalds case 2: 5221da177e4SLinus Torvalds break; 5231da177e4SLinus Torvalds case 3: 5241da177e4SLinus Torvalds *requested_mode &= ~(AGPSTAT2_1X); /* rate=2 */ 5251da177e4SLinus Torvalds break; 5261da177e4SLinus Torvalds case 4: 5271da177e4SLinus Torvalds break; 5281da177e4SLinus Torvalds case 5: 5291da177e4SLinus Torvalds case 6: 5301da177e4SLinus Torvalds case 7: 5311da177e4SLinus Torvalds *requested_mode &= ~(AGPSTAT2_1X|AGPSTAT2_2X); /* rate=4*/ 5321da177e4SLinus Torvalds break; 5331da177e4SLinus Torvalds } 5341da177e4SLinus Torvalds 5351da177e4SLinus Torvalds /* disable SBA if it's not supported */ 5361da177e4SLinus Torvalds if (!((*bridge_agpstat & AGPSTAT_SBA) && (*vga_agpstat & AGPSTAT_SBA) && (*requested_mode & AGPSTAT_SBA))) 5371da177e4SLinus Torvalds *bridge_agpstat &= ~AGPSTAT_SBA; 5381da177e4SLinus Torvalds 5391da177e4SLinus Torvalds /* Set rate */ 5401da177e4SLinus Torvalds if (!((*bridge_agpstat & AGPSTAT2_4X) && (*vga_agpstat & AGPSTAT2_4X) && (*requested_mode & AGPSTAT2_4X))) 5411da177e4SLinus Torvalds *bridge_agpstat &= ~AGPSTAT2_4X; 5421da177e4SLinus Torvalds 5431da177e4SLinus Torvalds if (!((*bridge_agpstat & AGPSTAT2_2X) && (*vga_agpstat & AGPSTAT2_2X) && (*requested_mode & AGPSTAT2_2X))) 5441da177e4SLinus Torvalds *bridge_agpstat &= ~AGPSTAT2_2X; 5451da177e4SLinus Torvalds 5461da177e4SLinus Torvalds if (!((*bridge_agpstat & AGPSTAT2_1X) && (*vga_agpstat & AGPSTAT2_1X) && (*requested_mode & AGPSTAT2_1X))) 5471da177e4SLinus Torvalds *bridge_agpstat &= ~AGPSTAT2_1X; 5481da177e4SLinus Torvalds 5491da177e4SLinus Torvalds /* Now we know what mode it should be, clear out the unwanted bits. */ 5501da177e4SLinus Torvalds if (*bridge_agpstat & AGPSTAT2_4X) 5511da177e4SLinus Torvalds *bridge_agpstat &= ~(AGPSTAT2_1X | AGPSTAT2_2X); /* 4X */ 5521da177e4SLinus Torvalds 5531da177e4SLinus Torvalds if (*bridge_agpstat & AGPSTAT2_2X) 5541da177e4SLinus Torvalds *bridge_agpstat &= ~(AGPSTAT2_1X | AGPSTAT2_4X); /* 2X */ 5551da177e4SLinus Torvalds 5561da177e4SLinus Torvalds if (*bridge_agpstat & AGPSTAT2_1X) 5571da177e4SLinus Torvalds *bridge_agpstat &= ~(AGPSTAT2_2X | AGPSTAT2_4X); /* 1X */ 5581da177e4SLinus Torvalds 5591da177e4SLinus Torvalds /* Apply any errata. */ 5601da177e4SLinus Torvalds if (agp_bridge->flags & AGP_ERRATA_FASTWRITES) 5611da177e4SLinus Torvalds *bridge_agpstat &= ~AGPSTAT_FW; 5621da177e4SLinus Torvalds 5631da177e4SLinus Torvalds if (agp_bridge->flags & AGP_ERRATA_SBA) 5641da177e4SLinus Torvalds *bridge_agpstat &= ~AGPSTAT_SBA; 5651da177e4SLinus Torvalds 5661da177e4SLinus Torvalds if (agp_bridge->flags & AGP_ERRATA_1X) { 5671da177e4SLinus Torvalds *bridge_agpstat &= ~(AGPSTAT2_2X | AGPSTAT2_4X); 5681da177e4SLinus Torvalds *bridge_agpstat |= AGPSTAT2_1X; 5691da177e4SLinus Torvalds } 5701da177e4SLinus Torvalds 5711da177e4SLinus Torvalds /* If we've dropped down to 1X, disable fast writes. */ 5721da177e4SLinus Torvalds if (*bridge_agpstat & AGPSTAT2_1X) 5731da177e4SLinus Torvalds *bridge_agpstat &= ~AGPSTAT_FW; 5741da177e4SLinus Torvalds } 5751da177e4SLinus Torvalds 5761da177e4SLinus Torvalds /* 5771da177e4SLinus Torvalds * requested_mode = Mode requested by (typically) X. 5781da177e4SLinus Torvalds * bridge_agpstat = PCI_AGP_STATUS from agp bridge. 5791da177e4SLinus Torvalds * vga_agpstat = PCI_AGP_STATUS from graphic card. 5801da177e4SLinus Torvalds */ 5811da177e4SLinus Torvalds static void agp_v3_parse_one(u32 *requested_mode, u32 *bridge_agpstat, u32 *vga_agpstat) 5821da177e4SLinus Torvalds { 5831da177e4SLinus Torvalds u32 origbridge=*bridge_agpstat, origvga=*vga_agpstat; 5841da177e4SLinus Torvalds u32 tmp; 5851da177e4SLinus Torvalds 5861da177e4SLinus Torvalds if (*requested_mode & AGP3_RESERVED_MASK) { 587c4dd4582SDave Jones printk(KERN_INFO PFX "reserved bits set (%x) in mode 0x%x. Fixed.\n", 588c4dd4582SDave Jones *requested_mode & AGP3_RESERVED_MASK, *requested_mode); 5891da177e4SLinus Torvalds *requested_mode &= ~AGP3_RESERVED_MASK; 5901da177e4SLinus Torvalds } 5911da177e4SLinus Torvalds 5921da177e4SLinus Torvalds /* Check the speed bits make sense. */ 5931da177e4SLinus Torvalds tmp = *requested_mode & 7; 5941da177e4SLinus Torvalds if (tmp == 0) { 5951da177e4SLinus Torvalds printk(KERN_INFO PFX "%s tried to set rate=x0. Setting to AGP3 x4 mode.\n", current->comm); 5961da177e4SLinus Torvalds *requested_mode |= AGPSTAT3_4X; 5971da177e4SLinus Torvalds } 5981da177e4SLinus Torvalds if (tmp >= 3) { 5991da177e4SLinus Torvalds printk(KERN_INFO PFX "%s tried to set rate=x%d. Setting to AGP3 x8 mode.\n", current->comm, tmp * 4); 6001da177e4SLinus Torvalds *requested_mode = (*requested_mode & ~7) | AGPSTAT3_8X; 6011da177e4SLinus Torvalds } 6021da177e4SLinus Torvalds 6031da177e4SLinus Torvalds /* ARQSZ - Set the value to the maximum one. 6041da177e4SLinus Torvalds * Don't allow the mode register to override values. */ 6051da177e4SLinus Torvalds *bridge_agpstat = ((*bridge_agpstat & ~AGPSTAT_ARQSZ) | 6061da177e4SLinus Torvalds max_t(u32,(*bridge_agpstat & AGPSTAT_ARQSZ),(*vga_agpstat & AGPSTAT_ARQSZ))); 6071da177e4SLinus Torvalds 6081da177e4SLinus Torvalds /* Calibration cycle. 6091da177e4SLinus Torvalds * Don't allow the mode register to override values. */ 6101da177e4SLinus Torvalds *bridge_agpstat = ((*bridge_agpstat & ~AGPSTAT_CAL_MASK) | 6111da177e4SLinus Torvalds min_t(u32,(*bridge_agpstat & AGPSTAT_CAL_MASK),(*vga_agpstat & AGPSTAT_CAL_MASK))); 6121da177e4SLinus Torvalds 6131da177e4SLinus Torvalds /* SBA *must* be supported for AGP v3 */ 6141da177e4SLinus Torvalds *bridge_agpstat |= AGPSTAT_SBA; 6151da177e4SLinus Torvalds 6161da177e4SLinus Torvalds /* 6171da177e4SLinus Torvalds * Set speed. 6181da177e4SLinus Torvalds * Check for invalid speeds. This can happen when applications 6191da177e4SLinus Torvalds * written before the AGP 3.0 standard pass AGP2.x modes to AGP3 hardware 6201da177e4SLinus Torvalds */ 6211da177e4SLinus Torvalds if (*requested_mode & AGPSTAT_MODE_3_0) { 6221da177e4SLinus Torvalds /* 6231da177e4SLinus Torvalds * Caller hasn't a clue what it is doing. Bridge is in 3.0 mode, 6241da177e4SLinus Torvalds * have been passed a 3.0 mode, but with 2.x speed bits set. 6251da177e4SLinus Torvalds * AGP2.x 4x -> AGP3.0 4x. 6261da177e4SLinus Torvalds */ 6271da177e4SLinus Torvalds if (*requested_mode & AGPSTAT2_4X) { 6281da177e4SLinus Torvalds printk(KERN_INFO PFX "%s passes broken AGP3 flags (%x). Fixed.\n", 6291da177e4SLinus Torvalds current->comm, *requested_mode); 6301da177e4SLinus Torvalds *requested_mode &= ~AGPSTAT2_4X; 6311da177e4SLinus Torvalds *requested_mode |= AGPSTAT3_4X; 6321da177e4SLinus Torvalds } 6331da177e4SLinus Torvalds } else { 6341da177e4SLinus Torvalds /* 6351da177e4SLinus Torvalds * The caller doesn't know what they are doing. We are in 3.0 mode, 6361da177e4SLinus Torvalds * but have been passed an AGP 2.x mode. 6371da177e4SLinus Torvalds * Convert AGP 1x,2x,4x -> AGP 3.0 4x. 6381da177e4SLinus Torvalds */ 6391da177e4SLinus Torvalds printk(KERN_INFO PFX "%s passes broken AGP2 flags (%x) in AGP3 mode. Fixed.\n", 6401da177e4SLinus Torvalds current->comm, *requested_mode); 6411da177e4SLinus Torvalds *requested_mode &= ~(AGPSTAT2_4X | AGPSTAT2_2X | AGPSTAT2_1X); 6421da177e4SLinus Torvalds *requested_mode |= AGPSTAT3_4X; 6431da177e4SLinus Torvalds } 6441da177e4SLinus Torvalds 6451da177e4SLinus Torvalds if (*requested_mode & AGPSTAT3_8X) { 6461da177e4SLinus Torvalds if (!(*bridge_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 bridge not capable.\n", current->comm); 6501da177e4SLinus Torvalds return; 6511da177e4SLinus Torvalds } 6521da177e4SLinus Torvalds if (!(*vga_agpstat & AGPSTAT3_8X)) { 6531da177e4SLinus Torvalds *bridge_agpstat &= ~(AGPSTAT3_8X | AGPSTAT3_RSVD); 6541da177e4SLinus Torvalds *bridge_agpstat |= AGPSTAT3_4X; 6558c8b8385SDave Jones printk(KERN_INFO PFX "%s requested AGPx8 but graphic card not capable.\n", current->comm); 6561da177e4SLinus Torvalds return; 6571da177e4SLinus Torvalds } 6581da177e4SLinus Torvalds /* All set, bridge & device can do AGP x8*/ 6591da177e4SLinus Torvalds *bridge_agpstat &= ~(AGPSTAT3_4X | AGPSTAT3_RSVD); 6601da177e4SLinus Torvalds goto done; 6611da177e4SLinus Torvalds 662edf03fb0SDave Jones } else if (*requested_mode & AGPSTAT3_4X) { 663edf03fb0SDave Jones *bridge_agpstat &= ~(AGPSTAT3_8X | AGPSTAT3_RSVD); 664edf03fb0SDave Jones *bridge_agpstat |= AGPSTAT3_4X; 665edf03fb0SDave Jones goto done; 666edf03fb0SDave Jones 6671da177e4SLinus Torvalds } else { 6681da177e4SLinus Torvalds 6691da177e4SLinus Torvalds /* 670edf03fb0SDave Jones * If we didn't specify an AGP mode, we see if both 671edf03fb0SDave Jones * the graphics card, and the bridge can do x8, and use if so. 672edf03fb0SDave Jones * If not, we fall back to x4 mode. 6731da177e4SLinus Torvalds */ 674edf03fb0SDave Jones if ((*bridge_agpstat & AGPSTAT3_8X) && (*vga_agpstat & AGPSTAT3_8X)) { 6752cc1a413SDave Jones printk(KERN_INFO PFX "No AGP mode specified. Setting to highest mode " 6762cc1a413SDave Jones "supported by bridge & card (x8).\n"); 677edf03fb0SDave Jones *bridge_agpstat &= ~(AGPSTAT3_4X | AGPSTAT3_RSVD); 678edf03fb0SDave Jones *vga_agpstat &= ~(AGPSTAT3_4X | AGPSTAT3_RSVD); 679edf03fb0SDave Jones } else { 680edf03fb0SDave Jones printk(KERN_INFO PFX "Fell back to AGPx4 mode because "); 681edf03fb0SDave Jones if (!(*bridge_agpstat & AGPSTAT3_8X)) { 6822cc1a413SDave Jones printk(KERN_INFO PFX "bridge couldn't do x8. bridge_agpstat:%x (orig=%x)\n", 6832cc1a413SDave Jones *bridge_agpstat, origbridge); 6841da177e4SLinus Torvalds *bridge_agpstat &= ~(AGPSTAT3_8X | AGPSTAT3_RSVD); 6851da177e4SLinus Torvalds *bridge_agpstat |= AGPSTAT3_4X; 686edf03fb0SDave Jones } 687edf03fb0SDave Jones if (!(*vga_agpstat & AGPSTAT3_8X)) { 6882cc1a413SDave Jones printk(KERN_INFO PFX "graphics card couldn't do x8. vga_agpstat:%x (orig=%x)\n", 6892cc1a413SDave Jones *vga_agpstat, origvga); 690edf03fb0SDave Jones *vga_agpstat &= ~(AGPSTAT3_8X | AGPSTAT3_RSVD); 691edf03fb0SDave Jones *vga_agpstat |= AGPSTAT3_4X; 692edf03fb0SDave Jones } 6931da177e4SLinus Torvalds } 6941da177e4SLinus Torvalds } 6951da177e4SLinus Torvalds 6961da177e4SLinus Torvalds done: 6971da177e4SLinus Torvalds /* Apply any errata. */ 6981da177e4SLinus Torvalds if (agp_bridge->flags & AGP_ERRATA_FASTWRITES) 6991da177e4SLinus Torvalds *bridge_agpstat &= ~AGPSTAT_FW; 7001da177e4SLinus Torvalds 7011da177e4SLinus Torvalds if (agp_bridge->flags & AGP_ERRATA_SBA) 7021da177e4SLinus Torvalds *bridge_agpstat &= ~AGPSTAT_SBA; 7031da177e4SLinus Torvalds 7041da177e4SLinus Torvalds if (agp_bridge->flags & AGP_ERRATA_1X) { 7051da177e4SLinus Torvalds *bridge_agpstat &= ~(AGPSTAT2_2X | AGPSTAT2_4X); 7061da177e4SLinus Torvalds *bridge_agpstat |= AGPSTAT2_1X; 7071da177e4SLinus Torvalds } 7081da177e4SLinus Torvalds } 7091da177e4SLinus Torvalds 7101da177e4SLinus Torvalds 7111da177e4SLinus Torvalds /** 7121da177e4SLinus Torvalds * agp_collect_device_status - determine correct agp_cmd from various agp_stat's 7131da177e4SLinus Torvalds * @bridge: an agp_bridge_data struct allocated for the AGP host bridge. 7141da177e4SLinus Torvalds * @requested_mode: requested agp_stat from userspace (Typically from X) 7151da177e4SLinus Torvalds * @bridge_agpstat: current agp_stat from AGP bridge. 7161da177e4SLinus Torvalds * 7171da177e4SLinus Torvalds * This function will hunt for an AGP graphics card, and try to match 7181da177e4SLinus Torvalds * the requested mode to the capabilities of both the bridge and the card. 7191da177e4SLinus Torvalds */ 7201da177e4SLinus Torvalds u32 agp_collect_device_status(struct agp_bridge_data *bridge, u32 requested_mode, u32 bridge_agpstat) 7211da177e4SLinus Torvalds { 7221da177e4SLinus Torvalds struct pci_dev *device = NULL; 7231da177e4SLinus Torvalds u32 vga_agpstat; 7241da177e4SLinus Torvalds u8 cap_ptr; 7251da177e4SLinus Torvalds 7261da177e4SLinus Torvalds for (;;) { 7271da177e4SLinus Torvalds device = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, device); 7281da177e4SLinus Torvalds if (!device) { 7291da177e4SLinus Torvalds printk(KERN_INFO PFX "Couldn't find an AGP VGA controller.\n"); 7301da177e4SLinus Torvalds return 0; 7311da177e4SLinus Torvalds } 7321da177e4SLinus Torvalds cap_ptr = pci_find_capability(device, PCI_CAP_ID_AGP); 7331da177e4SLinus Torvalds if (cap_ptr) 7341da177e4SLinus Torvalds break; 7351da177e4SLinus Torvalds } 7361da177e4SLinus Torvalds 7371da177e4SLinus Torvalds /* 7381da177e4SLinus Torvalds * Ok, here we have a AGP device. Disable impossible 7391da177e4SLinus Torvalds * settings, and adjust the readqueue to the minimum. 7401da177e4SLinus Torvalds */ 7411da177e4SLinus Torvalds pci_read_config_dword(device, cap_ptr+PCI_AGP_STATUS, &vga_agpstat); 7421da177e4SLinus Torvalds 7431da177e4SLinus Torvalds /* adjust RQ depth */ 7441da177e4SLinus Torvalds bridge_agpstat = ((bridge_agpstat & ~AGPSTAT_RQ_DEPTH) | 7451da177e4SLinus Torvalds min_t(u32, (requested_mode & AGPSTAT_RQ_DEPTH), 7461da177e4SLinus Torvalds min_t(u32, (bridge_agpstat & AGPSTAT_RQ_DEPTH), (vga_agpstat & AGPSTAT_RQ_DEPTH)))); 7471da177e4SLinus Torvalds 7481da177e4SLinus Torvalds /* disable FW if it's not supported */ 7491da177e4SLinus Torvalds if (!((bridge_agpstat & AGPSTAT_FW) && 7501da177e4SLinus Torvalds (vga_agpstat & AGPSTAT_FW) && 7511da177e4SLinus Torvalds (requested_mode & AGPSTAT_FW))) 7521da177e4SLinus Torvalds bridge_agpstat &= ~AGPSTAT_FW; 7531da177e4SLinus Torvalds 7541da177e4SLinus Torvalds /* Check to see if we are operating in 3.0 mode */ 75566bb8bf8SDavid Mosberger if (agp_bridge->mode & AGPSTAT_MODE_3_0) 7561da177e4SLinus Torvalds agp_v3_parse_one(&requested_mode, &bridge_agpstat, &vga_agpstat); 7571da177e4SLinus Torvalds else 7581da177e4SLinus Torvalds agp_v2_parse_one(&requested_mode, &bridge_agpstat, &vga_agpstat); 7591da177e4SLinus Torvalds 7601da177e4SLinus Torvalds pci_dev_put(device); 7611da177e4SLinus Torvalds return bridge_agpstat; 7621da177e4SLinus Torvalds } 7631da177e4SLinus Torvalds EXPORT_SYMBOL(agp_collect_device_status); 7641da177e4SLinus Torvalds 7651da177e4SLinus Torvalds 766c7258012SJoe Perches void agp_device_command(u32 bridge_agpstat, bool agp_v3) 7671da177e4SLinus Torvalds { 7681da177e4SLinus Torvalds struct pci_dev *device = NULL; 7691da177e4SLinus Torvalds int mode; 7701da177e4SLinus Torvalds 7711da177e4SLinus Torvalds mode = bridge_agpstat & 0x7; 7721da177e4SLinus Torvalds if (agp_v3) 7731da177e4SLinus Torvalds mode *= 4; 7741da177e4SLinus Torvalds 7751da177e4SLinus Torvalds for_each_pci_dev(device) { 7761da177e4SLinus Torvalds u8 agp = pci_find_capability(device, PCI_CAP_ID_AGP); 7771da177e4SLinus Torvalds if (!agp) 7781da177e4SLinus Torvalds continue; 7791da177e4SLinus Torvalds 780e3cf6951SBjorn Helgaas dev_info(&device->dev, "putting AGP V%d device into %dx mode\n", 781e3cf6951SBjorn Helgaas agp_v3 ? 3 : 2, mode); 7821da177e4SLinus Torvalds pci_write_config_dword(device, agp + PCI_AGP_COMMAND, bridge_agpstat); 7831da177e4SLinus Torvalds } 7841da177e4SLinus Torvalds } 7851da177e4SLinus Torvalds EXPORT_SYMBOL(agp_device_command); 7861da177e4SLinus Torvalds 7871da177e4SLinus Torvalds 7881da177e4SLinus Torvalds void get_agp_version(struct agp_bridge_data *bridge) 7891da177e4SLinus Torvalds { 7901da177e4SLinus Torvalds u32 ncapid; 7911da177e4SLinus Torvalds 7921da177e4SLinus Torvalds /* Exit early if already set by errata workarounds. */ 7931da177e4SLinus Torvalds if (bridge->major_version != 0) 7941da177e4SLinus Torvalds return; 7951da177e4SLinus Torvalds 7961da177e4SLinus Torvalds pci_read_config_dword(bridge->dev, bridge->capndx, &ncapid); 7971da177e4SLinus Torvalds bridge->major_version = (ncapid >> AGP_MAJOR_VERSION_SHIFT) & 0xf; 7981da177e4SLinus Torvalds bridge->minor_version = (ncapid >> AGP_MINOR_VERSION_SHIFT) & 0xf; 7991da177e4SLinus Torvalds } 8001da177e4SLinus Torvalds EXPORT_SYMBOL(get_agp_version); 8011da177e4SLinus Torvalds 8021da177e4SLinus Torvalds 8031da177e4SLinus Torvalds void agp_generic_enable(struct agp_bridge_data *bridge, u32 requested_mode) 8041da177e4SLinus Torvalds { 8051da177e4SLinus Torvalds u32 bridge_agpstat, temp; 8061da177e4SLinus Torvalds 8071da177e4SLinus Torvalds get_agp_version(agp_bridge); 8081da177e4SLinus Torvalds 809e3cf6951SBjorn Helgaas dev_info(&agp_bridge->dev->dev, "AGP %d.%d bridge\n", 810e3cf6951SBjorn Helgaas agp_bridge->major_version, agp_bridge->minor_version); 8111da177e4SLinus Torvalds 8121da177e4SLinus Torvalds pci_read_config_dword(agp_bridge->dev, 8131da177e4SLinus Torvalds agp_bridge->capndx + PCI_AGP_STATUS, &bridge_agpstat); 8141da177e4SLinus Torvalds 8151da177e4SLinus Torvalds bridge_agpstat = agp_collect_device_status(agp_bridge, requested_mode, bridge_agpstat); 8161da177e4SLinus Torvalds if (bridge_agpstat == 0) 8171da177e4SLinus Torvalds /* Something bad happened. FIXME: Return error code? */ 8181da177e4SLinus Torvalds return; 8191da177e4SLinus Torvalds 8201da177e4SLinus Torvalds bridge_agpstat |= AGPSTAT_AGP_ENABLE; 8211da177e4SLinus Torvalds 8221da177e4SLinus Torvalds /* Do AGP version specific frobbing. */ 8231da177e4SLinus Torvalds if (bridge->major_version >= 3) { 82466bb8bf8SDavid Mosberger if (bridge->mode & AGPSTAT_MODE_3_0) { 8251da177e4SLinus Torvalds /* If we have 3.5, we can do the isoch stuff. */ 8261da177e4SLinus Torvalds if (bridge->minor_version >= 5) 8271da177e4SLinus Torvalds agp_3_5_enable(bridge); 828c7258012SJoe Perches agp_device_command(bridge_agpstat, true); 8291da177e4SLinus Torvalds return; 8301da177e4SLinus Torvalds } else { 8311da177e4SLinus Torvalds /* Disable calibration cycle in RX91<1> when not in AGP3.0 mode of operation.*/ 8321da177e4SLinus Torvalds bridge_agpstat &= ~(7<<10) ; 8331da177e4SLinus Torvalds pci_read_config_dword(bridge->dev, 8341da177e4SLinus Torvalds bridge->capndx+AGPCTRL, &temp); 8351da177e4SLinus Torvalds temp |= (1<<9); 8361da177e4SLinus Torvalds pci_write_config_dword(bridge->dev, 8371da177e4SLinus Torvalds bridge->capndx+AGPCTRL, temp); 8381da177e4SLinus Torvalds 839e3cf6951SBjorn Helgaas dev_info(&bridge->dev->dev, "bridge is in legacy mode, falling back to 2.x\n"); 8401da177e4SLinus Torvalds } 8411da177e4SLinus Torvalds } 8421da177e4SLinus Torvalds 8431da177e4SLinus Torvalds /* AGP v<3 */ 844c7258012SJoe Perches agp_device_command(bridge_agpstat, false); 8451da177e4SLinus Torvalds } 8461da177e4SLinus Torvalds EXPORT_SYMBOL(agp_generic_enable); 8471da177e4SLinus Torvalds 8481da177e4SLinus Torvalds 8491da177e4SLinus Torvalds int agp_generic_create_gatt_table(struct agp_bridge_data *bridge) 8501da177e4SLinus Torvalds { 8511da177e4SLinus Torvalds char *table; 8521da177e4SLinus Torvalds char *table_end; 8531da177e4SLinus Torvalds int size; 8541da177e4SLinus Torvalds int page_order; 8551da177e4SLinus Torvalds int num_entries; 8561da177e4SLinus Torvalds int i; 8571da177e4SLinus Torvalds void *temp; 8581da177e4SLinus Torvalds struct page *page; 8591da177e4SLinus Torvalds 8601da177e4SLinus Torvalds /* The generic routines can't handle 2 level gatt's */ 8611da177e4SLinus Torvalds if (bridge->driver->size_type == LVL2_APER_SIZE) 8621da177e4SLinus Torvalds return -EINVAL; 8631da177e4SLinus Torvalds 8641da177e4SLinus Torvalds table = NULL; 8651da177e4SLinus Torvalds i = bridge->aperture_size_idx; 8661da177e4SLinus Torvalds temp = bridge->current_size; 8671da177e4SLinus Torvalds size = page_order = num_entries = 0; 8681da177e4SLinus Torvalds 8691da177e4SLinus Torvalds if (bridge->driver->size_type != FIXED_APER_SIZE) { 8701da177e4SLinus Torvalds do { 8711da177e4SLinus Torvalds switch (bridge->driver->size_type) { 8721da177e4SLinus Torvalds case U8_APER_SIZE: 8731da177e4SLinus Torvalds size = A_SIZE_8(temp)->size; 8741da177e4SLinus Torvalds page_order = 8751da177e4SLinus Torvalds A_SIZE_8(temp)->page_order; 8761da177e4SLinus Torvalds num_entries = 8771da177e4SLinus Torvalds A_SIZE_8(temp)->num_entries; 8781da177e4SLinus Torvalds break; 8791da177e4SLinus Torvalds case U16_APER_SIZE: 8801da177e4SLinus Torvalds size = A_SIZE_16(temp)->size; 8811da177e4SLinus Torvalds page_order = A_SIZE_16(temp)->page_order; 8821da177e4SLinus Torvalds num_entries = A_SIZE_16(temp)->num_entries; 8831da177e4SLinus Torvalds break; 8841da177e4SLinus Torvalds case U32_APER_SIZE: 8851da177e4SLinus Torvalds size = A_SIZE_32(temp)->size; 8861da177e4SLinus Torvalds page_order = A_SIZE_32(temp)->page_order; 8871da177e4SLinus Torvalds num_entries = A_SIZE_32(temp)->num_entries; 8881da177e4SLinus Torvalds break; 8891da177e4SLinus Torvalds /* This case will never really happen. */ 8901da177e4SLinus Torvalds case FIXED_APER_SIZE: 8911da177e4SLinus Torvalds case LVL2_APER_SIZE: 8921da177e4SLinus Torvalds default: 8931da177e4SLinus Torvalds size = page_order = num_entries = 0; 8941da177e4SLinus Torvalds break; 8951da177e4SLinus Torvalds } 8961da177e4SLinus Torvalds 89707eee78eSKeir Fraser table = alloc_gatt_pages(page_order); 8981da177e4SLinus Torvalds 8991da177e4SLinus Torvalds if (table == NULL) { 9001da177e4SLinus Torvalds i++; 9011da177e4SLinus Torvalds switch (bridge->driver->size_type) { 9021da177e4SLinus Torvalds case U8_APER_SIZE: 9031da177e4SLinus Torvalds bridge->current_size = A_IDX8(bridge); 9041da177e4SLinus Torvalds break; 9051da177e4SLinus Torvalds case U16_APER_SIZE: 9061da177e4SLinus Torvalds bridge->current_size = A_IDX16(bridge); 9071da177e4SLinus Torvalds break; 9081da177e4SLinus Torvalds case U32_APER_SIZE: 9091da177e4SLinus Torvalds bridge->current_size = A_IDX32(bridge); 9101da177e4SLinus Torvalds break; 91189197e34SDave Jones /* These cases will never really happen. */ 9121da177e4SLinus Torvalds case FIXED_APER_SIZE: 9131da177e4SLinus Torvalds case LVL2_APER_SIZE: 9141da177e4SLinus Torvalds default: 9151da177e4SLinus Torvalds break; 9161da177e4SLinus Torvalds } 9171da177e4SLinus Torvalds temp = bridge->current_size; 9181da177e4SLinus Torvalds } else { 9191da177e4SLinus Torvalds bridge->aperture_size_idx = i; 9201da177e4SLinus Torvalds } 9211da177e4SLinus Torvalds } while (!table && (i < bridge->driver->num_aperture_sizes)); 9221da177e4SLinus Torvalds } else { 9231da177e4SLinus Torvalds size = ((struct aper_size_info_fixed *) temp)->size; 9241da177e4SLinus Torvalds page_order = ((struct aper_size_info_fixed *) temp)->page_order; 9251da177e4SLinus Torvalds num_entries = ((struct aper_size_info_fixed *) temp)->num_entries; 92607eee78eSKeir Fraser table = alloc_gatt_pages(page_order); 9271da177e4SLinus Torvalds } 9281da177e4SLinus Torvalds 9291da177e4SLinus Torvalds if (table == NULL) 9301da177e4SLinus Torvalds return -ENOMEM; 9311da177e4SLinus Torvalds 9321da177e4SLinus Torvalds table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1); 9331da177e4SLinus Torvalds 9341da177e4SLinus Torvalds for (page = virt_to_page(table); page <= virt_to_page(table_end); page++) 9351da177e4SLinus Torvalds SetPageReserved(page); 9361da177e4SLinus Torvalds 9371da177e4SLinus Torvalds bridge->gatt_table_real = (u32 *) table; 9381da177e4SLinus Torvalds agp_gatt_table = (void *)table; 9391da177e4SLinus Torvalds 9401da177e4SLinus Torvalds bridge->driver->cache_flush(); 941fcea424dSArjan van dev Ven #ifdef CONFIG_X86 9421b13fe6aSBorislav Petkov if (set_memory_uc((unsigned long)table, 1 << page_order)) 943e11d0b87STormod Volden printk(KERN_WARNING "Could not set GATT table memory to UC!\n"); 9441b13fe6aSBorislav Petkov 945ae85226eSSantosh Nayak bridge->gatt_table = (u32 __iomem *)table; 946fcea424dSArjan van dev Ven #else 9476a12235cSDavid Woodhouse bridge->gatt_table = ioremap_nocache(virt_to_phys(table), 9481da177e4SLinus Torvalds (PAGE_SIZE * (1 << page_order))); 9491da177e4SLinus Torvalds bridge->driver->cache_flush(); 950fcea424dSArjan van dev Ven #endif 9511da177e4SLinus Torvalds 9521da177e4SLinus Torvalds if (bridge->gatt_table == NULL) { 9531da177e4SLinus Torvalds for (page = virt_to_page(table); page <= virt_to_page(table_end); page++) 9541da177e4SLinus Torvalds ClearPageReserved(page); 9551da177e4SLinus Torvalds 95607eee78eSKeir Fraser free_gatt_pages(table, page_order); 9571da177e4SLinus Torvalds 9581da177e4SLinus Torvalds return -ENOMEM; 9591da177e4SLinus Torvalds } 9606a12235cSDavid Woodhouse bridge->gatt_bus_addr = virt_to_phys(bridge->gatt_table_real); 9611da177e4SLinus Torvalds 9621da177e4SLinus Torvalds /* AK: bogus, should encode addresses > 4GB */ 9631da177e4SLinus Torvalds for (i = 0; i < num_entries; i++) { 9641da177e4SLinus Torvalds writel(bridge->scratch_page, bridge->gatt_table+i); 9651da177e4SLinus Torvalds readl(bridge->gatt_table+i); /* PCI Posting. */ 9661da177e4SLinus Torvalds } 9671da177e4SLinus Torvalds 9681da177e4SLinus Torvalds return 0; 9691da177e4SLinus Torvalds } 9701da177e4SLinus Torvalds EXPORT_SYMBOL(agp_generic_create_gatt_table); 9711da177e4SLinus Torvalds 9721da177e4SLinus Torvalds int agp_generic_free_gatt_table(struct agp_bridge_data *bridge) 9731da177e4SLinus Torvalds { 9741da177e4SLinus Torvalds int page_order; 9751da177e4SLinus Torvalds char *table, *table_end; 9761da177e4SLinus Torvalds void *temp; 9771da177e4SLinus Torvalds struct page *page; 9781da177e4SLinus Torvalds 9791da177e4SLinus Torvalds temp = bridge->current_size; 9801da177e4SLinus Torvalds 9811da177e4SLinus Torvalds switch (bridge->driver->size_type) { 9821da177e4SLinus Torvalds case U8_APER_SIZE: 9831da177e4SLinus Torvalds page_order = A_SIZE_8(temp)->page_order; 9841da177e4SLinus Torvalds break; 9851da177e4SLinus Torvalds case U16_APER_SIZE: 9861da177e4SLinus Torvalds page_order = A_SIZE_16(temp)->page_order; 9871da177e4SLinus Torvalds break; 9881da177e4SLinus Torvalds case U32_APER_SIZE: 9891da177e4SLinus Torvalds page_order = A_SIZE_32(temp)->page_order; 9901da177e4SLinus Torvalds break; 9911da177e4SLinus Torvalds case FIXED_APER_SIZE: 9921da177e4SLinus Torvalds page_order = A_SIZE_FIX(temp)->page_order; 9931da177e4SLinus Torvalds break; 9941da177e4SLinus Torvalds case LVL2_APER_SIZE: 9951da177e4SLinus Torvalds /* The generic routines can't deal with 2 level gatt's */ 9961da177e4SLinus Torvalds return -EINVAL; 9971da177e4SLinus Torvalds default: 9981da177e4SLinus Torvalds page_order = 0; 9991da177e4SLinus Torvalds break; 10001da177e4SLinus Torvalds } 10011da177e4SLinus Torvalds 10021da177e4SLinus Torvalds /* Do not worry about freeing memory, because if this is 10031da177e4SLinus Torvalds * called, then all agp memory is deallocated and removed 10041da177e4SLinus Torvalds * from the table. */ 10051da177e4SLinus Torvalds 1006fcea424dSArjan van dev Ven #ifdef CONFIG_X86 1007fcea424dSArjan van dev Ven set_memory_wb((unsigned long)bridge->gatt_table, 1 << page_order); 1008fcea424dSArjan van dev Ven #else 10091da177e4SLinus Torvalds iounmap(bridge->gatt_table); 1010fcea424dSArjan van dev Ven #endif 10111da177e4SLinus Torvalds table = (char *) bridge->gatt_table_real; 10121da177e4SLinus Torvalds table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1); 10131da177e4SLinus Torvalds 10141da177e4SLinus Torvalds for (page = virt_to_page(table); page <= virt_to_page(table_end); page++) 10151da177e4SLinus Torvalds ClearPageReserved(page); 10161da177e4SLinus Torvalds 101707eee78eSKeir Fraser free_gatt_pages(bridge->gatt_table_real, page_order); 10181da177e4SLinus Torvalds 10191da177e4SLinus Torvalds agp_gatt_table = NULL; 10201da177e4SLinus Torvalds bridge->gatt_table = NULL; 10211da177e4SLinus Torvalds bridge->gatt_table_real = NULL; 10221da177e4SLinus Torvalds bridge->gatt_bus_addr = 0; 10231da177e4SLinus Torvalds 10241da177e4SLinus Torvalds return 0; 10251da177e4SLinus Torvalds } 10261da177e4SLinus Torvalds EXPORT_SYMBOL(agp_generic_free_gatt_table); 10271da177e4SLinus Torvalds 10281da177e4SLinus Torvalds 10291da177e4SLinus Torvalds int agp_generic_insert_memory(struct agp_memory * mem, off_t pg_start, int type) 10301da177e4SLinus Torvalds { 10311da177e4SLinus Torvalds int num_entries; 10321da177e4SLinus Torvalds size_t i; 10331da177e4SLinus Torvalds off_t j; 10341da177e4SLinus Torvalds void *temp; 10351da177e4SLinus Torvalds struct agp_bridge_data *bridge; 1036a030ce44SThomas Hellstrom int mask_type; 10371da177e4SLinus Torvalds 10381da177e4SLinus Torvalds bridge = mem->bridge; 10391da177e4SLinus Torvalds if (!bridge) 10401da177e4SLinus Torvalds return -EINVAL; 10411da177e4SLinus Torvalds 10425aa80c72SThomas Hellstrom if (mem->page_count == 0) 10435aa80c72SThomas Hellstrom return 0; 10445aa80c72SThomas Hellstrom 10451da177e4SLinus Torvalds temp = bridge->current_size; 10461da177e4SLinus Torvalds 10471da177e4SLinus Torvalds switch (bridge->driver->size_type) { 10481da177e4SLinus Torvalds case U8_APER_SIZE: 10491da177e4SLinus Torvalds num_entries = A_SIZE_8(temp)->num_entries; 10501da177e4SLinus Torvalds break; 10511da177e4SLinus Torvalds case U16_APER_SIZE: 10521da177e4SLinus Torvalds num_entries = A_SIZE_16(temp)->num_entries; 10531da177e4SLinus Torvalds break; 10541da177e4SLinus Torvalds case U32_APER_SIZE: 10551da177e4SLinus Torvalds num_entries = A_SIZE_32(temp)->num_entries; 10561da177e4SLinus Torvalds break; 10571da177e4SLinus Torvalds case FIXED_APER_SIZE: 10581da177e4SLinus Torvalds num_entries = A_SIZE_FIX(temp)->num_entries; 10591da177e4SLinus Torvalds break; 10601da177e4SLinus Torvalds case LVL2_APER_SIZE: 10611da177e4SLinus Torvalds /* The generic routines can't deal with 2 level gatt's */ 10621da177e4SLinus Torvalds return -EINVAL; 10631da177e4SLinus Torvalds default: 10641da177e4SLinus Torvalds num_entries = 0; 10651da177e4SLinus Torvalds break; 10661da177e4SLinus Torvalds } 10671da177e4SLinus Torvalds 10681da177e4SLinus Torvalds num_entries -= agp_memory_reserved/PAGE_SIZE; 10691da177e4SLinus Torvalds if (num_entries < 0) num_entries = 0; 10701da177e4SLinus Torvalds 10711c14cfbbSAndrew Morton if (type != mem->type) 1072a030ce44SThomas Hellstrom return -EINVAL; 1073a030ce44SThomas Hellstrom 1074a030ce44SThomas Hellstrom mask_type = bridge->driver->agp_type_to_mask_type(bridge, type); 1075a030ce44SThomas Hellstrom if (mask_type != 0) { 10761da177e4SLinus Torvalds /* The generic routines know nothing of memory types */ 10771da177e4SLinus Torvalds return -EINVAL; 10781da177e4SLinus Torvalds } 10791da177e4SLinus Torvalds 1080194b3da8SVasiliy Kulikov if (((pg_start + mem->page_count) > num_entries) || 1081194b3da8SVasiliy Kulikov ((pg_start + mem->page_count) < pg_start)) 10821da177e4SLinus Torvalds return -EINVAL; 10831da177e4SLinus Torvalds 10841da177e4SLinus Torvalds j = pg_start; 10851da177e4SLinus Torvalds 10861da177e4SLinus Torvalds while (j < (pg_start + mem->page_count)) { 10871da177e4SLinus Torvalds if (!PGE_EMPTY(bridge, readl(bridge->gatt_table+j))) 10881da177e4SLinus Torvalds return -EBUSY; 10891da177e4SLinus Torvalds j++; 10901da177e4SLinus Torvalds } 10911da177e4SLinus Torvalds 1092c7258012SJoe Perches if (!mem->is_flushed) { 10931da177e4SLinus Torvalds bridge->driver->cache_flush(); 1094c7258012SJoe Perches mem->is_flushed = true; 10951da177e4SLinus Torvalds } 10961da177e4SLinus Torvalds 10971da177e4SLinus Torvalds for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { 10982a4ceb6dSDavid Woodhouse writel(bridge->driver->mask_memory(bridge, 10996a12235cSDavid Woodhouse page_to_phys(mem->pages[i]), 11002a4ceb6dSDavid Woodhouse mask_type), 1101a030ce44SThomas Hellstrom bridge->gatt_table+j); 11021da177e4SLinus Torvalds } 11035aa80c72SThomas Hellstrom readl(bridge->gatt_table+j-1); /* PCI Posting. */ 11041da177e4SLinus Torvalds 11051da177e4SLinus Torvalds bridge->driver->tlb_flush(mem); 11061da177e4SLinus Torvalds return 0; 11071da177e4SLinus Torvalds } 11081da177e4SLinus Torvalds EXPORT_SYMBOL(agp_generic_insert_memory); 11091da177e4SLinus Torvalds 11101da177e4SLinus Torvalds 11111da177e4SLinus Torvalds int agp_generic_remove_memory(struct agp_memory *mem, off_t pg_start, int type) 11121da177e4SLinus Torvalds { 11131da177e4SLinus Torvalds size_t i; 11141da177e4SLinus Torvalds struct agp_bridge_data *bridge; 1115194b3da8SVasiliy Kulikov int mask_type, num_entries; 11161da177e4SLinus Torvalds 11171da177e4SLinus Torvalds bridge = mem->bridge; 11181da177e4SLinus Torvalds if (!bridge) 11191da177e4SLinus Torvalds return -EINVAL; 11201da177e4SLinus Torvalds 11215aa80c72SThomas Hellstrom if (mem->page_count == 0) 11225aa80c72SThomas Hellstrom return 0; 11235aa80c72SThomas Hellstrom 1124a030ce44SThomas Hellstrom if (type != mem->type) 1125a030ce44SThomas Hellstrom return -EINVAL; 1126a030ce44SThomas Hellstrom 1127194b3da8SVasiliy Kulikov num_entries = agp_num_entries(); 1128194b3da8SVasiliy Kulikov if (((pg_start + mem->page_count) > num_entries) || 1129194b3da8SVasiliy Kulikov ((pg_start + mem->page_count) < pg_start)) 1130194b3da8SVasiliy Kulikov return -EINVAL; 1131194b3da8SVasiliy Kulikov 1132a030ce44SThomas Hellstrom mask_type = bridge->driver->agp_type_to_mask_type(bridge, type); 1133a030ce44SThomas Hellstrom if (mask_type != 0) { 11341da177e4SLinus Torvalds /* The generic routines know nothing of memory types */ 11351da177e4SLinus Torvalds return -EINVAL; 11361da177e4SLinus Torvalds } 11371da177e4SLinus Torvalds 11381da177e4SLinus Torvalds /* AK: bogus, should encode addresses > 4GB */ 11391da177e4SLinus Torvalds for (i = pg_start; i < (mem->page_count + pg_start); i++) { 11401da177e4SLinus Torvalds writel(bridge->scratch_page, bridge->gatt_table+i); 11411da177e4SLinus Torvalds } 11425aa80c72SThomas Hellstrom readl(bridge->gatt_table+i-1); /* PCI Posting. */ 11431da177e4SLinus Torvalds 11441da177e4SLinus Torvalds bridge->driver->tlb_flush(mem); 11451da177e4SLinus Torvalds return 0; 11461da177e4SLinus Torvalds } 11471da177e4SLinus Torvalds EXPORT_SYMBOL(agp_generic_remove_memory); 11481da177e4SLinus Torvalds 11491da177e4SLinus Torvalds struct agp_memory *agp_generic_alloc_by_type(size_t page_count, int type) 11501da177e4SLinus Torvalds { 11511da177e4SLinus Torvalds return NULL; 11521da177e4SLinus Torvalds } 11531da177e4SLinus Torvalds EXPORT_SYMBOL(agp_generic_alloc_by_type); 11541da177e4SLinus Torvalds 11551da177e4SLinus Torvalds void agp_generic_free_by_type(struct agp_memory *curr) 11561da177e4SLinus Torvalds { 1157a030ce44SThomas Hellstrom agp_free_page_array(curr); 11581da177e4SLinus Torvalds agp_free_key(curr->key); 11591da177e4SLinus Torvalds kfree(curr); 11601da177e4SLinus Torvalds } 11611da177e4SLinus Torvalds EXPORT_SYMBOL(agp_generic_free_by_type); 11621da177e4SLinus Torvalds 1163a030ce44SThomas Hellstrom struct agp_memory *agp_generic_alloc_user(size_t page_count, int type) 1164a030ce44SThomas Hellstrom { 1165a030ce44SThomas Hellstrom struct agp_memory *new; 1166a030ce44SThomas Hellstrom int i; 1167a030ce44SThomas Hellstrom int pages; 1168a030ce44SThomas Hellstrom 1169a030ce44SThomas Hellstrom pages = (page_count + ENTRIES_PER_PAGE - 1) / ENTRIES_PER_PAGE; 1170a030ce44SThomas Hellstrom new = agp_create_user_memory(page_count); 1171a030ce44SThomas Hellstrom if (new == NULL) 1172a030ce44SThomas Hellstrom return NULL; 1173a030ce44SThomas Hellstrom 11741c14cfbbSAndrew Morton for (i = 0; i < page_count; i++) 117583897badSBill Pemberton new->pages[i] = NULL; 1176a030ce44SThomas Hellstrom new->page_count = 0; 1177a030ce44SThomas Hellstrom new->type = type; 1178a030ce44SThomas Hellstrom new->num_scratch_pages = pages; 1179a030ce44SThomas Hellstrom 1180a030ce44SThomas Hellstrom return new; 1181a030ce44SThomas Hellstrom } 1182a030ce44SThomas Hellstrom EXPORT_SYMBOL(agp_generic_alloc_user); 1183a030ce44SThomas Hellstrom 11841da177e4SLinus Torvalds /* 11851da177e4SLinus Torvalds * Basic Page Allocation Routines - 11861da177e4SLinus Torvalds * These routines handle page allocation and by default they reserve the allocated 11871da177e4SLinus Torvalds * memory. They also handle incrementing the current_memory_agp value, Which is checked 11881da177e4SLinus Torvalds * against a maximum value. 11891da177e4SLinus Torvalds */ 11901da177e4SLinus Torvalds 119137acee10SShaohua Li int agp_generic_alloc_pages(struct agp_bridge_data *bridge, struct agp_memory *mem, size_t num_pages) 119237acee10SShaohua Li { 119337acee10SShaohua Li struct page * page; 119437acee10SShaohua Li int i, ret = -ENOMEM; 119537acee10SShaohua Li 119637acee10SShaohua Li for (i = 0; i < num_pages; i++) { 119759de2bebSShaohua Li page = alloc_page(GFP_KERNEL | GFP_DMA32 | __GFP_ZERO); 119837acee10SShaohua Li /* agp_free_memory() needs gart address */ 119937acee10SShaohua Li if (page == NULL) 120037acee10SShaohua Li goto out; 120137acee10SShaohua Li 120237acee10SShaohua Li #ifndef CONFIG_X86 120337acee10SShaohua Li map_page_into_agp(page); 120437acee10SShaohua Li #endif 120537acee10SShaohua Li get_page(page); 120637acee10SShaohua Li atomic_inc(&agp_bridge->current_memory_agp); 120737acee10SShaohua Li 120807613ba2SDave Airlie mem->pages[i] = page; 120937acee10SShaohua Li mem->page_count++; 121037acee10SShaohua Li } 121137acee10SShaohua Li 121237acee10SShaohua Li #ifdef CONFIG_X86 121307613ba2SDave Airlie set_pages_array_uc(mem->pages, num_pages); 121437acee10SShaohua Li #endif 121537acee10SShaohua Li ret = 0; 121637acee10SShaohua Li out: 121737acee10SShaohua Li return ret; 121837acee10SShaohua Li } 121937acee10SShaohua Li EXPORT_SYMBOL(agp_generic_alloc_pages); 122037acee10SShaohua Li 122107613ba2SDave Airlie struct page *agp_generic_alloc_page(struct agp_bridge_data *bridge) 12221da177e4SLinus Torvalds { 12231da177e4SLinus Torvalds struct page * page; 12241da177e4SLinus Torvalds 122559de2bebSShaohua Li page = alloc_page(GFP_KERNEL | GFP_DMA32 | __GFP_ZERO); 12261da177e4SLinus Torvalds if (page == NULL) 12271da177e4SLinus Torvalds return NULL; 12281da177e4SLinus Torvalds 12299326d61bSIngo Molnar map_page_into_agp(page); 12301da177e4SLinus Torvalds 12311da177e4SLinus Torvalds get_page(page); 12321da177e4SLinus Torvalds atomic_inc(&agp_bridge->current_memory_agp); 123307613ba2SDave Airlie return page; 12341da177e4SLinus Torvalds } 12351da177e4SLinus Torvalds EXPORT_SYMBOL(agp_generic_alloc_page); 12361da177e4SLinus Torvalds 1237bd07928cSShaohua Li void agp_generic_destroy_pages(struct agp_memory *mem) 1238bd07928cSShaohua Li { 1239bd07928cSShaohua Li int i; 1240bd07928cSShaohua Li struct page *page; 1241bd07928cSShaohua Li 1242bd07928cSShaohua Li if (!mem) 1243bd07928cSShaohua Li return; 1244bd07928cSShaohua Li 1245bd07928cSShaohua Li #ifdef CONFIG_X86 124607613ba2SDave Airlie set_pages_array_wb(mem->pages, mem->page_count); 1247bd07928cSShaohua Li #endif 1248bd07928cSShaohua Li 1249bd07928cSShaohua Li for (i = 0; i < mem->page_count; i++) { 125007613ba2SDave Airlie page = mem->pages[i]; 1251bd07928cSShaohua Li 1252bd07928cSShaohua Li #ifndef CONFIG_X86 1253bd07928cSShaohua Li unmap_page_from_agp(page); 1254bd07928cSShaohua Li #endif 1255bd07928cSShaohua Li put_page(page); 125607613ba2SDave Airlie __free_page(page); 1257bd07928cSShaohua Li atomic_dec(&agp_bridge->current_memory_agp); 125807613ba2SDave Airlie mem->pages[i] = NULL; 1259bd07928cSShaohua Li } 1260bd07928cSShaohua Li } 1261bd07928cSShaohua Li EXPORT_SYMBOL(agp_generic_destroy_pages); 12621da177e4SLinus Torvalds 126307613ba2SDave Airlie void agp_generic_destroy_page(struct page *page, int flags) 12641da177e4SLinus Torvalds { 126507613ba2SDave Airlie if (page == NULL) 12661da177e4SLinus Torvalds return; 12671da177e4SLinus Torvalds 1268a2721e99SDave Airlie if (flags & AGP_PAGE_DESTROY_UNMAP) 12691da177e4SLinus Torvalds unmap_page_from_agp(page); 1270a2721e99SDave Airlie 1271a2721e99SDave Airlie if (flags & AGP_PAGE_DESTROY_FREE) { 12721da177e4SLinus Torvalds put_page(page); 127307613ba2SDave Airlie __free_page(page); 12741da177e4SLinus Torvalds atomic_dec(&agp_bridge->current_memory_agp); 12751da177e4SLinus Torvalds } 1276a2721e99SDave Airlie } 12771da177e4SLinus Torvalds EXPORT_SYMBOL(agp_generic_destroy_page); 12781da177e4SLinus Torvalds 12791da177e4SLinus Torvalds /* End Basic Page Allocation Routines */ 12801da177e4SLinus Torvalds 12811da177e4SLinus Torvalds 12821da177e4SLinus Torvalds /** 12831da177e4SLinus Torvalds * agp_enable - initialise the agp point-to-point connection. 12841da177e4SLinus Torvalds * 12851da177e4SLinus Torvalds * @mode: agp mode register value to configure with. 12861da177e4SLinus Torvalds */ 12871da177e4SLinus Torvalds void agp_enable(struct agp_bridge_data *bridge, u32 mode) 12881da177e4SLinus Torvalds { 12891da177e4SLinus Torvalds if (!bridge) 12901da177e4SLinus Torvalds return; 12911da177e4SLinus Torvalds bridge->driver->agp_enable(bridge, mode); 12921da177e4SLinus Torvalds } 12931da177e4SLinus Torvalds EXPORT_SYMBOL(agp_enable); 12941da177e4SLinus Torvalds 12951da177e4SLinus Torvalds /* When we remove the global variable agp_bridge from all drivers 12961da177e4SLinus Torvalds * then agp_alloc_bridge and agp_generic_find_bridge need to be updated 12971da177e4SLinus Torvalds */ 12981da177e4SLinus Torvalds 12991da177e4SLinus Torvalds struct agp_bridge_data *agp_generic_find_bridge(struct pci_dev *pdev) 13001da177e4SLinus Torvalds { 13011da177e4SLinus Torvalds if (list_empty(&agp_bridges)) 13021da177e4SLinus Torvalds return NULL; 13031da177e4SLinus Torvalds 13041da177e4SLinus Torvalds return agp_bridge; 13051da177e4SLinus Torvalds } 13061da177e4SLinus Torvalds 13071da177e4SLinus Torvalds static void ipi_handler(void *null) 13081da177e4SLinus Torvalds { 13091da177e4SLinus Torvalds flush_agp_cache(); 13101da177e4SLinus Torvalds } 13111da177e4SLinus Torvalds 13121da177e4SLinus Torvalds void global_cache_flush(void) 13131da177e4SLinus Torvalds { 131415c8b6c1SJens Axboe if (on_each_cpu(ipi_handler, NULL, 1) != 0) 13151da177e4SLinus Torvalds panic(PFX "timed out waiting for the other CPUs!\n"); 13161da177e4SLinus Torvalds } 13171da177e4SLinus Torvalds EXPORT_SYMBOL(global_cache_flush); 13181da177e4SLinus Torvalds 13191da177e4SLinus Torvalds unsigned long agp_generic_mask_memory(struct agp_bridge_data *bridge, 13202a4ceb6dSDavid Woodhouse dma_addr_t addr, int type) 13211da177e4SLinus Torvalds { 13221da177e4SLinus Torvalds /* memory type is ignored in the generic routine */ 13231da177e4SLinus Torvalds if (bridge->driver->masks) 13241da177e4SLinus Torvalds return addr | bridge->driver->masks[0].mask; 13251da177e4SLinus Torvalds else 13261da177e4SLinus Torvalds return addr; 13271da177e4SLinus Torvalds } 13281da177e4SLinus Torvalds EXPORT_SYMBOL(agp_generic_mask_memory); 13291da177e4SLinus Torvalds 1330a030ce44SThomas Hellstrom int agp_generic_type_to_mask_type(struct agp_bridge_data *bridge, 1331a030ce44SThomas Hellstrom int type) 1332a030ce44SThomas Hellstrom { 1333a030ce44SThomas Hellstrom if (type >= AGP_USER_TYPES) 1334a030ce44SThomas Hellstrom return 0; 1335a030ce44SThomas Hellstrom return type; 1336a030ce44SThomas Hellstrom } 1337a030ce44SThomas Hellstrom EXPORT_SYMBOL(agp_generic_type_to_mask_type); 1338a030ce44SThomas Hellstrom 13391da177e4SLinus Torvalds /* 13401da177e4SLinus Torvalds * These functions are implemented according to the AGPv3 spec, 13411da177e4SLinus Torvalds * which covers implementation details that had previously been 13421da177e4SLinus Torvalds * left open. 13431da177e4SLinus Torvalds */ 13441da177e4SLinus Torvalds 13451da177e4SLinus Torvalds int agp3_generic_fetch_size(void) 13461da177e4SLinus Torvalds { 13471da177e4SLinus Torvalds u16 temp_size; 13481da177e4SLinus Torvalds int i; 13491da177e4SLinus Torvalds struct aper_size_info_16 *values; 13501da177e4SLinus Torvalds 13511da177e4SLinus Torvalds pci_read_config_word(agp_bridge->dev, agp_bridge->capndx+AGPAPSIZE, &temp_size); 13521da177e4SLinus Torvalds values = A_SIZE_16(agp_bridge->driver->aperture_sizes); 13531da177e4SLinus Torvalds 13541da177e4SLinus Torvalds for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++) { 13551da177e4SLinus Torvalds if (temp_size == values[i].size_value) { 13561da177e4SLinus Torvalds agp_bridge->previous_size = 13571da177e4SLinus Torvalds agp_bridge->current_size = (void *) (values + i); 13581da177e4SLinus Torvalds 13591da177e4SLinus Torvalds agp_bridge->aperture_size_idx = i; 13601da177e4SLinus Torvalds return values[i].size; 13611da177e4SLinus Torvalds } 13621da177e4SLinus Torvalds } 13631da177e4SLinus Torvalds return 0; 13641da177e4SLinus Torvalds } 13651da177e4SLinus Torvalds EXPORT_SYMBOL(agp3_generic_fetch_size); 13661da177e4SLinus Torvalds 13671da177e4SLinus Torvalds void agp3_generic_tlbflush(struct agp_memory *mem) 13681da177e4SLinus Torvalds { 13691da177e4SLinus Torvalds u32 ctrl; 13701da177e4SLinus Torvalds pci_read_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, &ctrl); 13711da177e4SLinus Torvalds pci_write_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, ctrl & ~AGPCTRL_GTLBEN); 13721da177e4SLinus Torvalds pci_write_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, ctrl); 13731da177e4SLinus Torvalds } 13741da177e4SLinus Torvalds EXPORT_SYMBOL(agp3_generic_tlbflush); 13751da177e4SLinus Torvalds 13761da177e4SLinus Torvalds int agp3_generic_configure(void) 13771da177e4SLinus Torvalds { 13781da177e4SLinus Torvalds u32 temp; 13791da177e4SLinus Torvalds struct aper_size_info_16 *current_size; 13801da177e4SLinus Torvalds 13811da177e4SLinus Torvalds current_size = A_SIZE_16(agp_bridge->current_size); 13821da177e4SLinus Torvalds 1383e501b3d8SBjorn Helgaas agp_bridge->gart_bus_addr = pci_bus_address(agp_bridge->dev, 1384e501b3d8SBjorn Helgaas AGP_APERTURE_BAR); 13851da177e4SLinus Torvalds 13861da177e4SLinus Torvalds /* set aperture size */ 13871da177e4SLinus Torvalds pci_write_config_word(agp_bridge->dev, agp_bridge->capndx+AGPAPSIZE, current_size->size_value); 13881da177e4SLinus Torvalds /* set gart pointer */ 13891da177e4SLinus Torvalds pci_write_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPGARTLO, agp_bridge->gatt_bus_addr); 13901da177e4SLinus Torvalds /* enable aperture and GTLB */ 13911da177e4SLinus Torvalds pci_read_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, &temp); 13921da177e4SLinus Torvalds pci_write_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, temp | AGPCTRL_APERENB | AGPCTRL_GTLBEN); 13931da177e4SLinus Torvalds return 0; 13941da177e4SLinus Torvalds } 13951da177e4SLinus Torvalds EXPORT_SYMBOL(agp3_generic_configure); 13961da177e4SLinus Torvalds 13971da177e4SLinus Torvalds void agp3_generic_cleanup(void) 13981da177e4SLinus Torvalds { 13991da177e4SLinus Torvalds u32 ctrl; 14001da177e4SLinus Torvalds pci_read_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, &ctrl); 14011da177e4SLinus Torvalds pci_write_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, ctrl & ~AGPCTRL_APERENB); 14021da177e4SLinus Torvalds } 14031da177e4SLinus Torvalds EXPORT_SYMBOL(agp3_generic_cleanup); 14041da177e4SLinus Torvalds 1405e5524f35SDave Jones const struct aper_size_info_16 agp3_generic_sizes[AGP_GENERIC_SIZES_ENTRIES] = 14061da177e4SLinus Torvalds { 14071da177e4SLinus Torvalds {4096, 1048576, 10,0x000}, 14081da177e4SLinus Torvalds {2048, 524288, 9, 0x800}, 14091da177e4SLinus Torvalds {1024, 262144, 8, 0xc00}, 14101da177e4SLinus Torvalds { 512, 131072, 7, 0xe00}, 14111da177e4SLinus Torvalds { 256, 65536, 6, 0xf00}, 14121da177e4SLinus Torvalds { 128, 32768, 5, 0xf20}, 14131da177e4SLinus Torvalds { 64, 16384, 4, 0xf30}, 14141da177e4SLinus Torvalds { 32, 8192, 3, 0xf38}, 14151da177e4SLinus Torvalds { 16, 4096, 2, 0xf3c}, 14161da177e4SLinus Torvalds { 8, 2048, 1, 0xf3e}, 14171da177e4SLinus Torvalds { 4, 1024, 0, 0xf3f} 14181da177e4SLinus Torvalds }; 14191da177e4SLinus Torvalds EXPORT_SYMBOL(agp3_generic_sizes); 14201da177e4SLinus Torvalds 1421