18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * AGPGART driver. 38c2ecf20Sopenharmony_ci * Copyright (C) 2004 Silicon Graphics, Inc. 48c2ecf20Sopenharmony_ci * Copyright (C) 2002-2005 Dave Jones. 58c2ecf20Sopenharmony_ci * Copyright (C) 1999 Jeff Hartmann. 68c2ecf20Sopenharmony_ci * Copyright (C) 1999 Precision Insight, Inc. 78c2ecf20Sopenharmony_ci * Copyright (C) 1999 Xi Graphics, Inc. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 108c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 118c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation 128c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 138c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 148c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice shall be included 178c2ecf20Sopenharmony_ci * in all copies or substantial portions of the Software. 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 208c2ecf20Sopenharmony_ci * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 218c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 228c2ecf20Sopenharmony_ci * JEFF HARTMANN, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM, 238c2ecf20Sopenharmony_ci * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 248c2ecf20Sopenharmony_ci * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 258c2ecf20Sopenharmony_ci * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 268c2ecf20Sopenharmony_ci * 278c2ecf20Sopenharmony_ci * TODO: 288c2ecf20Sopenharmony_ci * - Allocate more than order 0 pages to avoid too much linear map splitting. 298c2ecf20Sopenharmony_ci */ 308c2ecf20Sopenharmony_ci#include <linux/module.h> 318c2ecf20Sopenharmony_ci#include <linux/pci.h> 328c2ecf20Sopenharmony_ci#include <linux/pagemap.h> 338c2ecf20Sopenharmony_ci#include <linux/miscdevice.h> 348c2ecf20Sopenharmony_ci#include <linux/pm.h> 358c2ecf20Sopenharmony_ci#include <linux/agp_backend.h> 368c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 378c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 388c2ecf20Sopenharmony_ci#include <linux/mm.h> 398c2ecf20Sopenharmony_ci#include <linux/sched.h> 408c2ecf20Sopenharmony_ci#include <linux/slab.h> 418c2ecf20Sopenharmony_ci#include <asm/io.h> 428c2ecf20Sopenharmony_ci#ifdef CONFIG_X86 438c2ecf20Sopenharmony_ci#include <asm/set_memory.h> 448c2ecf20Sopenharmony_ci#endif 458c2ecf20Sopenharmony_ci#include "agp.h" 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci__u32 *agp_gatt_table; 488c2ecf20Sopenharmony_ciint agp_memory_reserved; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/* 518c2ecf20Sopenharmony_ci * Needed by the Nforce GART driver for the time being. Would be 528c2ecf20Sopenharmony_ci * nice to do this some other way instead of needing this export. 538c2ecf20Sopenharmony_ci */ 548c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(agp_memory_reserved); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/* 578c2ecf20Sopenharmony_ci * Generic routines for handling agp_memory structures - 588c2ecf20Sopenharmony_ci * They use the basic page allocation routines to do the brunt of the work. 598c2ecf20Sopenharmony_ci */ 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_civoid agp_free_key(int key) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci if (key < 0) 648c2ecf20Sopenharmony_ci return; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci if (key < MAXKEY) 678c2ecf20Sopenharmony_ci clear_bit(key, agp_bridge->key_list); 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ciEXPORT_SYMBOL(agp_free_key); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic int agp_get_key(void) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci int bit; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci bit = find_first_zero_bit(agp_bridge->key_list, MAXKEY); 778c2ecf20Sopenharmony_ci if (bit < MAXKEY) { 788c2ecf20Sopenharmony_ci set_bit(bit, agp_bridge->key_list); 798c2ecf20Sopenharmony_ci return bit; 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci return -1; 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci/* 858c2ecf20Sopenharmony_ci * Use kmalloc if possible for the page list. Otherwise fall back to 868c2ecf20Sopenharmony_ci * vmalloc. This speeds things up and also saves memory for small AGP 878c2ecf20Sopenharmony_ci * regions. 888c2ecf20Sopenharmony_ci */ 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_civoid agp_alloc_page_array(size_t size, struct agp_memory *mem) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci mem->pages = kvmalloc(size, GFP_KERNEL); 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ciEXPORT_SYMBOL(agp_alloc_page_array); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic struct agp_memory *agp_create_user_memory(unsigned long num_agp_pages) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci struct agp_memory *new; 998c2ecf20Sopenharmony_ci unsigned long alloc_size = num_agp_pages*sizeof(struct page *); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci if (INT_MAX/sizeof(struct page *) < num_agp_pages) 1028c2ecf20Sopenharmony_ci return NULL; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci new = kzalloc(sizeof(struct agp_memory), GFP_KERNEL); 1058c2ecf20Sopenharmony_ci if (new == NULL) 1068c2ecf20Sopenharmony_ci return NULL; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci new->key = agp_get_key(); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci if (new->key < 0) { 1118c2ecf20Sopenharmony_ci kfree(new); 1128c2ecf20Sopenharmony_ci return NULL; 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci agp_alloc_page_array(alloc_size, new); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci if (new->pages == NULL) { 1188c2ecf20Sopenharmony_ci agp_free_key(new->key); 1198c2ecf20Sopenharmony_ci kfree(new); 1208c2ecf20Sopenharmony_ci return NULL; 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci new->num_scratch_pages = 0; 1238c2ecf20Sopenharmony_ci return new; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistruct agp_memory *agp_create_memory(int scratch_pages) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci struct agp_memory *new; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci new = kzalloc(sizeof(struct agp_memory), GFP_KERNEL); 1318c2ecf20Sopenharmony_ci if (new == NULL) 1328c2ecf20Sopenharmony_ci return NULL; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci new->key = agp_get_key(); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci if (new->key < 0) { 1378c2ecf20Sopenharmony_ci kfree(new); 1388c2ecf20Sopenharmony_ci return NULL; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci agp_alloc_page_array(PAGE_SIZE * scratch_pages, new); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci if (new->pages == NULL) { 1448c2ecf20Sopenharmony_ci agp_free_key(new->key); 1458c2ecf20Sopenharmony_ci kfree(new); 1468c2ecf20Sopenharmony_ci return NULL; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci new->num_scratch_pages = scratch_pages; 1498c2ecf20Sopenharmony_ci new->type = AGP_NORMAL_MEMORY; 1508c2ecf20Sopenharmony_ci return new; 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ciEXPORT_SYMBOL(agp_create_memory); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci/** 1558c2ecf20Sopenharmony_ci * agp_free_memory - free memory associated with an agp_memory pointer. 1568c2ecf20Sopenharmony_ci * 1578c2ecf20Sopenharmony_ci * @curr: agp_memory pointer to be freed. 1588c2ecf20Sopenharmony_ci * 1598c2ecf20Sopenharmony_ci * It is the only function that can be called when the backend is not owned 1608c2ecf20Sopenharmony_ci * by the caller. (So it can free memory on client death.) 1618c2ecf20Sopenharmony_ci */ 1628c2ecf20Sopenharmony_civoid agp_free_memory(struct agp_memory *curr) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci size_t i; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci if (curr == NULL) 1678c2ecf20Sopenharmony_ci return; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci if (curr->is_bound) 1708c2ecf20Sopenharmony_ci agp_unbind_memory(curr); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci if (curr->type >= AGP_USER_TYPES) { 1738c2ecf20Sopenharmony_ci agp_generic_free_by_type(curr); 1748c2ecf20Sopenharmony_ci return; 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if (curr->type != 0) { 1788c2ecf20Sopenharmony_ci curr->bridge->driver->free_by_type(curr); 1798c2ecf20Sopenharmony_ci return; 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci if (curr->page_count != 0) { 1828c2ecf20Sopenharmony_ci if (curr->bridge->driver->agp_destroy_pages) { 1838c2ecf20Sopenharmony_ci curr->bridge->driver->agp_destroy_pages(curr); 1848c2ecf20Sopenharmony_ci } else { 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci for (i = 0; i < curr->page_count; i++) { 1878c2ecf20Sopenharmony_ci curr->bridge->driver->agp_destroy_page( 1888c2ecf20Sopenharmony_ci curr->pages[i], 1898c2ecf20Sopenharmony_ci AGP_PAGE_DESTROY_UNMAP); 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci for (i = 0; i < curr->page_count; i++) { 1928c2ecf20Sopenharmony_ci curr->bridge->driver->agp_destroy_page( 1938c2ecf20Sopenharmony_ci curr->pages[i], 1948c2ecf20Sopenharmony_ci AGP_PAGE_DESTROY_FREE); 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci agp_free_key(curr->key); 1998c2ecf20Sopenharmony_ci agp_free_page_array(curr); 2008c2ecf20Sopenharmony_ci kfree(curr); 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ciEXPORT_SYMBOL(agp_free_memory); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci#define ENTRIES_PER_PAGE (PAGE_SIZE / sizeof(unsigned long)) 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci/** 2078c2ecf20Sopenharmony_ci * agp_allocate_memory - allocate a group of pages of a certain type. 2088c2ecf20Sopenharmony_ci * 2098c2ecf20Sopenharmony_ci * @bridge: an agp_bridge_data struct allocated for the AGP host bridge. 2108c2ecf20Sopenharmony_ci * @page_count: size_t argument of the number of pages 2118c2ecf20Sopenharmony_ci * @type: u32 argument of the type of memory to be allocated. 2128c2ecf20Sopenharmony_ci * 2138c2ecf20Sopenharmony_ci * Every agp bridge device will allow you to allocate AGP_NORMAL_MEMORY which 2148c2ecf20Sopenharmony_ci * maps to physical ram. Any other type is device dependent. 2158c2ecf20Sopenharmony_ci * 2168c2ecf20Sopenharmony_ci * It returns NULL whenever memory is unavailable. 2178c2ecf20Sopenharmony_ci */ 2188c2ecf20Sopenharmony_cistruct agp_memory *agp_allocate_memory(struct agp_bridge_data *bridge, 2198c2ecf20Sopenharmony_ci size_t page_count, u32 type) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci int scratch_pages; 2228c2ecf20Sopenharmony_ci struct agp_memory *new; 2238c2ecf20Sopenharmony_ci size_t i; 2248c2ecf20Sopenharmony_ci int cur_memory; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci if (!bridge) 2278c2ecf20Sopenharmony_ci return NULL; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci cur_memory = atomic_read(&bridge->current_memory_agp); 2308c2ecf20Sopenharmony_ci if ((cur_memory + page_count > bridge->max_memory_agp) || 2318c2ecf20Sopenharmony_ci (cur_memory + page_count < page_count)) 2328c2ecf20Sopenharmony_ci return NULL; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci if (type >= AGP_USER_TYPES) { 2358c2ecf20Sopenharmony_ci new = agp_generic_alloc_user(page_count, type); 2368c2ecf20Sopenharmony_ci if (new) 2378c2ecf20Sopenharmony_ci new->bridge = bridge; 2388c2ecf20Sopenharmony_ci return new; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci if (type != 0) { 2428c2ecf20Sopenharmony_ci new = bridge->driver->alloc_by_type(page_count, type); 2438c2ecf20Sopenharmony_ci if (new) 2448c2ecf20Sopenharmony_ci new->bridge = bridge; 2458c2ecf20Sopenharmony_ci return new; 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci scratch_pages = (page_count + ENTRIES_PER_PAGE - 1) / ENTRIES_PER_PAGE; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci new = agp_create_memory(scratch_pages); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci if (new == NULL) 2538c2ecf20Sopenharmony_ci return NULL; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci if (bridge->driver->agp_alloc_pages) { 2568c2ecf20Sopenharmony_ci if (bridge->driver->agp_alloc_pages(bridge, new, page_count)) { 2578c2ecf20Sopenharmony_ci agp_free_memory(new); 2588c2ecf20Sopenharmony_ci return NULL; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci new->bridge = bridge; 2618c2ecf20Sopenharmony_ci return new; 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci for (i = 0; i < page_count; i++) { 2658c2ecf20Sopenharmony_ci struct page *page = bridge->driver->agp_alloc_page(bridge); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci if (page == NULL) { 2688c2ecf20Sopenharmony_ci agp_free_memory(new); 2698c2ecf20Sopenharmony_ci return NULL; 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci new->pages[i] = page; 2728c2ecf20Sopenharmony_ci new->page_count++; 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci new->bridge = bridge; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci return new; 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ciEXPORT_SYMBOL(agp_allocate_memory); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci/* End - Generic routines for handling agp_memory structures */ 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cistatic int agp_return_size(void) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci int current_size; 2878c2ecf20Sopenharmony_ci void *temp; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci temp = agp_bridge->current_size; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci switch (agp_bridge->driver->size_type) { 2928c2ecf20Sopenharmony_ci case U8_APER_SIZE: 2938c2ecf20Sopenharmony_ci current_size = A_SIZE_8(temp)->size; 2948c2ecf20Sopenharmony_ci break; 2958c2ecf20Sopenharmony_ci case U16_APER_SIZE: 2968c2ecf20Sopenharmony_ci current_size = A_SIZE_16(temp)->size; 2978c2ecf20Sopenharmony_ci break; 2988c2ecf20Sopenharmony_ci case U32_APER_SIZE: 2998c2ecf20Sopenharmony_ci current_size = A_SIZE_32(temp)->size; 3008c2ecf20Sopenharmony_ci break; 3018c2ecf20Sopenharmony_ci case LVL2_APER_SIZE: 3028c2ecf20Sopenharmony_ci current_size = A_SIZE_LVL2(temp)->size; 3038c2ecf20Sopenharmony_ci break; 3048c2ecf20Sopenharmony_ci case FIXED_APER_SIZE: 3058c2ecf20Sopenharmony_ci current_size = A_SIZE_FIX(temp)->size; 3068c2ecf20Sopenharmony_ci break; 3078c2ecf20Sopenharmony_ci default: 3088c2ecf20Sopenharmony_ci current_size = 0; 3098c2ecf20Sopenharmony_ci break; 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci current_size -= (agp_memory_reserved / (1024*1024)); 3138c2ecf20Sopenharmony_ci if (current_size <0) 3148c2ecf20Sopenharmony_ci current_size = 0; 3158c2ecf20Sopenharmony_ci return current_size; 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ciint agp_num_entries(void) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci int num_entries; 3228c2ecf20Sopenharmony_ci void *temp; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci temp = agp_bridge->current_size; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci switch (agp_bridge->driver->size_type) { 3278c2ecf20Sopenharmony_ci case U8_APER_SIZE: 3288c2ecf20Sopenharmony_ci num_entries = A_SIZE_8(temp)->num_entries; 3298c2ecf20Sopenharmony_ci break; 3308c2ecf20Sopenharmony_ci case U16_APER_SIZE: 3318c2ecf20Sopenharmony_ci num_entries = A_SIZE_16(temp)->num_entries; 3328c2ecf20Sopenharmony_ci break; 3338c2ecf20Sopenharmony_ci case U32_APER_SIZE: 3348c2ecf20Sopenharmony_ci num_entries = A_SIZE_32(temp)->num_entries; 3358c2ecf20Sopenharmony_ci break; 3368c2ecf20Sopenharmony_ci case LVL2_APER_SIZE: 3378c2ecf20Sopenharmony_ci num_entries = A_SIZE_LVL2(temp)->num_entries; 3388c2ecf20Sopenharmony_ci break; 3398c2ecf20Sopenharmony_ci case FIXED_APER_SIZE: 3408c2ecf20Sopenharmony_ci num_entries = A_SIZE_FIX(temp)->num_entries; 3418c2ecf20Sopenharmony_ci break; 3428c2ecf20Sopenharmony_ci default: 3438c2ecf20Sopenharmony_ci num_entries = 0; 3448c2ecf20Sopenharmony_ci break; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci num_entries -= agp_memory_reserved>>PAGE_SHIFT; 3488c2ecf20Sopenharmony_ci if (num_entries<0) 3498c2ecf20Sopenharmony_ci num_entries = 0; 3508c2ecf20Sopenharmony_ci return num_entries; 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(agp_num_entries); 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci/** 3568c2ecf20Sopenharmony_ci * agp_copy_info - copy bridge state information 3578c2ecf20Sopenharmony_ci * 3588c2ecf20Sopenharmony_ci * @bridge: an agp_bridge_data struct allocated for the AGP host bridge. 3598c2ecf20Sopenharmony_ci * @info: agp_kern_info pointer. The caller should insure that this pointer is valid. 3608c2ecf20Sopenharmony_ci * 3618c2ecf20Sopenharmony_ci * This function copies information about the agp bridge device and the state of 3628c2ecf20Sopenharmony_ci * the agp backend into an agp_kern_info pointer. 3638c2ecf20Sopenharmony_ci */ 3648c2ecf20Sopenharmony_ciint agp_copy_info(struct agp_bridge_data *bridge, struct agp_kern_info *info) 3658c2ecf20Sopenharmony_ci{ 3668c2ecf20Sopenharmony_ci memset(info, 0, sizeof(struct agp_kern_info)); 3678c2ecf20Sopenharmony_ci if (!bridge) { 3688c2ecf20Sopenharmony_ci info->chipset = NOT_SUPPORTED; 3698c2ecf20Sopenharmony_ci return -EIO; 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci info->version.major = bridge->version->major; 3738c2ecf20Sopenharmony_ci info->version.minor = bridge->version->minor; 3748c2ecf20Sopenharmony_ci info->chipset = SUPPORTED; 3758c2ecf20Sopenharmony_ci info->device = bridge->dev; 3768c2ecf20Sopenharmony_ci if (bridge->mode & AGPSTAT_MODE_3_0) 3778c2ecf20Sopenharmony_ci info->mode = bridge->mode & ~AGP3_RESERVED_MASK; 3788c2ecf20Sopenharmony_ci else 3798c2ecf20Sopenharmony_ci info->mode = bridge->mode & ~AGP2_RESERVED_MASK; 3808c2ecf20Sopenharmony_ci info->aper_base = bridge->gart_bus_addr; 3818c2ecf20Sopenharmony_ci info->aper_size = agp_return_size(); 3828c2ecf20Sopenharmony_ci info->max_memory = bridge->max_memory_agp; 3838c2ecf20Sopenharmony_ci info->current_memory = atomic_read(&bridge->current_memory_agp); 3848c2ecf20Sopenharmony_ci info->cant_use_aperture = bridge->driver->cant_use_aperture; 3858c2ecf20Sopenharmony_ci info->vm_ops = bridge->vm_ops; 3868c2ecf20Sopenharmony_ci info->page_mask = ~0UL; 3878c2ecf20Sopenharmony_ci return 0; 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ciEXPORT_SYMBOL(agp_copy_info); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci/* End - Routine to copy over information structure */ 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci/* 3948c2ecf20Sopenharmony_ci * Routines for handling swapping of agp_memory into the GATT - 3958c2ecf20Sopenharmony_ci * These routines take agp_memory and insert them into the GATT. 3968c2ecf20Sopenharmony_ci * They call device specific routines to actually write to the GATT. 3978c2ecf20Sopenharmony_ci */ 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci/** 4008c2ecf20Sopenharmony_ci * agp_bind_memory - Bind an agp_memory structure into the GATT. 4018c2ecf20Sopenharmony_ci * 4028c2ecf20Sopenharmony_ci * @curr: agp_memory pointer 4038c2ecf20Sopenharmony_ci * @pg_start: an offset into the graphics aperture translation table 4048c2ecf20Sopenharmony_ci * 4058c2ecf20Sopenharmony_ci * It returns -EINVAL if the pointer == NULL. 4068c2ecf20Sopenharmony_ci * It returns -EBUSY if the area of the table requested is already in use. 4078c2ecf20Sopenharmony_ci */ 4088c2ecf20Sopenharmony_ciint agp_bind_memory(struct agp_memory *curr, off_t pg_start) 4098c2ecf20Sopenharmony_ci{ 4108c2ecf20Sopenharmony_ci int ret_val; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci if (curr == NULL) 4138c2ecf20Sopenharmony_ci return -EINVAL; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci if (curr->is_bound) { 4168c2ecf20Sopenharmony_ci printk(KERN_INFO PFX "memory %p is already bound!\n", curr); 4178c2ecf20Sopenharmony_ci return -EINVAL; 4188c2ecf20Sopenharmony_ci } 4198c2ecf20Sopenharmony_ci if (!curr->is_flushed) { 4208c2ecf20Sopenharmony_ci curr->bridge->driver->cache_flush(); 4218c2ecf20Sopenharmony_ci curr->is_flushed = true; 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci ret_val = curr->bridge->driver->insert_memory(curr, pg_start, curr->type); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci if (ret_val != 0) 4278c2ecf20Sopenharmony_ci return ret_val; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci curr->is_bound = true; 4308c2ecf20Sopenharmony_ci curr->pg_start = pg_start; 4318c2ecf20Sopenharmony_ci spin_lock(&agp_bridge->mapped_lock); 4328c2ecf20Sopenharmony_ci list_add(&curr->mapped_list, &agp_bridge->mapped_list); 4338c2ecf20Sopenharmony_ci spin_unlock(&agp_bridge->mapped_lock); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci return 0; 4368c2ecf20Sopenharmony_ci} 4378c2ecf20Sopenharmony_ciEXPORT_SYMBOL(agp_bind_memory); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci/** 4418c2ecf20Sopenharmony_ci * agp_unbind_memory - Removes an agp_memory structure from the GATT 4428c2ecf20Sopenharmony_ci * 4438c2ecf20Sopenharmony_ci * @curr: agp_memory pointer to be removed from the GATT. 4448c2ecf20Sopenharmony_ci * 4458c2ecf20Sopenharmony_ci * It returns -EINVAL if this piece of agp_memory is not currently bound to 4468c2ecf20Sopenharmony_ci * the graphics aperture translation table or if the agp_memory pointer == NULL 4478c2ecf20Sopenharmony_ci */ 4488c2ecf20Sopenharmony_ciint agp_unbind_memory(struct agp_memory *curr) 4498c2ecf20Sopenharmony_ci{ 4508c2ecf20Sopenharmony_ci int ret_val; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci if (curr == NULL) 4538c2ecf20Sopenharmony_ci return -EINVAL; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci if (!curr->is_bound) { 4568c2ecf20Sopenharmony_ci printk(KERN_INFO PFX "memory %p was not bound!\n", curr); 4578c2ecf20Sopenharmony_ci return -EINVAL; 4588c2ecf20Sopenharmony_ci } 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci ret_val = curr->bridge->driver->remove_memory(curr, curr->pg_start, curr->type); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci if (ret_val != 0) 4638c2ecf20Sopenharmony_ci return ret_val; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci curr->is_bound = false; 4668c2ecf20Sopenharmony_ci curr->pg_start = 0; 4678c2ecf20Sopenharmony_ci spin_lock(&curr->bridge->mapped_lock); 4688c2ecf20Sopenharmony_ci list_del(&curr->mapped_list); 4698c2ecf20Sopenharmony_ci spin_unlock(&curr->bridge->mapped_lock); 4708c2ecf20Sopenharmony_ci return 0; 4718c2ecf20Sopenharmony_ci} 4728c2ecf20Sopenharmony_ciEXPORT_SYMBOL(agp_unbind_memory); 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci/* End - Routines for handling swapping of agp_memory into the GATT */ 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci/* Generic Agp routines - Start */ 4798c2ecf20Sopenharmony_cistatic void agp_v2_parse_one(u32 *requested_mode, u32 *bridge_agpstat, u32 *vga_agpstat) 4808c2ecf20Sopenharmony_ci{ 4818c2ecf20Sopenharmony_ci u32 tmp; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci if (*requested_mode & AGP2_RESERVED_MASK) { 4848c2ecf20Sopenharmony_ci printk(KERN_INFO PFX "reserved bits set (%x) in mode 0x%x. Fixed.\n", 4858c2ecf20Sopenharmony_ci *requested_mode & AGP2_RESERVED_MASK, *requested_mode); 4868c2ecf20Sopenharmony_ci *requested_mode &= ~AGP2_RESERVED_MASK; 4878c2ecf20Sopenharmony_ci } 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci /* 4908c2ecf20Sopenharmony_ci * Some dumb bridges are programmed to disobey the AGP2 spec. 4918c2ecf20Sopenharmony_ci * This is likely a BIOS misprogramming rather than poweron default, or 4928c2ecf20Sopenharmony_ci * it would be a lot more common. 4938c2ecf20Sopenharmony_ci * https://bugs.freedesktop.org/show_bug.cgi?id=8816 4948c2ecf20Sopenharmony_ci * AGPv2 spec 6.1.9 states: 4958c2ecf20Sopenharmony_ci * The RATE field indicates the data transfer rates supported by this 4968c2ecf20Sopenharmony_ci * device. A.G.P. devices must report all that apply. 4978c2ecf20Sopenharmony_ci * Fix them up as best we can. 4988c2ecf20Sopenharmony_ci */ 4998c2ecf20Sopenharmony_ci switch (*bridge_agpstat & 7) { 5008c2ecf20Sopenharmony_ci case 4: 5018c2ecf20Sopenharmony_ci *bridge_agpstat |= (AGPSTAT2_2X | AGPSTAT2_1X); 5028c2ecf20Sopenharmony_ci printk(KERN_INFO PFX "BIOS bug. AGP bridge claims to only support x4 rate. " 5038c2ecf20Sopenharmony_ci "Fixing up support for x2 & x1\n"); 5048c2ecf20Sopenharmony_ci break; 5058c2ecf20Sopenharmony_ci case 2: 5068c2ecf20Sopenharmony_ci *bridge_agpstat |= AGPSTAT2_1X; 5078c2ecf20Sopenharmony_ci printk(KERN_INFO PFX "BIOS bug. AGP bridge claims to only support x2 rate. " 5088c2ecf20Sopenharmony_ci "Fixing up support for x1\n"); 5098c2ecf20Sopenharmony_ci break; 5108c2ecf20Sopenharmony_ci default: 5118c2ecf20Sopenharmony_ci break; 5128c2ecf20Sopenharmony_ci } 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci /* Check the speed bits make sense. Only one should be set. */ 5158c2ecf20Sopenharmony_ci tmp = *requested_mode & 7; 5168c2ecf20Sopenharmony_ci switch (tmp) { 5178c2ecf20Sopenharmony_ci case 0: 5188c2ecf20Sopenharmony_ci printk(KERN_INFO PFX "%s tried to set rate=x0. Setting to x1 mode.\n", current->comm); 5198c2ecf20Sopenharmony_ci *requested_mode |= AGPSTAT2_1X; 5208c2ecf20Sopenharmony_ci break; 5218c2ecf20Sopenharmony_ci case 1: 5228c2ecf20Sopenharmony_ci case 2: 5238c2ecf20Sopenharmony_ci break; 5248c2ecf20Sopenharmony_ci case 3: 5258c2ecf20Sopenharmony_ci *requested_mode &= ~(AGPSTAT2_1X); /* rate=2 */ 5268c2ecf20Sopenharmony_ci break; 5278c2ecf20Sopenharmony_ci case 4: 5288c2ecf20Sopenharmony_ci break; 5298c2ecf20Sopenharmony_ci case 5: 5308c2ecf20Sopenharmony_ci case 6: 5318c2ecf20Sopenharmony_ci case 7: 5328c2ecf20Sopenharmony_ci *requested_mode &= ~(AGPSTAT2_1X|AGPSTAT2_2X); /* rate=4*/ 5338c2ecf20Sopenharmony_ci break; 5348c2ecf20Sopenharmony_ci } 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci /* disable SBA if it's not supported */ 5378c2ecf20Sopenharmony_ci if (!((*bridge_agpstat & AGPSTAT_SBA) && (*vga_agpstat & AGPSTAT_SBA) && (*requested_mode & AGPSTAT_SBA))) 5388c2ecf20Sopenharmony_ci *bridge_agpstat &= ~AGPSTAT_SBA; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci /* Set rate */ 5418c2ecf20Sopenharmony_ci if (!((*bridge_agpstat & AGPSTAT2_4X) && (*vga_agpstat & AGPSTAT2_4X) && (*requested_mode & AGPSTAT2_4X))) 5428c2ecf20Sopenharmony_ci *bridge_agpstat &= ~AGPSTAT2_4X; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci if (!((*bridge_agpstat & AGPSTAT2_2X) && (*vga_agpstat & AGPSTAT2_2X) && (*requested_mode & AGPSTAT2_2X))) 5458c2ecf20Sopenharmony_ci *bridge_agpstat &= ~AGPSTAT2_2X; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci if (!((*bridge_agpstat & AGPSTAT2_1X) && (*vga_agpstat & AGPSTAT2_1X) && (*requested_mode & AGPSTAT2_1X))) 5488c2ecf20Sopenharmony_ci *bridge_agpstat &= ~AGPSTAT2_1X; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci /* Now we know what mode it should be, clear out the unwanted bits. */ 5518c2ecf20Sopenharmony_ci if (*bridge_agpstat & AGPSTAT2_4X) 5528c2ecf20Sopenharmony_ci *bridge_agpstat &= ~(AGPSTAT2_1X | AGPSTAT2_2X); /* 4X */ 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci if (*bridge_agpstat & AGPSTAT2_2X) 5558c2ecf20Sopenharmony_ci *bridge_agpstat &= ~(AGPSTAT2_1X | AGPSTAT2_4X); /* 2X */ 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci if (*bridge_agpstat & AGPSTAT2_1X) 5588c2ecf20Sopenharmony_ci *bridge_agpstat &= ~(AGPSTAT2_2X | AGPSTAT2_4X); /* 1X */ 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci /* Apply any errata. */ 5618c2ecf20Sopenharmony_ci if (agp_bridge->flags & AGP_ERRATA_FASTWRITES) 5628c2ecf20Sopenharmony_ci *bridge_agpstat &= ~AGPSTAT_FW; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci if (agp_bridge->flags & AGP_ERRATA_SBA) 5658c2ecf20Sopenharmony_ci *bridge_agpstat &= ~AGPSTAT_SBA; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci if (agp_bridge->flags & AGP_ERRATA_1X) { 5688c2ecf20Sopenharmony_ci *bridge_agpstat &= ~(AGPSTAT2_2X | AGPSTAT2_4X); 5698c2ecf20Sopenharmony_ci *bridge_agpstat |= AGPSTAT2_1X; 5708c2ecf20Sopenharmony_ci } 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci /* If we've dropped down to 1X, disable fast writes. */ 5738c2ecf20Sopenharmony_ci if (*bridge_agpstat & AGPSTAT2_1X) 5748c2ecf20Sopenharmony_ci *bridge_agpstat &= ~AGPSTAT_FW; 5758c2ecf20Sopenharmony_ci} 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci/* 5788c2ecf20Sopenharmony_ci * requested_mode = Mode requested by (typically) X. 5798c2ecf20Sopenharmony_ci * bridge_agpstat = PCI_AGP_STATUS from agp bridge. 5808c2ecf20Sopenharmony_ci * vga_agpstat = PCI_AGP_STATUS from graphic card. 5818c2ecf20Sopenharmony_ci */ 5828c2ecf20Sopenharmony_cistatic void agp_v3_parse_one(u32 *requested_mode, u32 *bridge_agpstat, u32 *vga_agpstat) 5838c2ecf20Sopenharmony_ci{ 5848c2ecf20Sopenharmony_ci u32 origbridge=*bridge_agpstat, origvga=*vga_agpstat; 5858c2ecf20Sopenharmony_ci u32 tmp; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci if (*requested_mode & AGP3_RESERVED_MASK) { 5888c2ecf20Sopenharmony_ci printk(KERN_INFO PFX "reserved bits set (%x) in mode 0x%x. Fixed.\n", 5898c2ecf20Sopenharmony_ci *requested_mode & AGP3_RESERVED_MASK, *requested_mode); 5908c2ecf20Sopenharmony_ci *requested_mode &= ~AGP3_RESERVED_MASK; 5918c2ecf20Sopenharmony_ci } 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci /* Check the speed bits make sense. */ 5948c2ecf20Sopenharmony_ci tmp = *requested_mode & 7; 5958c2ecf20Sopenharmony_ci if (tmp == 0) { 5968c2ecf20Sopenharmony_ci printk(KERN_INFO PFX "%s tried to set rate=x0. Setting to AGP3 x4 mode.\n", current->comm); 5978c2ecf20Sopenharmony_ci *requested_mode |= AGPSTAT3_4X; 5988c2ecf20Sopenharmony_ci } 5998c2ecf20Sopenharmony_ci if (tmp >= 3) { 6008c2ecf20Sopenharmony_ci printk(KERN_INFO PFX "%s tried to set rate=x%d. Setting to AGP3 x8 mode.\n", current->comm, tmp * 4); 6018c2ecf20Sopenharmony_ci *requested_mode = (*requested_mode & ~7) | AGPSTAT3_8X; 6028c2ecf20Sopenharmony_ci } 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci /* ARQSZ - Set the value to the maximum one. 6058c2ecf20Sopenharmony_ci * Don't allow the mode register to override values. */ 6068c2ecf20Sopenharmony_ci *bridge_agpstat = ((*bridge_agpstat & ~AGPSTAT_ARQSZ) | 6078c2ecf20Sopenharmony_ci max_t(u32,(*bridge_agpstat & AGPSTAT_ARQSZ),(*vga_agpstat & AGPSTAT_ARQSZ))); 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci /* Calibration cycle. 6108c2ecf20Sopenharmony_ci * Don't allow the mode register to override values. */ 6118c2ecf20Sopenharmony_ci *bridge_agpstat = ((*bridge_agpstat & ~AGPSTAT_CAL_MASK) | 6128c2ecf20Sopenharmony_ci min_t(u32,(*bridge_agpstat & AGPSTAT_CAL_MASK),(*vga_agpstat & AGPSTAT_CAL_MASK))); 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci /* SBA *must* be supported for AGP v3 */ 6158c2ecf20Sopenharmony_ci *bridge_agpstat |= AGPSTAT_SBA; 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci /* 6188c2ecf20Sopenharmony_ci * Set speed. 6198c2ecf20Sopenharmony_ci * Check for invalid speeds. This can happen when applications 6208c2ecf20Sopenharmony_ci * written before the AGP 3.0 standard pass AGP2.x modes to AGP3 hardware 6218c2ecf20Sopenharmony_ci */ 6228c2ecf20Sopenharmony_ci if (*requested_mode & AGPSTAT_MODE_3_0) { 6238c2ecf20Sopenharmony_ci /* 6248c2ecf20Sopenharmony_ci * Caller hasn't a clue what it is doing. Bridge is in 3.0 mode, 6258c2ecf20Sopenharmony_ci * have been passed a 3.0 mode, but with 2.x speed bits set. 6268c2ecf20Sopenharmony_ci * AGP2.x 4x -> AGP3.0 4x. 6278c2ecf20Sopenharmony_ci */ 6288c2ecf20Sopenharmony_ci if (*requested_mode & AGPSTAT2_4X) { 6298c2ecf20Sopenharmony_ci printk(KERN_INFO PFX "%s passes broken AGP3 flags (%x). Fixed.\n", 6308c2ecf20Sopenharmony_ci current->comm, *requested_mode); 6318c2ecf20Sopenharmony_ci *requested_mode &= ~AGPSTAT2_4X; 6328c2ecf20Sopenharmony_ci *requested_mode |= AGPSTAT3_4X; 6338c2ecf20Sopenharmony_ci } 6348c2ecf20Sopenharmony_ci } else { 6358c2ecf20Sopenharmony_ci /* 6368c2ecf20Sopenharmony_ci * The caller doesn't know what they are doing. We are in 3.0 mode, 6378c2ecf20Sopenharmony_ci * but have been passed an AGP 2.x mode. 6388c2ecf20Sopenharmony_ci * Convert AGP 1x,2x,4x -> AGP 3.0 4x. 6398c2ecf20Sopenharmony_ci */ 6408c2ecf20Sopenharmony_ci printk(KERN_INFO PFX "%s passes broken AGP2 flags (%x) in AGP3 mode. Fixed.\n", 6418c2ecf20Sopenharmony_ci current->comm, *requested_mode); 6428c2ecf20Sopenharmony_ci *requested_mode &= ~(AGPSTAT2_4X | AGPSTAT2_2X | AGPSTAT2_1X); 6438c2ecf20Sopenharmony_ci *requested_mode |= AGPSTAT3_4X; 6448c2ecf20Sopenharmony_ci } 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci if (*requested_mode & AGPSTAT3_8X) { 6478c2ecf20Sopenharmony_ci if (!(*bridge_agpstat & AGPSTAT3_8X)) { 6488c2ecf20Sopenharmony_ci *bridge_agpstat &= ~(AGPSTAT3_8X | AGPSTAT3_RSVD); 6498c2ecf20Sopenharmony_ci *bridge_agpstat |= AGPSTAT3_4X; 6508c2ecf20Sopenharmony_ci printk(KERN_INFO PFX "%s requested AGPx8 but bridge not capable.\n", current->comm); 6518c2ecf20Sopenharmony_ci return; 6528c2ecf20Sopenharmony_ci } 6538c2ecf20Sopenharmony_ci if (!(*vga_agpstat & AGPSTAT3_8X)) { 6548c2ecf20Sopenharmony_ci *bridge_agpstat &= ~(AGPSTAT3_8X | AGPSTAT3_RSVD); 6558c2ecf20Sopenharmony_ci *bridge_agpstat |= AGPSTAT3_4X; 6568c2ecf20Sopenharmony_ci printk(KERN_INFO PFX "%s requested AGPx8 but graphic card not capable.\n", current->comm); 6578c2ecf20Sopenharmony_ci return; 6588c2ecf20Sopenharmony_ci } 6598c2ecf20Sopenharmony_ci /* All set, bridge & device can do AGP x8*/ 6608c2ecf20Sopenharmony_ci *bridge_agpstat &= ~(AGPSTAT3_4X | AGPSTAT3_RSVD); 6618c2ecf20Sopenharmony_ci goto done; 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci } else if (*requested_mode & AGPSTAT3_4X) { 6648c2ecf20Sopenharmony_ci *bridge_agpstat &= ~(AGPSTAT3_8X | AGPSTAT3_RSVD); 6658c2ecf20Sopenharmony_ci *bridge_agpstat |= AGPSTAT3_4X; 6668c2ecf20Sopenharmony_ci goto done; 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci } else { 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci /* 6718c2ecf20Sopenharmony_ci * If we didn't specify an AGP mode, we see if both 6728c2ecf20Sopenharmony_ci * the graphics card, and the bridge can do x8, and use if so. 6738c2ecf20Sopenharmony_ci * If not, we fall back to x4 mode. 6748c2ecf20Sopenharmony_ci */ 6758c2ecf20Sopenharmony_ci if ((*bridge_agpstat & AGPSTAT3_8X) && (*vga_agpstat & AGPSTAT3_8X)) { 6768c2ecf20Sopenharmony_ci printk(KERN_INFO PFX "No AGP mode specified. Setting to highest mode " 6778c2ecf20Sopenharmony_ci "supported by bridge & card (x8).\n"); 6788c2ecf20Sopenharmony_ci *bridge_agpstat &= ~(AGPSTAT3_4X | AGPSTAT3_RSVD); 6798c2ecf20Sopenharmony_ci *vga_agpstat &= ~(AGPSTAT3_4X | AGPSTAT3_RSVD); 6808c2ecf20Sopenharmony_ci } else { 6818c2ecf20Sopenharmony_ci printk(KERN_INFO PFX "Fell back to AGPx4 mode because "); 6828c2ecf20Sopenharmony_ci if (!(*bridge_agpstat & AGPSTAT3_8X)) { 6838c2ecf20Sopenharmony_ci printk(KERN_INFO PFX "bridge couldn't do x8. bridge_agpstat:%x (orig=%x)\n", 6848c2ecf20Sopenharmony_ci *bridge_agpstat, origbridge); 6858c2ecf20Sopenharmony_ci *bridge_agpstat &= ~(AGPSTAT3_8X | AGPSTAT3_RSVD); 6868c2ecf20Sopenharmony_ci *bridge_agpstat |= AGPSTAT3_4X; 6878c2ecf20Sopenharmony_ci } 6888c2ecf20Sopenharmony_ci if (!(*vga_agpstat & AGPSTAT3_8X)) { 6898c2ecf20Sopenharmony_ci printk(KERN_INFO PFX "graphics card couldn't do x8. vga_agpstat:%x (orig=%x)\n", 6908c2ecf20Sopenharmony_ci *vga_agpstat, origvga); 6918c2ecf20Sopenharmony_ci *vga_agpstat &= ~(AGPSTAT3_8X | AGPSTAT3_RSVD); 6928c2ecf20Sopenharmony_ci *vga_agpstat |= AGPSTAT3_4X; 6938c2ecf20Sopenharmony_ci } 6948c2ecf20Sopenharmony_ci } 6958c2ecf20Sopenharmony_ci } 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_cidone: 6988c2ecf20Sopenharmony_ci /* Apply any errata. */ 6998c2ecf20Sopenharmony_ci if (agp_bridge->flags & AGP_ERRATA_FASTWRITES) 7008c2ecf20Sopenharmony_ci *bridge_agpstat &= ~AGPSTAT_FW; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci if (agp_bridge->flags & AGP_ERRATA_SBA) 7038c2ecf20Sopenharmony_ci *bridge_agpstat &= ~AGPSTAT_SBA; 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci if (agp_bridge->flags & AGP_ERRATA_1X) { 7068c2ecf20Sopenharmony_ci *bridge_agpstat &= ~(AGPSTAT2_2X | AGPSTAT2_4X); 7078c2ecf20Sopenharmony_ci *bridge_agpstat |= AGPSTAT2_1X; 7088c2ecf20Sopenharmony_ci } 7098c2ecf20Sopenharmony_ci} 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci/** 7138c2ecf20Sopenharmony_ci * agp_collect_device_status - determine correct agp_cmd from various agp_stat's 7148c2ecf20Sopenharmony_ci * @bridge: an agp_bridge_data struct allocated for the AGP host bridge. 7158c2ecf20Sopenharmony_ci * @requested_mode: requested agp_stat from userspace (Typically from X) 7168c2ecf20Sopenharmony_ci * @bridge_agpstat: current agp_stat from AGP bridge. 7178c2ecf20Sopenharmony_ci * 7188c2ecf20Sopenharmony_ci * This function will hunt for an AGP graphics card, and try to match 7198c2ecf20Sopenharmony_ci * the requested mode to the capabilities of both the bridge and the card. 7208c2ecf20Sopenharmony_ci */ 7218c2ecf20Sopenharmony_ciu32 agp_collect_device_status(struct agp_bridge_data *bridge, u32 requested_mode, u32 bridge_agpstat) 7228c2ecf20Sopenharmony_ci{ 7238c2ecf20Sopenharmony_ci struct pci_dev *device = NULL; 7248c2ecf20Sopenharmony_ci u32 vga_agpstat; 7258c2ecf20Sopenharmony_ci u8 cap_ptr; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci for (;;) { 7288c2ecf20Sopenharmony_ci device = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, device); 7298c2ecf20Sopenharmony_ci if (!device) { 7308c2ecf20Sopenharmony_ci printk(KERN_INFO PFX "Couldn't find an AGP VGA controller.\n"); 7318c2ecf20Sopenharmony_ci return 0; 7328c2ecf20Sopenharmony_ci } 7338c2ecf20Sopenharmony_ci cap_ptr = pci_find_capability(device, PCI_CAP_ID_AGP); 7348c2ecf20Sopenharmony_ci if (cap_ptr) 7358c2ecf20Sopenharmony_ci break; 7368c2ecf20Sopenharmony_ci } 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci /* 7398c2ecf20Sopenharmony_ci * Ok, here we have a AGP device. Disable impossible 7408c2ecf20Sopenharmony_ci * settings, and adjust the readqueue to the minimum. 7418c2ecf20Sopenharmony_ci */ 7428c2ecf20Sopenharmony_ci pci_read_config_dword(device, cap_ptr+PCI_AGP_STATUS, &vga_agpstat); 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci /* adjust RQ depth */ 7458c2ecf20Sopenharmony_ci bridge_agpstat = ((bridge_agpstat & ~AGPSTAT_RQ_DEPTH) | 7468c2ecf20Sopenharmony_ci min_t(u32, (requested_mode & AGPSTAT_RQ_DEPTH), 7478c2ecf20Sopenharmony_ci min_t(u32, (bridge_agpstat & AGPSTAT_RQ_DEPTH), (vga_agpstat & AGPSTAT_RQ_DEPTH)))); 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci /* disable FW if it's not supported */ 7508c2ecf20Sopenharmony_ci if (!((bridge_agpstat & AGPSTAT_FW) && 7518c2ecf20Sopenharmony_ci (vga_agpstat & AGPSTAT_FW) && 7528c2ecf20Sopenharmony_ci (requested_mode & AGPSTAT_FW))) 7538c2ecf20Sopenharmony_ci bridge_agpstat &= ~AGPSTAT_FW; 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci /* Check to see if we are operating in 3.0 mode */ 7568c2ecf20Sopenharmony_ci if (agp_bridge->mode & AGPSTAT_MODE_3_0) 7578c2ecf20Sopenharmony_ci agp_v3_parse_one(&requested_mode, &bridge_agpstat, &vga_agpstat); 7588c2ecf20Sopenharmony_ci else 7598c2ecf20Sopenharmony_ci agp_v2_parse_one(&requested_mode, &bridge_agpstat, &vga_agpstat); 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci pci_dev_put(device); 7628c2ecf20Sopenharmony_ci return bridge_agpstat; 7638c2ecf20Sopenharmony_ci} 7648c2ecf20Sopenharmony_ciEXPORT_SYMBOL(agp_collect_device_status); 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_civoid agp_device_command(u32 bridge_agpstat, bool agp_v3) 7688c2ecf20Sopenharmony_ci{ 7698c2ecf20Sopenharmony_ci struct pci_dev *device = NULL; 7708c2ecf20Sopenharmony_ci int mode; 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci mode = bridge_agpstat & 0x7; 7738c2ecf20Sopenharmony_ci if (agp_v3) 7748c2ecf20Sopenharmony_ci mode *= 4; 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci for_each_pci_dev(device) { 7778c2ecf20Sopenharmony_ci u8 agp = pci_find_capability(device, PCI_CAP_ID_AGP); 7788c2ecf20Sopenharmony_ci if (!agp) 7798c2ecf20Sopenharmony_ci continue; 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci dev_info(&device->dev, "putting AGP V%d device into %dx mode\n", 7828c2ecf20Sopenharmony_ci agp_v3 ? 3 : 2, mode); 7838c2ecf20Sopenharmony_ci pci_write_config_dword(device, agp + PCI_AGP_COMMAND, bridge_agpstat); 7848c2ecf20Sopenharmony_ci } 7858c2ecf20Sopenharmony_ci} 7868c2ecf20Sopenharmony_ciEXPORT_SYMBOL(agp_device_command); 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_civoid get_agp_version(struct agp_bridge_data *bridge) 7908c2ecf20Sopenharmony_ci{ 7918c2ecf20Sopenharmony_ci u32 ncapid; 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci /* Exit early if already set by errata workarounds. */ 7948c2ecf20Sopenharmony_ci if (bridge->major_version != 0) 7958c2ecf20Sopenharmony_ci return; 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci pci_read_config_dword(bridge->dev, bridge->capndx, &ncapid); 7988c2ecf20Sopenharmony_ci bridge->major_version = (ncapid >> AGP_MAJOR_VERSION_SHIFT) & 0xf; 7998c2ecf20Sopenharmony_ci bridge->minor_version = (ncapid >> AGP_MINOR_VERSION_SHIFT) & 0xf; 8008c2ecf20Sopenharmony_ci} 8018c2ecf20Sopenharmony_ciEXPORT_SYMBOL(get_agp_version); 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_civoid agp_generic_enable(struct agp_bridge_data *bridge, u32 requested_mode) 8058c2ecf20Sopenharmony_ci{ 8068c2ecf20Sopenharmony_ci u32 bridge_agpstat, temp; 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci get_agp_version(agp_bridge); 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci dev_info(&agp_bridge->dev->dev, "AGP %d.%d bridge\n", 8118c2ecf20Sopenharmony_ci agp_bridge->major_version, agp_bridge->minor_version); 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci pci_read_config_dword(agp_bridge->dev, 8148c2ecf20Sopenharmony_ci agp_bridge->capndx + PCI_AGP_STATUS, &bridge_agpstat); 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci bridge_agpstat = agp_collect_device_status(agp_bridge, requested_mode, bridge_agpstat); 8178c2ecf20Sopenharmony_ci if (bridge_agpstat == 0) 8188c2ecf20Sopenharmony_ci /* Something bad happened. FIXME: Return error code? */ 8198c2ecf20Sopenharmony_ci return; 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci bridge_agpstat |= AGPSTAT_AGP_ENABLE; 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci /* Do AGP version specific frobbing. */ 8248c2ecf20Sopenharmony_ci if (bridge->major_version >= 3) { 8258c2ecf20Sopenharmony_ci if (bridge->mode & AGPSTAT_MODE_3_0) { 8268c2ecf20Sopenharmony_ci /* If we have 3.5, we can do the isoch stuff. */ 8278c2ecf20Sopenharmony_ci if (bridge->minor_version >= 5) 8288c2ecf20Sopenharmony_ci agp_3_5_enable(bridge); 8298c2ecf20Sopenharmony_ci agp_device_command(bridge_agpstat, true); 8308c2ecf20Sopenharmony_ci return; 8318c2ecf20Sopenharmony_ci } else { 8328c2ecf20Sopenharmony_ci /* Disable calibration cycle in RX91<1> when not in AGP3.0 mode of operation.*/ 8338c2ecf20Sopenharmony_ci bridge_agpstat &= ~(7<<10) ; 8348c2ecf20Sopenharmony_ci pci_read_config_dword(bridge->dev, 8358c2ecf20Sopenharmony_ci bridge->capndx+AGPCTRL, &temp); 8368c2ecf20Sopenharmony_ci temp |= (1<<9); 8378c2ecf20Sopenharmony_ci pci_write_config_dword(bridge->dev, 8388c2ecf20Sopenharmony_ci bridge->capndx+AGPCTRL, temp); 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci dev_info(&bridge->dev->dev, "bridge is in legacy mode, falling back to 2.x\n"); 8418c2ecf20Sopenharmony_ci } 8428c2ecf20Sopenharmony_ci } 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci /* AGP v<3 */ 8458c2ecf20Sopenharmony_ci agp_device_command(bridge_agpstat, false); 8468c2ecf20Sopenharmony_ci} 8478c2ecf20Sopenharmony_ciEXPORT_SYMBOL(agp_generic_enable); 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ciint agp_generic_create_gatt_table(struct agp_bridge_data *bridge) 8518c2ecf20Sopenharmony_ci{ 8528c2ecf20Sopenharmony_ci char *table; 8538c2ecf20Sopenharmony_ci char *table_end; 8548c2ecf20Sopenharmony_ci int page_order; 8558c2ecf20Sopenharmony_ci int num_entries; 8568c2ecf20Sopenharmony_ci int i; 8578c2ecf20Sopenharmony_ci void *temp; 8588c2ecf20Sopenharmony_ci struct page *page; 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci /* The generic routines can't handle 2 level gatt's */ 8618c2ecf20Sopenharmony_ci if (bridge->driver->size_type == LVL2_APER_SIZE) 8628c2ecf20Sopenharmony_ci return -EINVAL; 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci table = NULL; 8658c2ecf20Sopenharmony_ci i = bridge->aperture_size_idx; 8668c2ecf20Sopenharmony_ci temp = bridge->current_size; 8678c2ecf20Sopenharmony_ci page_order = num_entries = 0; 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci if (bridge->driver->size_type != FIXED_APER_SIZE) { 8708c2ecf20Sopenharmony_ci do { 8718c2ecf20Sopenharmony_ci switch (bridge->driver->size_type) { 8728c2ecf20Sopenharmony_ci case U8_APER_SIZE: 8738c2ecf20Sopenharmony_ci page_order = 8748c2ecf20Sopenharmony_ci A_SIZE_8(temp)->page_order; 8758c2ecf20Sopenharmony_ci num_entries = 8768c2ecf20Sopenharmony_ci A_SIZE_8(temp)->num_entries; 8778c2ecf20Sopenharmony_ci break; 8788c2ecf20Sopenharmony_ci case U16_APER_SIZE: 8798c2ecf20Sopenharmony_ci page_order = A_SIZE_16(temp)->page_order; 8808c2ecf20Sopenharmony_ci num_entries = A_SIZE_16(temp)->num_entries; 8818c2ecf20Sopenharmony_ci break; 8828c2ecf20Sopenharmony_ci case U32_APER_SIZE: 8838c2ecf20Sopenharmony_ci page_order = A_SIZE_32(temp)->page_order; 8848c2ecf20Sopenharmony_ci num_entries = A_SIZE_32(temp)->num_entries; 8858c2ecf20Sopenharmony_ci break; 8868c2ecf20Sopenharmony_ci /* This case will never really happen. */ 8878c2ecf20Sopenharmony_ci case FIXED_APER_SIZE: 8888c2ecf20Sopenharmony_ci case LVL2_APER_SIZE: 8898c2ecf20Sopenharmony_ci default: 8908c2ecf20Sopenharmony_ci page_order = num_entries = 0; 8918c2ecf20Sopenharmony_ci break; 8928c2ecf20Sopenharmony_ci } 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci table = alloc_gatt_pages(page_order); 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci if (table == NULL) { 8978c2ecf20Sopenharmony_ci i++; 8988c2ecf20Sopenharmony_ci switch (bridge->driver->size_type) { 8998c2ecf20Sopenharmony_ci case U8_APER_SIZE: 9008c2ecf20Sopenharmony_ci bridge->current_size = A_IDX8(bridge); 9018c2ecf20Sopenharmony_ci break; 9028c2ecf20Sopenharmony_ci case U16_APER_SIZE: 9038c2ecf20Sopenharmony_ci bridge->current_size = A_IDX16(bridge); 9048c2ecf20Sopenharmony_ci break; 9058c2ecf20Sopenharmony_ci case U32_APER_SIZE: 9068c2ecf20Sopenharmony_ci bridge->current_size = A_IDX32(bridge); 9078c2ecf20Sopenharmony_ci break; 9088c2ecf20Sopenharmony_ci /* These cases will never really happen. */ 9098c2ecf20Sopenharmony_ci case FIXED_APER_SIZE: 9108c2ecf20Sopenharmony_ci case LVL2_APER_SIZE: 9118c2ecf20Sopenharmony_ci default: 9128c2ecf20Sopenharmony_ci break; 9138c2ecf20Sopenharmony_ci } 9148c2ecf20Sopenharmony_ci temp = bridge->current_size; 9158c2ecf20Sopenharmony_ci } else { 9168c2ecf20Sopenharmony_ci bridge->aperture_size_idx = i; 9178c2ecf20Sopenharmony_ci } 9188c2ecf20Sopenharmony_ci } while (!table && (i < bridge->driver->num_aperture_sizes)); 9198c2ecf20Sopenharmony_ci } else { 9208c2ecf20Sopenharmony_ci page_order = ((struct aper_size_info_fixed *) temp)->page_order; 9218c2ecf20Sopenharmony_ci num_entries = ((struct aper_size_info_fixed *) temp)->num_entries; 9228c2ecf20Sopenharmony_ci table = alloc_gatt_pages(page_order); 9238c2ecf20Sopenharmony_ci } 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci if (table == NULL) 9268c2ecf20Sopenharmony_ci return -ENOMEM; 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1); 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci for (page = virt_to_page(table); page <= virt_to_page(table_end); page++) 9318c2ecf20Sopenharmony_ci SetPageReserved(page); 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci bridge->gatt_table_real = (u32 *) table; 9348c2ecf20Sopenharmony_ci agp_gatt_table = (void *)table; 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci bridge->driver->cache_flush(); 9378c2ecf20Sopenharmony_ci#ifdef CONFIG_X86 9388c2ecf20Sopenharmony_ci if (set_memory_uc((unsigned long)table, 1 << page_order)) 9398c2ecf20Sopenharmony_ci printk(KERN_WARNING "Could not set GATT table memory to UC!\n"); 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci bridge->gatt_table = (u32 __iomem *)table; 9428c2ecf20Sopenharmony_ci#else 9438c2ecf20Sopenharmony_ci bridge->gatt_table = ioremap(virt_to_phys(table), 9448c2ecf20Sopenharmony_ci (PAGE_SIZE * (1 << page_order))); 9458c2ecf20Sopenharmony_ci bridge->driver->cache_flush(); 9468c2ecf20Sopenharmony_ci#endif 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci if (bridge->gatt_table == NULL) { 9498c2ecf20Sopenharmony_ci for (page = virt_to_page(table); page <= virt_to_page(table_end); page++) 9508c2ecf20Sopenharmony_ci ClearPageReserved(page); 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci free_gatt_pages(table, page_order); 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci return -ENOMEM; 9558c2ecf20Sopenharmony_ci } 9568c2ecf20Sopenharmony_ci bridge->gatt_bus_addr = virt_to_phys(bridge->gatt_table_real); 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci /* AK: bogus, should encode addresses > 4GB */ 9598c2ecf20Sopenharmony_ci for (i = 0; i < num_entries; i++) { 9608c2ecf20Sopenharmony_ci writel(bridge->scratch_page, bridge->gatt_table+i); 9618c2ecf20Sopenharmony_ci readl(bridge->gatt_table+i); /* PCI Posting. */ 9628c2ecf20Sopenharmony_ci } 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci return 0; 9658c2ecf20Sopenharmony_ci} 9668c2ecf20Sopenharmony_ciEXPORT_SYMBOL(agp_generic_create_gatt_table); 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ciint agp_generic_free_gatt_table(struct agp_bridge_data *bridge) 9698c2ecf20Sopenharmony_ci{ 9708c2ecf20Sopenharmony_ci int page_order; 9718c2ecf20Sopenharmony_ci char *table, *table_end; 9728c2ecf20Sopenharmony_ci void *temp; 9738c2ecf20Sopenharmony_ci struct page *page; 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci temp = bridge->current_size; 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci switch (bridge->driver->size_type) { 9788c2ecf20Sopenharmony_ci case U8_APER_SIZE: 9798c2ecf20Sopenharmony_ci page_order = A_SIZE_8(temp)->page_order; 9808c2ecf20Sopenharmony_ci break; 9818c2ecf20Sopenharmony_ci case U16_APER_SIZE: 9828c2ecf20Sopenharmony_ci page_order = A_SIZE_16(temp)->page_order; 9838c2ecf20Sopenharmony_ci break; 9848c2ecf20Sopenharmony_ci case U32_APER_SIZE: 9858c2ecf20Sopenharmony_ci page_order = A_SIZE_32(temp)->page_order; 9868c2ecf20Sopenharmony_ci break; 9878c2ecf20Sopenharmony_ci case FIXED_APER_SIZE: 9888c2ecf20Sopenharmony_ci page_order = A_SIZE_FIX(temp)->page_order; 9898c2ecf20Sopenharmony_ci break; 9908c2ecf20Sopenharmony_ci case LVL2_APER_SIZE: 9918c2ecf20Sopenharmony_ci /* The generic routines can't deal with 2 level gatt's */ 9928c2ecf20Sopenharmony_ci return -EINVAL; 9938c2ecf20Sopenharmony_ci default: 9948c2ecf20Sopenharmony_ci page_order = 0; 9958c2ecf20Sopenharmony_ci break; 9968c2ecf20Sopenharmony_ci } 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci /* Do not worry about freeing memory, because if this is 9998c2ecf20Sopenharmony_ci * called, then all agp memory is deallocated and removed 10008c2ecf20Sopenharmony_ci * from the table. */ 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci#ifdef CONFIG_X86 10038c2ecf20Sopenharmony_ci set_memory_wb((unsigned long)bridge->gatt_table, 1 << page_order); 10048c2ecf20Sopenharmony_ci#else 10058c2ecf20Sopenharmony_ci iounmap(bridge->gatt_table); 10068c2ecf20Sopenharmony_ci#endif 10078c2ecf20Sopenharmony_ci table = (char *) bridge->gatt_table_real; 10088c2ecf20Sopenharmony_ci table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1); 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci for (page = virt_to_page(table); page <= virt_to_page(table_end); page++) 10118c2ecf20Sopenharmony_ci ClearPageReserved(page); 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci free_gatt_pages(bridge->gatt_table_real, page_order); 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci agp_gatt_table = NULL; 10168c2ecf20Sopenharmony_ci bridge->gatt_table = NULL; 10178c2ecf20Sopenharmony_ci bridge->gatt_table_real = NULL; 10188c2ecf20Sopenharmony_ci bridge->gatt_bus_addr = 0; 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci return 0; 10218c2ecf20Sopenharmony_ci} 10228c2ecf20Sopenharmony_ciEXPORT_SYMBOL(agp_generic_free_gatt_table); 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ciint agp_generic_insert_memory(struct agp_memory * mem, off_t pg_start, int type) 10268c2ecf20Sopenharmony_ci{ 10278c2ecf20Sopenharmony_ci int num_entries; 10288c2ecf20Sopenharmony_ci size_t i; 10298c2ecf20Sopenharmony_ci off_t j; 10308c2ecf20Sopenharmony_ci void *temp; 10318c2ecf20Sopenharmony_ci struct agp_bridge_data *bridge; 10328c2ecf20Sopenharmony_ci int mask_type; 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci bridge = mem->bridge; 10358c2ecf20Sopenharmony_ci if (!bridge) 10368c2ecf20Sopenharmony_ci return -EINVAL; 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci if (mem->page_count == 0) 10398c2ecf20Sopenharmony_ci return 0; 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci temp = bridge->current_size; 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci switch (bridge->driver->size_type) { 10448c2ecf20Sopenharmony_ci case U8_APER_SIZE: 10458c2ecf20Sopenharmony_ci num_entries = A_SIZE_8(temp)->num_entries; 10468c2ecf20Sopenharmony_ci break; 10478c2ecf20Sopenharmony_ci case U16_APER_SIZE: 10488c2ecf20Sopenharmony_ci num_entries = A_SIZE_16(temp)->num_entries; 10498c2ecf20Sopenharmony_ci break; 10508c2ecf20Sopenharmony_ci case U32_APER_SIZE: 10518c2ecf20Sopenharmony_ci num_entries = A_SIZE_32(temp)->num_entries; 10528c2ecf20Sopenharmony_ci break; 10538c2ecf20Sopenharmony_ci case FIXED_APER_SIZE: 10548c2ecf20Sopenharmony_ci num_entries = A_SIZE_FIX(temp)->num_entries; 10558c2ecf20Sopenharmony_ci break; 10568c2ecf20Sopenharmony_ci case LVL2_APER_SIZE: 10578c2ecf20Sopenharmony_ci /* The generic routines can't deal with 2 level gatt's */ 10588c2ecf20Sopenharmony_ci return -EINVAL; 10598c2ecf20Sopenharmony_ci default: 10608c2ecf20Sopenharmony_ci num_entries = 0; 10618c2ecf20Sopenharmony_ci break; 10628c2ecf20Sopenharmony_ci } 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci num_entries -= agp_memory_reserved/PAGE_SIZE; 10658c2ecf20Sopenharmony_ci if (num_entries < 0) num_entries = 0; 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci if (type != mem->type) 10688c2ecf20Sopenharmony_ci return -EINVAL; 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci mask_type = bridge->driver->agp_type_to_mask_type(bridge, type); 10718c2ecf20Sopenharmony_ci if (mask_type != 0) { 10728c2ecf20Sopenharmony_ci /* The generic routines know nothing of memory types */ 10738c2ecf20Sopenharmony_ci return -EINVAL; 10748c2ecf20Sopenharmony_ci } 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci if (((pg_start + mem->page_count) > num_entries) || 10778c2ecf20Sopenharmony_ci ((pg_start + mem->page_count) < pg_start)) 10788c2ecf20Sopenharmony_ci return -EINVAL; 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci j = pg_start; 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci while (j < (pg_start + mem->page_count)) { 10838c2ecf20Sopenharmony_ci if (!PGE_EMPTY(bridge, readl(bridge->gatt_table+j))) 10848c2ecf20Sopenharmony_ci return -EBUSY; 10858c2ecf20Sopenharmony_ci j++; 10868c2ecf20Sopenharmony_ci } 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci if (!mem->is_flushed) { 10898c2ecf20Sopenharmony_ci bridge->driver->cache_flush(); 10908c2ecf20Sopenharmony_ci mem->is_flushed = true; 10918c2ecf20Sopenharmony_ci } 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { 10948c2ecf20Sopenharmony_ci writel(bridge->driver->mask_memory(bridge, 10958c2ecf20Sopenharmony_ci page_to_phys(mem->pages[i]), 10968c2ecf20Sopenharmony_ci mask_type), 10978c2ecf20Sopenharmony_ci bridge->gatt_table+j); 10988c2ecf20Sopenharmony_ci } 10998c2ecf20Sopenharmony_ci readl(bridge->gatt_table+j-1); /* PCI Posting. */ 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci bridge->driver->tlb_flush(mem); 11028c2ecf20Sopenharmony_ci return 0; 11038c2ecf20Sopenharmony_ci} 11048c2ecf20Sopenharmony_ciEXPORT_SYMBOL(agp_generic_insert_memory); 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ciint agp_generic_remove_memory(struct agp_memory *mem, off_t pg_start, int type) 11088c2ecf20Sopenharmony_ci{ 11098c2ecf20Sopenharmony_ci size_t i; 11108c2ecf20Sopenharmony_ci struct agp_bridge_data *bridge; 11118c2ecf20Sopenharmony_ci int mask_type, num_entries; 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci bridge = mem->bridge; 11148c2ecf20Sopenharmony_ci if (!bridge) 11158c2ecf20Sopenharmony_ci return -EINVAL; 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci if (mem->page_count == 0) 11188c2ecf20Sopenharmony_ci return 0; 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci if (type != mem->type) 11218c2ecf20Sopenharmony_ci return -EINVAL; 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci num_entries = agp_num_entries(); 11248c2ecf20Sopenharmony_ci if (((pg_start + mem->page_count) > num_entries) || 11258c2ecf20Sopenharmony_ci ((pg_start + mem->page_count) < pg_start)) 11268c2ecf20Sopenharmony_ci return -EINVAL; 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci mask_type = bridge->driver->agp_type_to_mask_type(bridge, type); 11298c2ecf20Sopenharmony_ci if (mask_type != 0) { 11308c2ecf20Sopenharmony_ci /* The generic routines know nothing of memory types */ 11318c2ecf20Sopenharmony_ci return -EINVAL; 11328c2ecf20Sopenharmony_ci } 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci /* AK: bogus, should encode addresses > 4GB */ 11358c2ecf20Sopenharmony_ci for (i = pg_start; i < (mem->page_count + pg_start); i++) { 11368c2ecf20Sopenharmony_ci writel(bridge->scratch_page, bridge->gatt_table+i); 11378c2ecf20Sopenharmony_ci } 11388c2ecf20Sopenharmony_ci readl(bridge->gatt_table+i-1); /* PCI Posting. */ 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci bridge->driver->tlb_flush(mem); 11418c2ecf20Sopenharmony_ci return 0; 11428c2ecf20Sopenharmony_ci} 11438c2ecf20Sopenharmony_ciEXPORT_SYMBOL(agp_generic_remove_memory); 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_cistruct agp_memory *agp_generic_alloc_by_type(size_t page_count, int type) 11468c2ecf20Sopenharmony_ci{ 11478c2ecf20Sopenharmony_ci return NULL; 11488c2ecf20Sopenharmony_ci} 11498c2ecf20Sopenharmony_ciEXPORT_SYMBOL(agp_generic_alloc_by_type); 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_civoid agp_generic_free_by_type(struct agp_memory *curr) 11528c2ecf20Sopenharmony_ci{ 11538c2ecf20Sopenharmony_ci agp_free_page_array(curr); 11548c2ecf20Sopenharmony_ci agp_free_key(curr->key); 11558c2ecf20Sopenharmony_ci kfree(curr); 11568c2ecf20Sopenharmony_ci} 11578c2ecf20Sopenharmony_ciEXPORT_SYMBOL(agp_generic_free_by_type); 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_cistruct agp_memory *agp_generic_alloc_user(size_t page_count, int type) 11608c2ecf20Sopenharmony_ci{ 11618c2ecf20Sopenharmony_ci struct agp_memory *new; 11628c2ecf20Sopenharmony_ci int i; 11638c2ecf20Sopenharmony_ci int pages; 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci pages = (page_count + ENTRIES_PER_PAGE - 1) / ENTRIES_PER_PAGE; 11668c2ecf20Sopenharmony_ci new = agp_create_user_memory(page_count); 11678c2ecf20Sopenharmony_ci if (new == NULL) 11688c2ecf20Sopenharmony_ci return NULL; 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci for (i = 0; i < page_count; i++) 11718c2ecf20Sopenharmony_ci new->pages[i] = NULL; 11728c2ecf20Sopenharmony_ci new->page_count = 0; 11738c2ecf20Sopenharmony_ci new->type = type; 11748c2ecf20Sopenharmony_ci new->num_scratch_pages = pages; 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci return new; 11778c2ecf20Sopenharmony_ci} 11788c2ecf20Sopenharmony_ciEXPORT_SYMBOL(agp_generic_alloc_user); 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci/* 11818c2ecf20Sopenharmony_ci * Basic Page Allocation Routines - 11828c2ecf20Sopenharmony_ci * These routines handle page allocation and by default they reserve the allocated 11838c2ecf20Sopenharmony_ci * memory. They also handle incrementing the current_memory_agp value, Which is checked 11848c2ecf20Sopenharmony_ci * against a maximum value. 11858c2ecf20Sopenharmony_ci */ 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ciint agp_generic_alloc_pages(struct agp_bridge_data *bridge, struct agp_memory *mem, size_t num_pages) 11888c2ecf20Sopenharmony_ci{ 11898c2ecf20Sopenharmony_ci struct page * page; 11908c2ecf20Sopenharmony_ci int i, ret = -ENOMEM; 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci for (i = 0; i < num_pages; i++) { 11938c2ecf20Sopenharmony_ci page = alloc_page(GFP_KERNEL | GFP_DMA32 | __GFP_ZERO); 11948c2ecf20Sopenharmony_ci /* agp_free_memory() needs gart address */ 11958c2ecf20Sopenharmony_ci if (page == NULL) 11968c2ecf20Sopenharmony_ci goto out; 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci#ifndef CONFIG_X86 11998c2ecf20Sopenharmony_ci map_page_into_agp(page); 12008c2ecf20Sopenharmony_ci#endif 12018c2ecf20Sopenharmony_ci get_page(page); 12028c2ecf20Sopenharmony_ci atomic_inc(&agp_bridge->current_memory_agp); 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci mem->pages[i] = page; 12058c2ecf20Sopenharmony_ci mem->page_count++; 12068c2ecf20Sopenharmony_ci } 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci#ifdef CONFIG_X86 12098c2ecf20Sopenharmony_ci set_pages_array_uc(mem->pages, num_pages); 12108c2ecf20Sopenharmony_ci#endif 12118c2ecf20Sopenharmony_ci ret = 0; 12128c2ecf20Sopenharmony_ciout: 12138c2ecf20Sopenharmony_ci return ret; 12148c2ecf20Sopenharmony_ci} 12158c2ecf20Sopenharmony_ciEXPORT_SYMBOL(agp_generic_alloc_pages); 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_cistruct page *agp_generic_alloc_page(struct agp_bridge_data *bridge) 12188c2ecf20Sopenharmony_ci{ 12198c2ecf20Sopenharmony_ci struct page * page; 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci page = alloc_page(GFP_KERNEL | GFP_DMA32 | __GFP_ZERO); 12228c2ecf20Sopenharmony_ci if (page == NULL) 12238c2ecf20Sopenharmony_ci return NULL; 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci map_page_into_agp(page); 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci get_page(page); 12288c2ecf20Sopenharmony_ci atomic_inc(&agp_bridge->current_memory_agp); 12298c2ecf20Sopenharmony_ci return page; 12308c2ecf20Sopenharmony_ci} 12318c2ecf20Sopenharmony_ciEXPORT_SYMBOL(agp_generic_alloc_page); 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_civoid agp_generic_destroy_pages(struct agp_memory *mem) 12348c2ecf20Sopenharmony_ci{ 12358c2ecf20Sopenharmony_ci int i; 12368c2ecf20Sopenharmony_ci struct page *page; 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci if (!mem) 12398c2ecf20Sopenharmony_ci return; 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci#ifdef CONFIG_X86 12428c2ecf20Sopenharmony_ci set_pages_array_wb(mem->pages, mem->page_count); 12438c2ecf20Sopenharmony_ci#endif 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_ci for (i = 0; i < mem->page_count; i++) { 12468c2ecf20Sopenharmony_ci page = mem->pages[i]; 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ci#ifndef CONFIG_X86 12498c2ecf20Sopenharmony_ci unmap_page_from_agp(page); 12508c2ecf20Sopenharmony_ci#endif 12518c2ecf20Sopenharmony_ci put_page(page); 12528c2ecf20Sopenharmony_ci __free_page(page); 12538c2ecf20Sopenharmony_ci atomic_dec(&agp_bridge->current_memory_agp); 12548c2ecf20Sopenharmony_ci mem->pages[i] = NULL; 12558c2ecf20Sopenharmony_ci } 12568c2ecf20Sopenharmony_ci} 12578c2ecf20Sopenharmony_ciEXPORT_SYMBOL(agp_generic_destroy_pages); 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_civoid agp_generic_destroy_page(struct page *page, int flags) 12608c2ecf20Sopenharmony_ci{ 12618c2ecf20Sopenharmony_ci if (page == NULL) 12628c2ecf20Sopenharmony_ci return; 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci if (flags & AGP_PAGE_DESTROY_UNMAP) 12658c2ecf20Sopenharmony_ci unmap_page_from_agp(page); 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_ci if (flags & AGP_PAGE_DESTROY_FREE) { 12688c2ecf20Sopenharmony_ci put_page(page); 12698c2ecf20Sopenharmony_ci __free_page(page); 12708c2ecf20Sopenharmony_ci atomic_dec(&agp_bridge->current_memory_agp); 12718c2ecf20Sopenharmony_ci } 12728c2ecf20Sopenharmony_ci} 12738c2ecf20Sopenharmony_ciEXPORT_SYMBOL(agp_generic_destroy_page); 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci/* End Basic Page Allocation Routines */ 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_ci/** 12798c2ecf20Sopenharmony_ci * agp_enable - initialise the agp point-to-point connection. 12808c2ecf20Sopenharmony_ci * 12818c2ecf20Sopenharmony_ci * @bridge: an agp_bridge_data struct allocated for the AGP host bridge. 12828c2ecf20Sopenharmony_ci * @mode: agp mode register value to configure with. 12838c2ecf20Sopenharmony_ci */ 12848c2ecf20Sopenharmony_civoid agp_enable(struct agp_bridge_data *bridge, u32 mode) 12858c2ecf20Sopenharmony_ci{ 12868c2ecf20Sopenharmony_ci if (!bridge) 12878c2ecf20Sopenharmony_ci return; 12888c2ecf20Sopenharmony_ci bridge->driver->agp_enable(bridge, mode); 12898c2ecf20Sopenharmony_ci} 12908c2ecf20Sopenharmony_ciEXPORT_SYMBOL(agp_enable); 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_ci/* When we remove the global variable agp_bridge from all drivers 12938c2ecf20Sopenharmony_ci * then agp_alloc_bridge and agp_generic_find_bridge need to be updated 12948c2ecf20Sopenharmony_ci */ 12958c2ecf20Sopenharmony_ci 12968c2ecf20Sopenharmony_cistruct agp_bridge_data *agp_generic_find_bridge(struct pci_dev *pdev) 12978c2ecf20Sopenharmony_ci{ 12988c2ecf20Sopenharmony_ci if (list_empty(&agp_bridges)) 12998c2ecf20Sopenharmony_ci return NULL; 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_ci return agp_bridge; 13028c2ecf20Sopenharmony_ci} 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_cistatic void ipi_handler(void *null) 13058c2ecf20Sopenharmony_ci{ 13068c2ecf20Sopenharmony_ci flush_agp_cache(); 13078c2ecf20Sopenharmony_ci} 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_civoid global_cache_flush(void) 13108c2ecf20Sopenharmony_ci{ 13118c2ecf20Sopenharmony_ci on_each_cpu(ipi_handler, NULL, 1); 13128c2ecf20Sopenharmony_ci} 13138c2ecf20Sopenharmony_ciEXPORT_SYMBOL(global_cache_flush); 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ciunsigned long agp_generic_mask_memory(struct agp_bridge_data *bridge, 13168c2ecf20Sopenharmony_ci dma_addr_t addr, int type) 13178c2ecf20Sopenharmony_ci{ 13188c2ecf20Sopenharmony_ci /* memory type is ignored in the generic routine */ 13198c2ecf20Sopenharmony_ci if (bridge->driver->masks) 13208c2ecf20Sopenharmony_ci return addr | bridge->driver->masks[0].mask; 13218c2ecf20Sopenharmony_ci else 13228c2ecf20Sopenharmony_ci return addr; 13238c2ecf20Sopenharmony_ci} 13248c2ecf20Sopenharmony_ciEXPORT_SYMBOL(agp_generic_mask_memory); 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_ciint agp_generic_type_to_mask_type(struct agp_bridge_data *bridge, 13278c2ecf20Sopenharmony_ci int type) 13288c2ecf20Sopenharmony_ci{ 13298c2ecf20Sopenharmony_ci if (type >= AGP_USER_TYPES) 13308c2ecf20Sopenharmony_ci return 0; 13318c2ecf20Sopenharmony_ci return type; 13328c2ecf20Sopenharmony_ci} 13338c2ecf20Sopenharmony_ciEXPORT_SYMBOL(agp_generic_type_to_mask_type); 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_ci/* 13368c2ecf20Sopenharmony_ci * These functions are implemented according to the AGPv3 spec, 13378c2ecf20Sopenharmony_ci * which covers implementation details that had previously been 13388c2ecf20Sopenharmony_ci * left open. 13398c2ecf20Sopenharmony_ci */ 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_ciint agp3_generic_fetch_size(void) 13428c2ecf20Sopenharmony_ci{ 13438c2ecf20Sopenharmony_ci u16 temp_size; 13448c2ecf20Sopenharmony_ci int i; 13458c2ecf20Sopenharmony_ci struct aper_size_info_16 *values; 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_ci pci_read_config_word(agp_bridge->dev, agp_bridge->capndx+AGPAPSIZE, &temp_size); 13488c2ecf20Sopenharmony_ci values = A_SIZE_16(agp_bridge->driver->aperture_sizes); 13498c2ecf20Sopenharmony_ci 13508c2ecf20Sopenharmony_ci for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++) { 13518c2ecf20Sopenharmony_ci if (temp_size == values[i].size_value) { 13528c2ecf20Sopenharmony_ci agp_bridge->previous_size = 13538c2ecf20Sopenharmony_ci agp_bridge->current_size = (void *) (values + i); 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci agp_bridge->aperture_size_idx = i; 13568c2ecf20Sopenharmony_ci return values[i].size; 13578c2ecf20Sopenharmony_ci } 13588c2ecf20Sopenharmony_ci } 13598c2ecf20Sopenharmony_ci return 0; 13608c2ecf20Sopenharmony_ci} 13618c2ecf20Sopenharmony_ciEXPORT_SYMBOL(agp3_generic_fetch_size); 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_civoid agp3_generic_tlbflush(struct agp_memory *mem) 13648c2ecf20Sopenharmony_ci{ 13658c2ecf20Sopenharmony_ci u32 ctrl; 13668c2ecf20Sopenharmony_ci pci_read_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, &ctrl); 13678c2ecf20Sopenharmony_ci pci_write_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, ctrl & ~AGPCTRL_GTLBEN); 13688c2ecf20Sopenharmony_ci pci_write_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, ctrl); 13698c2ecf20Sopenharmony_ci} 13708c2ecf20Sopenharmony_ciEXPORT_SYMBOL(agp3_generic_tlbflush); 13718c2ecf20Sopenharmony_ci 13728c2ecf20Sopenharmony_ciint agp3_generic_configure(void) 13738c2ecf20Sopenharmony_ci{ 13748c2ecf20Sopenharmony_ci u32 temp; 13758c2ecf20Sopenharmony_ci struct aper_size_info_16 *current_size; 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_ci current_size = A_SIZE_16(agp_bridge->current_size); 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_ci agp_bridge->gart_bus_addr = pci_bus_address(agp_bridge->dev, 13808c2ecf20Sopenharmony_ci AGP_APERTURE_BAR); 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci /* set aperture size */ 13838c2ecf20Sopenharmony_ci pci_write_config_word(agp_bridge->dev, agp_bridge->capndx+AGPAPSIZE, current_size->size_value); 13848c2ecf20Sopenharmony_ci /* set gart pointer */ 13858c2ecf20Sopenharmony_ci pci_write_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPGARTLO, agp_bridge->gatt_bus_addr); 13868c2ecf20Sopenharmony_ci /* enable aperture and GTLB */ 13878c2ecf20Sopenharmony_ci pci_read_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, &temp); 13888c2ecf20Sopenharmony_ci pci_write_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, temp | AGPCTRL_APERENB | AGPCTRL_GTLBEN); 13898c2ecf20Sopenharmony_ci return 0; 13908c2ecf20Sopenharmony_ci} 13918c2ecf20Sopenharmony_ciEXPORT_SYMBOL(agp3_generic_configure); 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_civoid agp3_generic_cleanup(void) 13948c2ecf20Sopenharmony_ci{ 13958c2ecf20Sopenharmony_ci u32 ctrl; 13968c2ecf20Sopenharmony_ci pci_read_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, &ctrl); 13978c2ecf20Sopenharmony_ci pci_write_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, ctrl & ~AGPCTRL_APERENB); 13988c2ecf20Sopenharmony_ci} 13998c2ecf20Sopenharmony_ciEXPORT_SYMBOL(agp3_generic_cleanup); 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_ciconst struct aper_size_info_16 agp3_generic_sizes[AGP_GENERIC_SIZES_ENTRIES] = 14028c2ecf20Sopenharmony_ci{ 14038c2ecf20Sopenharmony_ci {4096, 1048576, 10,0x000}, 14048c2ecf20Sopenharmony_ci {2048, 524288, 9, 0x800}, 14058c2ecf20Sopenharmony_ci {1024, 262144, 8, 0xc00}, 14068c2ecf20Sopenharmony_ci { 512, 131072, 7, 0xe00}, 14078c2ecf20Sopenharmony_ci { 256, 65536, 6, 0xf00}, 14088c2ecf20Sopenharmony_ci { 128, 32768, 5, 0xf20}, 14098c2ecf20Sopenharmony_ci { 64, 16384, 4, 0xf30}, 14108c2ecf20Sopenharmony_ci { 32, 8192, 3, 0xf38}, 14118c2ecf20Sopenharmony_ci { 16, 4096, 2, 0xf3c}, 14128c2ecf20Sopenharmony_ci { 8, 2048, 1, 0xf3e}, 14138c2ecf20Sopenharmony_ci { 4, 1024, 0, 0xf3f} 14148c2ecf20Sopenharmony_ci}; 14158c2ecf20Sopenharmony_ciEXPORT_SYMBOL(agp3_generic_sizes); 14168c2ecf20Sopenharmony_ci 1417