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>
42e47036b4SLaura Abbott #ifdef CONFIG_X86
43e47036b4SLaura Abbott #include <asm/set_memory.h>
44e47036b4SLaura Abbott #endif
451da177e4SLinus Torvalds #include "agp.h"
461da177e4SLinus Torvalds
471da177e4SLinus Torvalds __u32 *agp_gatt_table;
481da177e4SLinus Torvalds int agp_memory_reserved;
491da177e4SLinus Torvalds
501da177e4SLinus Torvalds /*
511da177e4SLinus Torvalds * Needed by the Nforce GART driver for the time being. Would be
521da177e4SLinus Torvalds * nice to do this some other way instead of needing this export.
531da177e4SLinus Torvalds */
541da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(agp_memory_reserved);
551da177e4SLinus Torvalds
561da177e4SLinus Torvalds /*
571da177e4SLinus Torvalds * Generic routines for handling agp_memory structures -
581da177e4SLinus Torvalds * They use the basic page allocation routines to do the brunt of the work.
591da177e4SLinus Torvalds */
601da177e4SLinus Torvalds
agp_free_key(int key)611da177e4SLinus Torvalds void agp_free_key(int key)
621da177e4SLinus Torvalds {
631da177e4SLinus Torvalds if (key < 0)
641da177e4SLinus Torvalds return;
651da177e4SLinus Torvalds
661da177e4SLinus Torvalds if (key < MAXKEY)
671da177e4SLinus Torvalds clear_bit(key, agp_bridge->key_list);
681da177e4SLinus Torvalds }
691da177e4SLinus Torvalds EXPORT_SYMBOL(agp_free_key);
701da177e4SLinus Torvalds
711da177e4SLinus Torvalds
agp_get_key(void)721da177e4SLinus Torvalds static int agp_get_key(void)
731da177e4SLinus Torvalds {
741da177e4SLinus Torvalds int bit;
751da177e4SLinus Torvalds
761da177e4SLinus Torvalds bit = find_first_zero_bit(agp_bridge->key_list, MAXKEY);
771da177e4SLinus Torvalds if (bit < MAXKEY) {
781da177e4SLinus Torvalds set_bit(bit, agp_bridge->key_list);
791da177e4SLinus Torvalds return bit;
801da177e4SLinus Torvalds }
811da177e4SLinus Torvalds return -1;
821da177e4SLinus Torvalds }
831da177e4SLinus Torvalds
84a030ce44SThomas Hellstrom /*
85a030ce44SThomas Hellstrom * Use kmalloc if possible for the page list. Otherwise fall back to
86a030ce44SThomas Hellstrom * vmalloc. This speeds things up and also saves memory for small AGP
87a030ce44SThomas Hellstrom * regions.
88a030ce44SThomas Hellstrom */
89a030ce44SThomas Hellstrom
agp_alloc_page_array(size_t size,struct agp_memory * mem)90a030ce44SThomas Hellstrom void agp_alloc_page_array(size_t size, struct agp_memory *mem)
91a030ce44SThomas Hellstrom {
92752ade68SMichal Hocko mem->pages = kvmalloc(size, GFP_KERNEL);
93a030ce44SThomas Hellstrom }
94a030ce44SThomas Hellstrom EXPORT_SYMBOL(agp_alloc_page_array);
95a030ce44SThomas Hellstrom
agp_create_user_memory(unsigned long num_agp_pages)96a030ce44SThomas Hellstrom static struct agp_memory *agp_create_user_memory(unsigned long num_agp_pages)
97a030ce44SThomas Hellstrom {
98a030ce44SThomas Hellstrom struct agp_memory *new;
99a030ce44SThomas Hellstrom unsigned long alloc_size = num_agp_pages*sizeof(struct page *);
100a030ce44SThomas Hellstrom
101b522f021SVasiliy Kulikov if (INT_MAX/sizeof(struct page *) < num_agp_pages)
102b522f021SVasiliy Kulikov return NULL;
103b522f021SVasiliy Kulikov
1041c14cfbbSAndrew Morton new = kzalloc(sizeof(struct agp_memory), GFP_KERNEL);
105a030ce44SThomas Hellstrom if (new == NULL)
106a030ce44SThomas Hellstrom return NULL;
107a030ce44SThomas Hellstrom
108a030ce44SThomas Hellstrom new->key = agp_get_key();
109a030ce44SThomas Hellstrom
110a030ce44SThomas Hellstrom if (new->key < 0) {
111a030ce44SThomas Hellstrom kfree(new);
112a030ce44SThomas Hellstrom return NULL;
113a030ce44SThomas Hellstrom }
114a030ce44SThomas Hellstrom
115a030ce44SThomas Hellstrom agp_alloc_page_array(alloc_size, new);
116a030ce44SThomas Hellstrom
11707613ba2SDave Airlie if (new->pages == NULL) {
118a030ce44SThomas Hellstrom agp_free_key(new->key);
119a030ce44SThomas Hellstrom kfree(new);
120a030ce44SThomas Hellstrom return NULL;
121a030ce44SThomas Hellstrom }
122a030ce44SThomas Hellstrom new->num_scratch_pages = 0;
123a030ce44SThomas Hellstrom return new;
124a030ce44SThomas Hellstrom }
125a030ce44SThomas Hellstrom
agp_create_memory(int scratch_pages)1261da177e4SLinus Torvalds struct agp_memory *agp_create_memory(int scratch_pages)
1271da177e4SLinus Torvalds {
1281da177e4SLinus Torvalds struct agp_memory *new;
1291da177e4SLinus Torvalds
1300ea27d9fSDave Jones new = kzalloc(sizeof(struct agp_memory), GFP_KERNEL);
1311da177e4SLinus Torvalds if (new == NULL)
1321da177e4SLinus Torvalds return NULL;
1331da177e4SLinus Torvalds
1341da177e4SLinus Torvalds new->key = agp_get_key();
1351da177e4SLinus Torvalds
1361da177e4SLinus Torvalds if (new->key < 0) {
1371da177e4SLinus Torvalds kfree(new);
1381da177e4SLinus Torvalds return NULL;
1391da177e4SLinus Torvalds }
140a030ce44SThomas Hellstrom
141a030ce44SThomas Hellstrom agp_alloc_page_array(PAGE_SIZE * scratch_pages, new);
1421da177e4SLinus Torvalds
14307613ba2SDave Airlie if (new->pages == NULL) {
1441da177e4SLinus Torvalds agp_free_key(new->key);
1451da177e4SLinus Torvalds kfree(new);
1461da177e4SLinus Torvalds return NULL;
1471da177e4SLinus Torvalds }
1481da177e4SLinus Torvalds new->num_scratch_pages = scratch_pages;
149a030ce44SThomas Hellstrom new->type = AGP_NORMAL_MEMORY;
1501da177e4SLinus Torvalds return new;
1511da177e4SLinus Torvalds }
1521da177e4SLinus Torvalds EXPORT_SYMBOL(agp_create_memory);
1531da177e4SLinus Torvalds
1541da177e4SLinus Torvalds /**
1551da177e4SLinus Torvalds * agp_free_memory - free memory associated with an agp_memory pointer.
1561da177e4SLinus Torvalds *
1571da177e4SLinus Torvalds * @curr: agp_memory pointer to be freed.
1581da177e4SLinus Torvalds *
1591da177e4SLinus Torvalds * It is the only function that can be called when the backend is not owned
1601da177e4SLinus Torvalds * by the caller. (So it can free memory on client death.)
1611da177e4SLinus Torvalds */
agp_free_memory(struct agp_memory * curr)1621da177e4SLinus Torvalds void agp_free_memory(struct agp_memory *curr)
1631da177e4SLinus Torvalds {
1641da177e4SLinus Torvalds size_t i;
1651da177e4SLinus Torvalds
1661da177e4SLinus Torvalds if (curr == NULL)
1671da177e4SLinus Torvalds return;
1681da177e4SLinus Torvalds
169c7258012SJoe Perches if (curr->is_bound)
1701da177e4SLinus Torvalds agp_unbind_memory(curr);
1711da177e4SLinus Torvalds
172a030ce44SThomas Hellstrom if (curr->type >= AGP_USER_TYPES) {
173a030ce44SThomas Hellstrom agp_generic_free_by_type(curr);
174a030ce44SThomas Hellstrom return;
175a030ce44SThomas Hellstrom }
176a030ce44SThomas Hellstrom
1771da177e4SLinus Torvalds if (curr->type != 0) {
1781da177e4SLinus Torvalds curr->bridge->driver->free_by_type(curr);
1791da177e4SLinus Torvalds return;
1801da177e4SLinus Torvalds }
1811da177e4SLinus Torvalds if (curr->page_count != 0) {
182bd07928cSShaohua Li if (curr->bridge->driver->agp_destroy_pages) {
183bd07928cSShaohua Li curr->bridge->driver->agp_destroy_pages(curr);
184bd07928cSShaohua Li } else {
185bd07928cSShaohua Li
1861da177e4SLinus Torvalds for (i = 0; i < curr->page_count; i++) {
187bd07928cSShaohua Li curr->bridge->driver->agp_destroy_page(
18807613ba2SDave Airlie curr->pages[i],
189da503fa6SJan Beulich AGP_PAGE_DESTROY_UNMAP);
1901da177e4SLinus Torvalds }
191a2721e99SDave Airlie for (i = 0; i < curr->page_count; i++) {
192bd07928cSShaohua Li curr->bridge->driver->agp_destroy_page(
19307613ba2SDave Airlie curr->pages[i],
194da503fa6SJan Beulich AGP_PAGE_DESTROY_FREE);
195a2721e99SDave Airlie }
1961da177e4SLinus Torvalds }
197bd07928cSShaohua Li }
1981da177e4SLinus Torvalds agp_free_key(curr->key);
199a030ce44SThomas Hellstrom agp_free_page_array(curr);
2001da177e4SLinus Torvalds kfree(curr);
2011da177e4SLinus Torvalds }
2021da177e4SLinus Torvalds EXPORT_SYMBOL(agp_free_memory);
2031da177e4SLinus Torvalds
2041da177e4SLinus Torvalds #define ENTRIES_PER_PAGE (PAGE_SIZE / sizeof(unsigned long))
2051da177e4SLinus Torvalds
2061da177e4SLinus Torvalds /**
2071da177e4SLinus Torvalds * agp_allocate_memory - allocate a group of pages of a certain type.
2081da177e4SLinus Torvalds *
2095f448266SCorentin Labbe * @bridge: an agp_bridge_data struct allocated for the AGP host bridge.
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 */
agp_allocate_memory(struct agp_bridge_data * bridge,size_t page_count,u32 type)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
agp_return_size(void)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
agp_num_entries(void)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 *
3585f448266SCorentin Labbe * @bridge: an agp_bridge_data struct allocated for the AGP host bridge.
3591da177e4SLinus Torvalds * @info: agp_kern_info pointer. The caller should insure that this pointer is valid.
3601da177e4SLinus Torvalds *
3611da177e4SLinus Torvalds * This function copies information about the agp bridge device and the state of
3621da177e4SLinus Torvalds * the agp backend into an agp_kern_info pointer.
3631da177e4SLinus Torvalds */
agp_copy_info(struct agp_bridge_data * bridge,struct agp_kern_info * info)3641da177e4SLinus Torvalds int agp_copy_info(struct agp_bridge_data *bridge, struct agp_kern_info *info)
3651da177e4SLinus Torvalds {
3661da177e4SLinus Torvalds memset(info, 0, sizeof(struct agp_kern_info));
3671da177e4SLinus Torvalds if (!bridge) {
3681da177e4SLinus Torvalds info->chipset = NOT_SUPPORTED;
3691da177e4SLinus Torvalds return -EIO;
3701da177e4SLinus Torvalds }
3711da177e4SLinus Torvalds
3721da177e4SLinus Torvalds info->version.major = bridge->version->major;
3731da177e4SLinus Torvalds info->version.minor = bridge->version->minor;
3741da177e4SLinus Torvalds info->chipset = SUPPORTED;
3751da177e4SLinus Torvalds info->device = bridge->dev;
37666bb8bf8SDavid Mosberger if (bridge->mode & AGPSTAT_MODE_3_0)
3771da177e4SLinus Torvalds info->mode = bridge->mode & ~AGP3_RESERVED_MASK;
3781da177e4SLinus Torvalds else
3791da177e4SLinus Torvalds info->mode = bridge->mode & ~AGP2_RESERVED_MASK;
3801da177e4SLinus Torvalds info->aper_base = bridge->gart_bus_addr;
3811da177e4SLinus Torvalds info->aper_size = agp_return_size();
3821da177e4SLinus Torvalds info->max_memory = bridge->max_memory_agp;
3831da177e4SLinus Torvalds info->current_memory = atomic_read(&bridge->current_memory_agp);
3841da177e4SLinus Torvalds info->cant_use_aperture = bridge->driver->cant_use_aperture;
3851da177e4SLinus Torvalds info->vm_ops = bridge->vm_ops;
3861da177e4SLinus Torvalds info->page_mask = ~0UL;
3871da177e4SLinus Torvalds return 0;
3881da177e4SLinus Torvalds }
3891da177e4SLinus Torvalds EXPORT_SYMBOL(agp_copy_info);
3901da177e4SLinus Torvalds
3911da177e4SLinus Torvalds /* End - Routine to copy over information structure */
3921da177e4SLinus Torvalds
3931da177e4SLinus Torvalds /*
3941da177e4SLinus Torvalds * Routines for handling swapping of agp_memory into the GATT -
3951da177e4SLinus Torvalds * These routines take agp_memory and insert them into the GATT.
3961da177e4SLinus Torvalds * They call device specific routines to actually write to the GATT.
3971da177e4SLinus Torvalds */
3981da177e4SLinus Torvalds
3991da177e4SLinus Torvalds /**
4001da177e4SLinus Torvalds * agp_bind_memory - Bind an agp_memory structure into the GATT.
4011da177e4SLinus Torvalds *
4021da177e4SLinus Torvalds * @curr: agp_memory pointer
4031da177e4SLinus Torvalds * @pg_start: an offset into the graphics aperture translation table
4041da177e4SLinus Torvalds *
4051da177e4SLinus Torvalds * It returns -EINVAL if the pointer == NULL.
4061da177e4SLinus Torvalds * It returns -EBUSY if the area of the table requested is already in use.
4071da177e4SLinus Torvalds */
agp_bind_memory(struct agp_memory * curr,off_t pg_start)4081da177e4SLinus Torvalds int agp_bind_memory(struct agp_memory *curr, off_t pg_start)
4091da177e4SLinus Torvalds {
4101da177e4SLinus Torvalds int ret_val;
4111da177e4SLinus Torvalds
4121da177e4SLinus Torvalds if (curr == NULL)
4131da177e4SLinus Torvalds return -EINVAL;
4141da177e4SLinus Torvalds
415c7258012SJoe Perches if (curr->is_bound) {
4161da177e4SLinus Torvalds printk(KERN_INFO PFX "memory %p is already bound!\n", curr);
4171da177e4SLinus Torvalds return -EINVAL;
4181da177e4SLinus Torvalds }
419c7258012SJoe Perches if (!curr->is_flushed) {
4201da177e4SLinus Torvalds curr->bridge->driver->cache_flush();
421c7258012SJoe Perches curr->is_flushed = true;
4221da177e4SLinus Torvalds }
423ff663cf8SZhenyu Wang
4241da177e4SLinus Torvalds ret_val = curr->bridge->driver->insert_memory(curr, pg_start, curr->type);
4251da177e4SLinus Torvalds
4261da177e4SLinus Torvalds if (ret_val != 0)
4271da177e4SLinus Torvalds return ret_val;
4281da177e4SLinus Torvalds
429c7258012SJoe Perches curr->is_bound = true;
4301da177e4SLinus Torvalds curr->pg_start = pg_start;
431a8c84df9SKeith Packard spin_lock(&agp_bridge->mapped_lock);
432a8c84df9SKeith Packard list_add(&curr->mapped_list, &agp_bridge->mapped_list);
433a8c84df9SKeith Packard spin_unlock(&agp_bridge->mapped_lock);
434a8c84df9SKeith Packard
4351da177e4SLinus Torvalds return 0;
4361da177e4SLinus Torvalds }
4371da177e4SLinus Torvalds EXPORT_SYMBOL(agp_bind_memory);
4381da177e4SLinus Torvalds
4391da177e4SLinus Torvalds
4401da177e4SLinus Torvalds /**
4411da177e4SLinus Torvalds * agp_unbind_memory - Removes an agp_memory structure from the GATT
4421da177e4SLinus Torvalds *
4431da177e4SLinus Torvalds * @curr: agp_memory pointer to be removed from the GATT.
4441da177e4SLinus Torvalds *
4451da177e4SLinus Torvalds * It returns -EINVAL if this piece of agp_memory is not currently bound to
4461da177e4SLinus Torvalds * the graphics aperture translation table or if the agp_memory pointer == NULL
4471da177e4SLinus Torvalds */
agp_unbind_memory(struct agp_memory * curr)4481da177e4SLinus Torvalds int agp_unbind_memory(struct agp_memory *curr)
4491da177e4SLinus Torvalds {
4501da177e4SLinus Torvalds int ret_val;
4511da177e4SLinus Torvalds
4521da177e4SLinus Torvalds if (curr == NULL)
4531da177e4SLinus Torvalds return -EINVAL;
4541da177e4SLinus Torvalds
455c7258012SJoe Perches if (!curr->is_bound) {
4561da177e4SLinus Torvalds printk(KERN_INFO PFX "memory %p was not bound!\n", curr);
4571da177e4SLinus Torvalds return -EINVAL;
4581da177e4SLinus Torvalds }
4591da177e4SLinus Torvalds
4601da177e4SLinus Torvalds ret_val = curr->bridge->driver->remove_memory(curr, curr->pg_start, curr->type);
4611da177e4SLinus Torvalds
4621da177e4SLinus Torvalds if (ret_val != 0)
4631da177e4SLinus Torvalds return ret_val;
4641da177e4SLinus Torvalds
465c7258012SJoe Perches curr->is_bound = false;
4661da177e4SLinus Torvalds curr->pg_start = 0;
467a8c84df9SKeith Packard spin_lock(&curr->bridge->mapped_lock);
468a8c84df9SKeith Packard list_del(&curr->mapped_list);
469a8c84df9SKeith Packard spin_unlock(&curr->bridge->mapped_lock);
4701da177e4SLinus Torvalds return 0;
4711da177e4SLinus Torvalds }
4721da177e4SLinus Torvalds EXPORT_SYMBOL(agp_unbind_memory);
4731da177e4SLinus Torvalds
474a8c84df9SKeith Packard
4751da177e4SLinus Torvalds /* End - Routines for handling swapping of agp_memory into the GATT */
4761da177e4SLinus Torvalds
4771da177e4SLinus Torvalds
4781da177e4SLinus Torvalds /* Generic Agp routines - Start */
agp_v2_parse_one(u32 * requested_mode,u32 * bridge_agpstat,u32 * vga_agpstat)4791da177e4SLinus Torvalds static void agp_v2_parse_one(u32 *requested_mode, u32 *bridge_agpstat, u32 *vga_agpstat)
4801da177e4SLinus Torvalds {
4811da177e4SLinus Torvalds u32 tmp;
4821da177e4SLinus Torvalds
4831da177e4SLinus Torvalds if (*requested_mode & AGP2_RESERVED_MASK) {
484c4dd4582SDave Jones printk(KERN_INFO PFX "reserved bits set (%x) in mode 0x%x. Fixed.\n",
485c4dd4582SDave Jones *requested_mode & AGP2_RESERVED_MASK, *requested_mode);
4861da177e4SLinus Torvalds *requested_mode &= ~AGP2_RESERVED_MASK;
4871da177e4SLinus Torvalds }
4881da177e4SLinus Torvalds
48928af24bbSDave Jones /*
49028af24bbSDave Jones * Some dumb bridges are programmed to disobey the AGP2 spec.
49128af24bbSDave Jones * This is likely a BIOS misprogramming rather than poweron default, or
49228af24bbSDave Jones * it would be a lot more common.
49328af24bbSDave Jones * https://bugs.freedesktop.org/show_bug.cgi?id=8816
49428af24bbSDave Jones * AGPv2 spec 6.1.9 states:
49528af24bbSDave Jones * The RATE field indicates the data transfer rates supported by this
49628af24bbSDave Jones * device. A.G.P. devices must report all that apply.
49728af24bbSDave Jones * Fix them up as best we can.
49828af24bbSDave Jones */
49928af24bbSDave Jones switch (*bridge_agpstat & 7) {
50028af24bbSDave Jones case 4:
50128af24bbSDave Jones *bridge_agpstat |= (AGPSTAT2_2X | AGPSTAT2_1X);
502e11d0b87STormod Volden printk(KERN_INFO PFX "BIOS bug. AGP bridge claims to only support x4 rate. "
50328af24bbSDave Jones "Fixing up support for x2 & x1\n");
50428af24bbSDave Jones break;
50528af24bbSDave Jones case 2:
50628af24bbSDave Jones *bridge_agpstat |= AGPSTAT2_1X;
507e11d0b87STormod Volden printk(KERN_INFO PFX "BIOS bug. AGP bridge claims to only support x2 rate. "
50828af24bbSDave Jones "Fixing up support for x1\n");
50928af24bbSDave Jones break;
51028af24bbSDave Jones default:
51128af24bbSDave Jones break;
51228af24bbSDave Jones }
51328af24bbSDave Jones
5141da177e4SLinus Torvalds /* Check the speed bits make sense. Only one should be set. */
5151da177e4SLinus Torvalds tmp = *requested_mode & 7;
5161da177e4SLinus Torvalds switch (tmp) {
5171da177e4SLinus Torvalds case 0:
5181da177e4SLinus Torvalds printk(KERN_INFO PFX "%s tried to set rate=x0. Setting to x1 mode.\n", current->comm);
5191da177e4SLinus Torvalds *requested_mode |= AGPSTAT2_1X;
5201da177e4SLinus Torvalds break;
5211da177e4SLinus Torvalds case 1:
5221da177e4SLinus Torvalds case 2:
5231da177e4SLinus Torvalds break;
5241da177e4SLinus Torvalds case 3:
5251da177e4SLinus Torvalds *requested_mode &= ~(AGPSTAT2_1X); /* rate=2 */
5261da177e4SLinus Torvalds break;
5271da177e4SLinus Torvalds case 4:
5281da177e4SLinus Torvalds break;
5291da177e4SLinus Torvalds case 5:
5301da177e4SLinus Torvalds case 6:
5311da177e4SLinus Torvalds case 7:
5321da177e4SLinus Torvalds *requested_mode &= ~(AGPSTAT2_1X|AGPSTAT2_2X); /* rate=4*/
5331da177e4SLinus Torvalds break;
5341da177e4SLinus Torvalds }
5351da177e4SLinus Torvalds
5361da177e4SLinus Torvalds /* disable SBA if it's not supported */
5371da177e4SLinus Torvalds if (!((*bridge_agpstat & AGPSTAT_SBA) && (*vga_agpstat & AGPSTAT_SBA) && (*requested_mode & AGPSTAT_SBA)))
5381da177e4SLinus Torvalds *bridge_agpstat &= ~AGPSTAT_SBA;
5391da177e4SLinus Torvalds
5401da177e4SLinus Torvalds /* Set rate */
5411da177e4SLinus Torvalds if (!((*bridge_agpstat & AGPSTAT2_4X) && (*vga_agpstat & AGPSTAT2_4X) && (*requested_mode & AGPSTAT2_4X)))
5421da177e4SLinus Torvalds *bridge_agpstat &= ~AGPSTAT2_4X;
5431da177e4SLinus Torvalds
5441da177e4SLinus Torvalds if (!((*bridge_agpstat & AGPSTAT2_2X) && (*vga_agpstat & AGPSTAT2_2X) && (*requested_mode & AGPSTAT2_2X)))
5451da177e4SLinus Torvalds *bridge_agpstat &= ~AGPSTAT2_2X;
5461da177e4SLinus Torvalds
5471da177e4SLinus Torvalds if (!((*bridge_agpstat & AGPSTAT2_1X) && (*vga_agpstat & AGPSTAT2_1X) && (*requested_mode & AGPSTAT2_1X)))
5481da177e4SLinus Torvalds *bridge_agpstat &= ~AGPSTAT2_1X;
5491da177e4SLinus Torvalds
5501da177e4SLinus Torvalds /* Now we know what mode it should be, clear out the unwanted bits. */
5511da177e4SLinus Torvalds if (*bridge_agpstat & AGPSTAT2_4X)
5521da177e4SLinus Torvalds *bridge_agpstat &= ~(AGPSTAT2_1X | AGPSTAT2_2X); /* 4X */
5531da177e4SLinus Torvalds
5541da177e4SLinus Torvalds if (*bridge_agpstat & AGPSTAT2_2X)
5551da177e4SLinus Torvalds *bridge_agpstat &= ~(AGPSTAT2_1X | AGPSTAT2_4X); /* 2X */
5561da177e4SLinus Torvalds
5571da177e4SLinus Torvalds if (*bridge_agpstat & AGPSTAT2_1X)
5581da177e4SLinus Torvalds *bridge_agpstat &= ~(AGPSTAT2_2X | AGPSTAT2_4X); /* 1X */
5591da177e4SLinus Torvalds
5601da177e4SLinus Torvalds /* Apply any errata. */
5611da177e4SLinus Torvalds if (agp_bridge->flags & AGP_ERRATA_FASTWRITES)
5621da177e4SLinus Torvalds *bridge_agpstat &= ~AGPSTAT_FW;
5631da177e4SLinus Torvalds
5641da177e4SLinus Torvalds if (agp_bridge->flags & AGP_ERRATA_SBA)
5651da177e4SLinus Torvalds *bridge_agpstat &= ~AGPSTAT_SBA;
5661da177e4SLinus Torvalds
5671da177e4SLinus Torvalds if (agp_bridge->flags & AGP_ERRATA_1X) {
5681da177e4SLinus Torvalds *bridge_agpstat &= ~(AGPSTAT2_2X | AGPSTAT2_4X);
5691da177e4SLinus Torvalds *bridge_agpstat |= AGPSTAT2_1X;
5701da177e4SLinus Torvalds }
5711da177e4SLinus Torvalds
5721da177e4SLinus Torvalds /* If we've dropped down to 1X, disable fast writes. */
5731da177e4SLinus Torvalds if (*bridge_agpstat & AGPSTAT2_1X)
5741da177e4SLinus Torvalds *bridge_agpstat &= ~AGPSTAT_FW;
5751da177e4SLinus Torvalds }
5761da177e4SLinus Torvalds
5771da177e4SLinus Torvalds /*
5781da177e4SLinus Torvalds * requested_mode = Mode requested by (typically) X.
5791da177e4SLinus Torvalds * bridge_agpstat = PCI_AGP_STATUS from agp bridge.
5801da177e4SLinus Torvalds * vga_agpstat = PCI_AGP_STATUS from graphic card.
5811da177e4SLinus Torvalds */
agp_v3_parse_one(u32 * requested_mode,u32 * bridge_agpstat,u32 * vga_agpstat)5821da177e4SLinus Torvalds static void agp_v3_parse_one(u32 *requested_mode, u32 *bridge_agpstat, u32 *vga_agpstat)
5831da177e4SLinus Torvalds {
5841da177e4SLinus Torvalds u32 origbridge=*bridge_agpstat, origvga=*vga_agpstat;
5851da177e4SLinus Torvalds u32 tmp;
5861da177e4SLinus Torvalds
5871da177e4SLinus Torvalds if (*requested_mode & AGP3_RESERVED_MASK) {
588c4dd4582SDave Jones printk(KERN_INFO PFX "reserved bits set (%x) in mode 0x%x. Fixed.\n",
589c4dd4582SDave Jones *requested_mode & AGP3_RESERVED_MASK, *requested_mode);
5901da177e4SLinus Torvalds *requested_mode &= ~AGP3_RESERVED_MASK;
5911da177e4SLinus Torvalds }
5921da177e4SLinus Torvalds
5931da177e4SLinus Torvalds /* Check the speed bits make sense. */
5941da177e4SLinus Torvalds tmp = *requested_mode & 7;
5951da177e4SLinus Torvalds if (tmp == 0) {
5961da177e4SLinus Torvalds printk(KERN_INFO PFX "%s tried to set rate=x0. Setting to AGP3 x4 mode.\n", current->comm);
5971da177e4SLinus Torvalds *requested_mode |= AGPSTAT3_4X;
5981da177e4SLinus Torvalds }
5991da177e4SLinus Torvalds if (tmp >= 3) {
6001da177e4SLinus Torvalds printk(KERN_INFO PFX "%s tried to set rate=x%d. Setting to AGP3 x8 mode.\n", current->comm, tmp * 4);
6011da177e4SLinus Torvalds *requested_mode = (*requested_mode & ~7) | AGPSTAT3_8X;
6021da177e4SLinus Torvalds }
6031da177e4SLinus Torvalds
6041da177e4SLinus Torvalds /* ARQSZ - Set the value to the maximum one.
6051da177e4SLinus Torvalds * Don't allow the mode register to override values. */
6061da177e4SLinus Torvalds *bridge_agpstat = ((*bridge_agpstat & ~AGPSTAT_ARQSZ) |
6071da177e4SLinus Torvalds max_t(u32,(*bridge_agpstat & AGPSTAT_ARQSZ),(*vga_agpstat & AGPSTAT_ARQSZ)));
6081da177e4SLinus Torvalds
6091da177e4SLinus Torvalds /* Calibration cycle.
6101da177e4SLinus Torvalds * Don't allow the mode register to override values. */
6111da177e4SLinus Torvalds *bridge_agpstat = ((*bridge_agpstat & ~AGPSTAT_CAL_MASK) |
6121da177e4SLinus Torvalds min_t(u32,(*bridge_agpstat & AGPSTAT_CAL_MASK),(*vga_agpstat & AGPSTAT_CAL_MASK)));
6131da177e4SLinus Torvalds
6141da177e4SLinus Torvalds /* SBA *must* be supported for AGP v3 */
6151da177e4SLinus Torvalds *bridge_agpstat |= AGPSTAT_SBA;
6161da177e4SLinus Torvalds
6171da177e4SLinus Torvalds /*
6181da177e4SLinus Torvalds * Set speed.
6191da177e4SLinus Torvalds * Check for invalid speeds. This can happen when applications
6201da177e4SLinus Torvalds * written before the AGP 3.0 standard pass AGP2.x modes to AGP3 hardware
6211da177e4SLinus Torvalds */
6221da177e4SLinus Torvalds if (*requested_mode & AGPSTAT_MODE_3_0) {
6231da177e4SLinus Torvalds /*
6241da177e4SLinus Torvalds * Caller hasn't a clue what it is doing. Bridge is in 3.0 mode,
6251da177e4SLinus Torvalds * have been passed a 3.0 mode, but with 2.x speed bits set.
6261da177e4SLinus Torvalds * AGP2.x 4x -> AGP3.0 4x.
6271da177e4SLinus Torvalds */
6281da177e4SLinus Torvalds if (*requested_mode & AGPSTAT2_4X) {
6291da177e4SLinus Torvalds printk(KERN_INFO PFX "%s passes broken AGP3 flags (%x). Fixed.\n",
6301da177e4SLinus Torvalds current->comm, *requested_mode);
6311da177e4SLinus Torvalds *requested_mode &= ~AGPSTAT2_4X;
6321da177e4SLinus Torvalds *requested_mode |= AGPSTAT3_4X;
6331da177e4SLinus Torvalds }
6341da177e4SLinus Torvalds } else {
6351da177e4SLinus Torvalds /*
6361da177e4SLinus Torvalds * The caller doesn't know what they are doing. We are in 3.0 mode,
6371da177e4SLinus Torvalds * but have been passed an AGP 2.x mode.
6381da177e4SLinus Torvalds * Convert AGP 1x,2x,4x -> AGP 3.0 4x.
6391da177e4SLinus Torvalds */
6401da177e4SLinus Torvalds printk(KERN_INFO PFX "%s passes broken AGP2 flags (%x) in AGP3 mode. Fixed.\n",
6411da177e4SLinus Torvalds current->comm, *requested_mode);
6421da177e4SLinus Torvalds *requested_mode &= ~(AGPSTAT2_4X | AGPSTAT2_2X | AGPSTAT2_1X);
6431da177e4SLinus Torvalds *requested_mode |= AGPSTAT3_4X;
6441da177e4SLinus Torvalds }
6451da177e4SLinus Torvalds
6461da177e4SLinus Torvalds if (*requested_mode & AGPSTAT3_8X) {
6471da177e4SLinus Torvalds if (!(*bridge_agpstat & AGPSTAT3_8X)) {
6481da177e4SLinus Torvalds *bridge_agpstat &= ~(AGPSTAT3_8X | AGPSTAT3_RSVD);
6491da177e4SLinus Torvalds *bridge_agpstat |= AGPSTAT3_4X;
6508c8b8385SDave Jones printk(KERN_INFO PFX "%s requested AGPx8 but bridge not capable.\n", current->comm);
6511da177e4SLinus Torvalds return;
6521da177e4SLinus Torvalds }
6531da177e4SLinus Torvalds if (!(*vga_agpstat & AGPSTAT3_8X)) {
6541da177e4SLinus Torvalds *bridge_agpstat &= ~(AGPSTAT3_8X | AGPSTAT3_RSVD);
6551da177e4SLinus Torvalds *bridge_agpstat |= AGPSTAT3_4X;
6568c8b8385SDave Jones printk(KERN_INFO PFX "%s requested AGPx8 but graphic card not capable.\n", current->comm);
6571da177e4SLinus Torvalds return;
6581da177e4SLinus Torvalds }
6591da177e4SLinus Torvalds /* All set, bridge & device can do AGP x8*/
6601da177e4SLinus Torvalds *bridge_agpstat &= ~(AGPSTAT3_4X | AGPSTAT3_RSVD);
6611da177e4SLinus Torvalds goto done;
6621da177e4SLinus Torvalds
663edf03fb0SDave Jones } else if (*requested_mode & AGPSTAT3_4X) {
664edf03fb0SDave Jones *bridge_agpstat &= ~(AGPSTAT3_8X | AGPSTAT3_RSVD);
665edf03fb0SDave Jones *bridge_agpstat |= AGPSTAT3_4X;
666edf03fb0SDave Jones goto done;
667edf03fb0SDave Jones
6681da177e4SLinus Torvalds } else {
6691da177e4SLinus Torvalds
6701da177e4SLinus Torvalds /*
671edf03fb0SDave Jones * If we didn't specify an AGP mode, we see if both
672edf03fb0SDave Jones * the graphics card, and the bridge can do x8, and use if so.
673edf03fb0SDave Jones * If not, we fall back to x4 mode.
6741da177e4SLinus Torvalds */
675edf03fb0SDave Jones if ((*bridge_agpstat & AGPSTAT3_8X) && (*vga_agpstat & AGPSTAT3_8X)) {
6762cc1a413SDave Jones printk(KERN_INFO PFX "No AGP mode specified. Setting to highest mode "
6772cc1a413SDave Jones "supported by bridge & card (x8).\n");
678edf03fb0SDave Jones *bridge_agpstat &= ~(AGPSTAT3_4X | AGPSTAT3_RSVD);
679edf03fb0SDave Jones *vga_agpstat &= ~(AGPSTAT3_4X | AGPSTAT3_RSVD);
680edf03fb0SDave Jones } else {
681edf03fb0SDave Jones printk(KERN_INFO PFX "Fell back to AGPx4 mode because ");
682edf03fb0SDave Jones if (!(*bridge_agpstat & AGPSTAT3_8X)) {
6832cc1a413SDave Jones printk(KERN_INFO PFX "bridge couldn't do x8. bridge_agpstat:%x (orig=%x)\n",
6842cc1a413SDave Jones *bridge_agpstat, origbridge);
6851da177e4SLinus Torvalds *bridge_agpstat &= ~(AGPSTAT3_8X | AGPSTAT3_RSVD);
6861da177e4SLinus Torvalds *bridge_agpstat |= AGPSTAT3_4X;
687edf03fb0SDave Jones }
688edf03fb0SDave Jones if (!(*vga_agpstat & AGPSTAT3_8X)) {
6892cc1a413SDave Jones printk(KERN_INFO PFX "graphics card couldn't do x8. vga_agpstat:%x (orig=%x)\n",
6902cc1a413SDave Jones *vga_agpstat, origvga);
691edf03fb0SDave Jones *vga_agpstat &= ~(AGPSTAT3_8X | AGPSTAT3_RSVD);
692edf03fb0SDave Jones *vga_agpstat |= AGPSTAT3_4X;
693edf03fb0SDave Jones }
6941da177e4SLinus Torvalds }
6951da177e4SLinus Torvalds }
6961da177e4SLinus Torvalds
6971da177e4SLinus Torvalds done:
6981da177e4SLinus Torvalds /* Apply any errata. */
6991da177e4SLinus Torvalds if (agp_bridge->flags & AGP_ERRATA_FASTWRITES)
7001da177e4SLinus Torvalds *bridge_agpstat &= ~AGPSTAT_FW;
7011da177e4SLinus Torvalds
7021da177e4SLinus Torvalds if (agp_bridge->flags & AGP_ERRATA_SBA)
7031da177e4SLinus Torvalds *bridge_agpstat &= ~AGPSTAT_SBA;
7041da177e4SLinus Torvalds
7051da177e4SLinus Torvalds if (agp_bridge->flags & AGP_ERRATA_1X) {
7061da177e4SLinus Torvalds *bridge_agpstat &= ~(AGPSTAT2_2X | AGPSTAT2_4X);
7071da177e4SLinus Torvalds *bridge_agpstat |= AGPSTAT2_1X;
7081da177e4SLinus Torvalds }
7091da177e4SLinus Torvalds }
7101da177e4SLinus Torvalds
7111da177e4SLinus Torvalds
7121da177e4SLinus Torvalds /**
7131da177e4SLinus Torvalds * agp_collect_device_status - determine correct agp_cmd from various agp_stat's
7141da177e4SLinus Torvalds * @bridge: an agp_bridge_data struct allocated for the AGP host bridge.
7151da177e4SLinus Torvalds * @requested_mode: requested agp_stat from userspace (Typically from X)
7161da177e4SLinus Torvalds * @bridge_agpstat: current agp_stat from AGP bridge.
7171da177e4SLinus Torvalds *
7181da177e4SLinus Torvalds * This function will hunt for an AGP graphics card, and try to match
7191da177e4SLinus Torvalds * the requested mode to the capabilities of both the bridge and the card.
7201da177e4SLinus Torvalds */
agp_collect_device_status(struct agp_bridge_data * bridge,u32 requested_mode,u32 bridge_agpstat)7211da177e4SLinus Torvalds u32 agp_collect_device_status(struct agp_bridge_data *bridge, u32 requested_mode, u32 bridge_agpstat)
7221da177e4SLinus Torvalds {
7231da177e4SLinus Torvalds struct pci_dev *device = NULL;
7241da177e4SLinus Torvalds u32 vga_agpstat;
7251da177e4SLinus Torvalds u8 cap_ptr;
7261da177e4SLinus Torvalds
7271da177e4SLinus Torvalds for (;;) {
7281da177e4SLinus Torvalds device = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, device);
7291da177e4SLinus Torvalds if (!device) {
7301da177e4SLinus Torvalds printk(KERN_INFO PFX "Couldn't find an AGP VGA controller.\n");
7311da177e4SLinus Torvalds return 0;
7321da177e4SLinus Torvalds }
7331da177e4SLinus Torvalds cap_ptr = pci_find_capability(device, PCI_CAP_ID_AGP);
7341da177e4SLinus Torvalds if (cap_ptr)
7351da177e4SLinus Torvalds break;
7361da177e4SLinus Torvalds }
7371da177e4SLinus Torvalds
7381da177e4SLinus Torvalds /*
7391da177e4SLinus Torvalds * Ok, here we have a AGP device. Disable impossible
7401da177e4SLinus Torvalds * settings, and adjust the readqueue to the minimum.
7411da177e4SLinus Torvalds */
7421da177e4SLinus Torvalds pci_read_config_dword(device, cap_ptr+PCI_AGP_STATUS, &vga_agpstat);
7431da177e4SLinus Torvalds
7441da177e4SLinus Torvalds /* adjust RQ depth */
7451da177e4SLinus Torvalds bridge_agpstat = ((bridge_agpstat & ~AGPSTAT_RQ_DEPTH) |
7461da177e4SLinus Torvalds min_t(u32, (requested_mode & AGPSTAT_RQ_DEPTH),
7471da177e4SLinus Torvalds min_t(u32, (bridge_agpstat & AGPSTAT_RQ_DEPTH), (vga_agpstat & AGPSTAT_RQ_DEPTH))));
7481da177e4SLinus Torvalds
7491da177e4SLinus Torvalds /* disable FW if it's not supported */
7501da177e4SLinus Torvalds if (!((bridge_agpstat & AGPSTAT_FW) &&
7511da177e4SLinus Torvalds (vga_agpstat & AGPSTAT_FW) &&
7521da177e4SLinus Torvalds (requested_mode & AGPSTAT_FW)))
7531da177e4SLinus Torvalds bridge_agpstat &= ~AGPSTAT_FW;
7541da177e4SLinus Torvalds
7551da177e4SLinus Torvalds /* Check to see if we are operating in 3.0 mode */
75666bb8bf8SDavid Mosberger if (agp_bridge->mode & AGPSTAT_MODE_3_0)
7571da177e4SLinus Torvalds agp_v3_parse_one(&requested_mode, &bridge_agpstat, &vga_agpstat);
7581da177e4SLinus Torvalds else
7591da177e4SLinus Torvalds agp_v2_parse_one(&requested_mode, &bridge_agpstat, &vga_agpstat);
7601da177e4SLinus Torvalds
7611da177e4SLinus Torvalds pci_dev_put(device);
7621da177e4SLinus Torvalds return bridge_agpstat;
7631da177e4SLinus Torvalds }
7641da177e4SLinus Torvalds EXPORT_SYMBOL(agp_collect_device_status);
7651da177e4SLinus Torvalds
7661da177e4SLinus Torvalds
agp_device_command(u32 bridge_agpstat,bool agp_v3)767c7258012SJoe Perches void agp_device_command(u32 bridge_agpstat, bool agp_v3)
7681da177e4SLinus Torvalds {
7691da177e4SLinus Torvalds struct pci_dev *device = NULL;
7701da177e4SLinus Torvalds int mode;
7711da177e4SLinus Torvalds
7721da177e4SLinus Torvalds mode = bridge_agpstat & 0x7;
7731da177e4SLinus Torvalds if (agp_v3)
7741da177e4SLinus Torvalds mode *= 4;
7751da177e4SLinus Torvalds
7761da177e4SLinus Torvalds for_each_pci_dev(device) {
7771da177e4SLinus Torvalds u8 agp = pci_find_capability(device, PCI_CAP_ID_AGP);
7781da177e4SLinus Torvalds if (!agp)
7791da177e4SLinus Torvalds continue;
7801da177e4SLinus Torvalds
781e3cf6951SBjorn Helgaas dev_info(&device->dev, "putting AGP V%d device into %dx mode\n",
782e3cf6951SBjorn Helgaas agp_v3 ? 3 : 2, mode);
7831da177e4SLinus Torvalds pci_write_config_dword(device, agp + PCI_AGP_COMMAND, bridge_agpstat);
7841da177e4SLinus Torvalds }
7851da177e4SLinus Torvalds }
7861da177e4SLinus Torvalds EXPORT_SYMBOL(agp_device_command);
7871da177e4SLinus Torvalds
7881da177e4SLinus Torvalds
get_agp_version(struct agp_bridge_data * bridge)7891da177e4SLinus Torvalds void get_agp_version(struct agp_bridge_data *bridge)
7901da177e4SLinus Torvalds {
7911da177e4SLinus Torvalds u32 ncapid;
7921da177e4SLinus Torvalds
7931da177e4SLinus Torvalds /* Exit early if already set by errata workarounds. */
7941da177e4SLinus Torvalds if (bridge->major_version != 0)
7951da177e4SLinus Torvalds return;
7961da177e4SLinus Torvalds
7971da177e4SLinus Torvalds pci_read_config_dword(bridge->dev, bridge->capndx, &ncapid);
7981da177e4SLinus Torvalds bridge->major_version = (ncapid >> AGP_MAJOR_VERSION_SHIFT) & 0xf;
7991da177e4SLinus Torvalds bridge->minor_version = (ncapid >> AGP_MINOR_VERSION_SHIFT) & 0xf;
8001da177e4SLinus Torvalds }
8011da177e4SLinus Torvalds EXPORT_SYMBOL(get_agp_version);
8021da177e4SLinus Torvalds
8031da177e4SLinus Torvalds
agp_generic_enable(struct agp_bridge_data * bridge,u32 requested_mode)8041da177e4SLinus Torvalds void agp_generic_enable(struct agp_bridge_data *bridge, u32 requested_mode)
8051da177e4SLinus Torvalds {
8061da177e4SLinus Torvalds u32 bridge_agpstat, temp;
8071da177e4SLinus Torvalds
8081da177e4SLinus Torvalds get_agp_version(agp_bridge);
8091da177e4SLinus Torvalds
810e3cf6951SBjorn Helgaas dev_info(&agp_bridge->dev->dev, "AGP %d.%d bridge\n",
811e3cf6951SBjorn Helgaas agp_bridge->major_version, agp_bridge->minor_version);
8121da177e4SLinus Torvalds
8131da177e4SLinus Torvalds pci_read_config_dword(agp_bridge->dev,
8141da177e4SLinus Torvalds agp_bridge->capndx + PCI_AGP_STATUS, &bridge_agpstat);
8151da177e4SLinus Torvalds
8161da177e4SLinus Torvalds bridge_agpstat = agp_collect_device_status(agp_bridge, requested_mode, bridge_agpstat);
8171da177e4SLinus Torvalds if (bridge_agpstat == 0)
8181da177e4SLinus Torvalds /* Something bad happened. FIXME: Return error code? */
8191da177e4SLinus Torvalds return;
8201da177e4SLinus Torvalds
8211da177e4SLinus Torvalds bridge_agpstat |= AGPSTAT_AGP_ENABLE;
8221da177e4SLinus Torvalds
8231da177e4SLinus Torvalds /* Do AGP version specific frobbing. */
8241da177e4SLinus Torvalds if (bridge->major_version >= 3) {
82566bb8bf8SDavid Mosberger if (bridge->mode & AGPSTAT_MODE_3_0) {
8261da177e4SLinus Torvalds /* If we have 3.5, we can do the isoch stuff. */
8271da177e4SLinus Torvalds if (bridge->minor_version >= 5)
8281da177e4SLinus Torvalds agp_3_5_enable(bridge);
829c7258012SJoe Perches agp_device_command(bridge_agpstat, true);
8301da177e4SLinus Torvalds return;
8311da177e4SLinus Torvalds } else {
8321da177e4SLinus Torvalds /* Disable calibration cycle in RX91<1> when not in AGP3.0 mode of operation.*/
8331da177e4SLinus Torvalds bridge_agpstat &= ~(7<<10) ;
8341da177e4SLinus Torvalds pci_read_config_dword(bridge->dev,
8351da177e4SLinus Torvalds bridge->capndx+AGPCTRL, &temp);
8361da177e4SLinus Torvalds temp |= (1<<9);
8371da177e4SLinus Torvalds pci_write_config_dword(bridge->dev,
8381da177e4SLinus Torvalds bridge->capndx+AGPCTRL, temp);
8391da177e4SLinus Torvalds
840e3cf6951SBjorn Helgaas dev_info(&bridge->dev->dev, "bridge is in legacy mode, falling back to 2.x\n");
8411da177e4SLinus Torvalds }
8421da177e4SLinus Torvalds }
8431da177e4SLinus Torvalds
8441da177e4SLinus Torvalds /* AGP v<3 */
845c7258012SJoe Perches agp_device_command(bridge_agpstat, false);
8461da177e4SLinus Torvalds }
8471da177e4SLinus Torvalds EXPORT_SYMBOL(agp_generic_enable);
8481da177e4SLinus Torvalds
8491da177e4SLinus Torvalds
agp_generic_create_gatt_table(struct agp_bridge_data * bridge)8501da177e4SLinus Torvalds int agp_generic_create_gatt_table(struct agp_bridge_data *bridge)
8511da177e4SLinus Torvalds {
8521da177e4SLinus Torvalds char *table;
8531da177e4SLinus Torvalds char *table_end;
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;
8679fc785f1SCorentin Labbe 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 page_order =
8741da177e4SLinus Torvalds A_SIZE_8(temp)->page_order;
8751da177e4SLinus Torvalds num_entries =
8761da177e4SLinus Torvalds A_SIZE_8(temp)->num_entries;
8771da177e4SLinus Torvalds break;
8781da177e4SLinus Torvalds case U16_APER_SIZE:
8791da177e4SLinus Torvalds page_order = A_SIZE_16(temp)->page_order;
8801da177e4SLinus Torvalds num_entries = A_SIZE_16(temp)->num_entries;
8811da177e4SLinus Torvalds break;
8821da177e4SLinus Torvalds case U32_APER_SIZE:
8831da177e4SLinus Torvalds page_order = A_SIZE_32(temp)->page_order;
8841da177e4SLinus Torvalds num_entries = A_SIZE_32(temp)->num_entries;
8851da177e4SLinus Torvalds break;
8861da177e4SLinus Torvalds /* This case will never really happen. */
8871da177e4SLinus Torvalds case FIXED_APER_SIZE:
8881da177e4SLinus Torvalds case LVL2_APER_SIZE:
8891da177e4SLinus Torvalds default:
8909fc785f1SCorentin Labbe page_order = num_entries = 0;
8911da177e4SLinus Torvalds break;
8921da177e4SLinus Torvalds }
8931da177e4SLinus Torvalds
89407eee78eSKeir Fraser table = alloc_gatt_pages(page_order);
8951da177e4SLinus Torvalds
8961da177e4SLinus Torvalds if (table == NULL) {
8971da177e4SLinus Torvalds i++;
8981da177e4SLinus Torvalds switch (bridge->driver->size_type) {
8991da177e4SLinus Torvalds case U8_APER_SIZE:
9001da177e4SLinus Torvalds bridge->current_size = A_IDX8(bridge);
9011da177e4SLinus Torvalds break;
9021da177e4SLinus Torvalds case U16_APER_SIZE:
9031da177e4SLinus Torvalds bridge->current_size = A_IDX16(bridge);
9041da177e4SLinus Torvalds break;
9051da177e4SLinus Torvalds case U32_APER_SIZE:
9061da177e4SLinus Torvalds bridge->current_size = A_IDX32(bridge);
9071da177e4SLinus Torvalds break;
90889197e34SDave Jones /* These cases will never really happen. */
9091da177e4SLinus Torvalds case FIXED_APER_SIZE:
9101da177e4SLinus Torvalds case LVL2_APER_SIZE:
9111da177e4SLinus Torvalds default:
9121da177e4SLinus Torvalds break;
9131da177e4SLinus Torvalds }
9141da177e4SLinus Torvalds temp = bridge->current_size;
9151da177e4SLinus Torvalds } else {
9161da177e4SLinus Torvalds bridge->aperture_size_idx = i;
9171da177e4SLinus Torvalds }
9181da177e4SLinus Torvalds } while (!table && (i < bridge->driver->num_aperture_sizes));
9191da177e4SLinus Torvalds } else {
9201da177e4SLinus Torvalds page_order = ((struct aper_size_info_fixed *) temp)->page_order;
9211da177e4SLinus Torvalds num_entries = ((struct aper_size_info_fixed *) temp)->num_entries;
92207eee78eSKeir Fraser table = alloc_gatt_pages(page_order);
9231da177e4SLinus Torvalds }
9241da177e4SLinus Torvalds
9251da177e4SLinus Torvalds if (table == NULL)
9261da177e4SLinus Torvalds return -ENOMEM;
9271da177e4SLinus Torvalds
9281da177e4SLinus Torvalds table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1);
9291da177e4SLinus Torvalds
9301da177e4SLinus Torvalds for (page = virt_to_page(table); page <= virt_to_page(table_end); page++)
9311da177e4SLinus Torvalds SetPageReserved(page);
9321da177e4SLinus Torvalds
9331da177e4SLinus Torvalds bridge->gatt_table_real = (u32 *) table;
9341da177e4SLinus Torvalds agp_gatt_table = (void *)table;
9351da177e4SLinus Torvalds
9361da177e4SLinus Torvalds bridge->driver->cache_flush();
937fcea424dSArjan van dev Ven #ifdef CONFIG_X86
9381b13fe6aSBorislav Petkov if (set_memory_uc((unsigned long)table, 1 << page_order))
939e11d0b87STormod Volden printk(KERN_WARNING "Could not set GATT table memory to UC!\n");
9401b13fe6aSBorislav Petkov
941ae85226eSSantosh Nayak bridge->gatt_table = (u32 __iomem *)table;
942fcea424dSArjan van dev Ven #else
943*4bdc0d67SChristoph Hellwig bridge->gatt_table = ioremap(virt_to_phys(table),
9441da177e4SLinus Torvalds (PAGE_SIZE * (1 << page_order)));
9451da177e4SLinus Torvalds bridge->driver->cache_flush();
946fcea424dSArjan van dev Ven #endif
9471da177e4SLinus Torvalds
9481da177e4SLinus Torvalds if (bridge->gatt_table == NULL) {
9491da177e4SLinus Torvalds for (page = virt_to_page(table); page <= virt_to_page(table_end); page++)
9501da177e4SLinus Torvalds ClearPageReserved(page);
9511da177e4SLinus Torvalds
95207eee78eSKeir Fraser free_gatt_pages(table, page_order);
9531da177e4SLinus Torvalds
9541da177e4SLinus Torvalds return -ENOMEM;
9551da177e4SLinus Torvalds }
9566a12235cSDavid Woodhouse bridge->gatt_bus_addr = virt_to_phys(bridge->gatt_table_real);
9571da177e4SLinus Torvalds
9581da177e4SLinus Torvalds /* AK: bogus, should encode addresses > 4GB */
9591da177e4SLinus Torvalds for (i = 0; i < num_entries; i++) {
9601da177e4SLinus Torvalds writel(bridge->scratch_page, bridge->gatt_table+i);
9611da177e4SLinus Torvalds readl(bridge->gatt_table+i); /* PCI Posting. */
9621da177e4SLinus Torvalds }
9631da177e4SLinus Torvalds
9641da177e4SLinus Torvalds return 0;
9651da177e4SLinus Torvalds }
9661da177e4SLinus Torvalds EXPORT_SYMBOL(agp_generic_create_gatt_table);
9671da177e4SLinus Torvalds
agp_generic_free_gatt_table(struct agp_bridge_data * bridge)9681da177e4SLinus Torvalds int agp_generic_free_gatt_table(struct agp_bridge_data *bridge)
9691da177e4SLinus Torvalds {
9701da177e4SLinus Torvalds int page_order;
9711da177e4SLinus Torvalds char *table, *table_end;
9721da177e4SLinus Torvalds void *temp;
9731da177e4SLinus Torvalds struct page *page;
9741da177e4SLinus Torvalds
9751da177e4SLinus Torvalds temp = bridge->current_size;
9761da177e4SLinus Torvalds
9771da177e4SLinus Torvalds switch (bridge->driver->size_type) {
9781da177e4SLinus Torvalds case U8_APER_SIZE:
9791da177e4SLinus Torvalds page_order = A_SIZE_8(temp)->page_order;
9801da177e4SLinus Torvalds break;
9811da177e4SLinus Torvalds case U16_APER_SIZE:
9821da177e4SLinus Torvalds page_order = A_SIZE_16(temp)->page_order;
9831da177e4SLinus Torvalds break;
9841da177e4SLinus Torvalds case U32_APER_SIZE:
9851da177e4SLinus Torvalds page_order = A_SIZE_32(temp)->page_order;
9861da177e4SLinus Torvalds break;
9871da177e4SLinus Torvalds case FIXED_APER_SIZE:
9881da177e4SLinus Torvalds page_order = A_SIZE_FIX(temp)->page_order;
9891da177e4SLinus Torvalds break;
9901da177e4SLinus Torvalds case LVL2_APER_SIZE:
9911da177e4SLinus Torvalds /* The generic routines can't deal with 2 level gatt's */
9921da177e4SLinus Torvalds return -EINVAL;
9931da177e4SLinus Torvalds default:
9941da177e4SLinus Torvalds page_order = 0;
9951da177e4SLinus Torvalds break;
9961da177e4SLinus Torvalds }
9971da177e4SLinus Torvalds
9981da177e4SLinus Torvalds /* Do not worry about freeing memory, because if this is
9991da177e4SLinus Torvalds * called, then all agp memory is deallocated and removed
10001da177e4SLinus Torvalds * from the table. */
10011da177e4SLinus Torvalds
1002fcea424dSArjan van dev Ven #ifdef CONFIG_X86
1003fcea424dSArjan van dev Ven set_memory_wb((unsigned long)bridge->gatt_table, 1 << page_order);
1004fcea424dSArjan van dev Ven #else
10051da177e4SLinus Torvalds iounmap(bridge->gatt_table);
1006fcea424dSArjan van dev Ven #endif
10071da177e4SLinus Torvalds table = (char *) bridge->gatt_table_real;
10081da177e4SLinus Torvalds table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1);
10091da177e4SLinus Torvalds
10101da177e4SLinus Torvalds for (page = virt_to_page(table); page <= virt_to_page(table_end); page++)
10111da177e4SLinus Torvalds ClearPageReserved(page);
10121da177e4SLinus Torvalds
101307eee78eSKeir Fraser free_gatt_pages(bridge->gatt_table_real, page_order);
10141da177e4SLinus Torvalds
10151da177e4SLinus Torvalds agp_gatt_table = NULL;
10161da177e4SLinus Torvalds bridge->gatt_table = NULL;
10171da177e4SLinus Torvalds bridge->gatt_table_real = NULL;
10181da177e4SLinus Torvalds bridge->gatt_bus_addr = 0;
10191da177e4SLinus Torvalds
10201da177e4SLinus Torvalds return 0;
10211da177e4SLinus Torvalds }
10221da177e4SLinus Torvalds EXPORT_SYMBOL(agp_generic_free_gatt_table);
10231da177e4SLinus Torvalds
10241da177e4SLinus Torvalds
agp_generic_insert_memory(struct agp_memory * mem,off_t pg_start,int type)10251da177e4SLinus Torvalds int agp_generic_insert_memory(struct agp_memory * mem, off_t pg_start, int type)
10261da177e4SLinus Torvalds {
10271da177e4SLinus Torvalds int num_entries;
10281da177e4SLinus Torvalds size_t i;
10291da177e4SLinus Torvalds off_t j;
10301da177e4SLinus Torvalds void *temp;
10311da177e4SLinus Torvalds struct agp_bridge_data *bridge;
1032a030ce44SThomas Hellstrom int mask_type;
10331da177e4SLinus Torvalds
10341da177e4SLinus Torvalds bridge = mem->bridge;
10351da177e4SLinus Torvalds if (!bridge)
10361da177e4SLinus Torvalds return -EINVAL;
10371da177e4SLinus Torvalds
10385aa80c72SThomas Hellstrom if (mem->page_count == 0)
10395aa80c72SThomas Hellstrom return 0;
10405aa80c72SThomas Hellstrom
10411da177e4SLinus Torvalds temp = bridge->current_size;
10421da177e4SLinus Torvalds
10431da177e4SLinus Torvalds switch (bridge->driver->size_type) {
10441da177e4SLinus Torvalds case U8_APER_SIZE:
10451da177e4SLinus Torvalds num_entries = A_SIZE_8(temp)->num_entries;
10461da177e4SLinus Torvalds break;
10471da177e4SLinus Torvalds case U16_APER_SIZE:
10481da177e4SLinus Torvalds num_entries = A_SIZE_16(temp)->num_entries;
10491da177e4SLinus Torvalds break;
10501da177e4SLinus Torvalds case U32_APER_SIZE:
10511da177e4SLinus Torvalds num_entries = A_SIZE_32(temp)->num_entries;
10521da177e4SLinus Torvalds break;
10531da177e4SLinus Torvalds case FIXED_APER_SIZE:
10541da177e4SLinus Torvalds num_entries = A_SIZE_FIX(temp)->num_entries;
10551da177e4SLinus Torvalds break;
10561da177e4SLinus Torvalds case LVL2_APER_SIZE:
10571da177e4SLinus Torvalds /* The generic routines can't deal with 2 level gatt's */
10581da177e4SLinus Torvalds return -EINVAL;
10591da177e4SLinus Torvalds default:
10601da177e4SLinus Torvalds num_entries = 0;
10611da177e4SLinus Torvalds break;
10621da177e4SLinus Torvalds }
10631da177e4SLinus Torvalds
10641da177e4SLinus Torvalds num_entries -= agp_memory_reserved/PAGE_SIZE;
10651da177e4SLinus Torvalds if (num_entries < 0) num_entries = 0;
10661da177e4SLinus Torvalds
10671c14cfbbSAndrew Morton if (type != mem->type)
1068a030ce44SThomas Hellstrom return -EINVAL;
1069a030ce44SThomas Hellstrom
1070a030ce44SThomas Hellstrom mask_type = bridge->driver->agp_type_to_mask_type(bridge, type);
1071a030ce44SThomas Hellstrom if (mask_type != 0) {
10721da177e4SLinus Torvalds /* The generic routines know nothing of memory types */
10731da177e4SLinus Torvalds return -EINVAL;
10741da177e4SLinus Torvalds }
10751da177e4SLinus Torvalds
1076194b3da8SVasiliy Kulikov if (((pg_start + mem->page_count) > num_entries) ||
1077194b3da8SVasiliy Kulikov ((pg_start + mem->page_count) < pg_start))
10781da177e4SLinus Torvalds return -EINVAL;
10791da177e4SLinus Torvalds
10801da177e4SLinus Torvalds j = pg_start;
10811da177e4SLinus Torvalds
10821da177e4SLinus Torvalds while (j < (pg_start + mem->page_count)) {
10831da177e4SLinus Torvalds if (!PGE_EMPTY(bridge, readl(bridge->gatt_table+j)))
10841da177e4SLinus Torvalds return -EBUSY;
10851da177e4SLinus Torvalds j++;
10861da177e4SLinus Torvalds }
10871da177e4SLinus Torvalds
1088c7258012SJoe Perches if (!mem->is_flushed) {
10891da177e4SLinus Torvalds bridge->driver->cache_flush();
1090c7258012SJoe Perches mem->is_flushed = true;
10911da177e4SLinus Torvalds }
10921da177e4SLinus Torvalds
10931da177e4SLinus Torvalds for (i = 0, j = pg_start; i < mem->page_count; i++, j++) {
10942a4ceb6dSDavid Woodhouse writel(bridge->driver->mask_memory(bridge,
10956a12235cSDavid Woodhouse page_to_phys(mem->pages[i]),
10962a4ceb6dSDavid Woodhouse mask_type),
1097a030ce44SThomas Hellstrom bridge->gatt_table+j);
10981da177e4SLinus Torvalds }
10995aa80c72SThomas Hellstrom readl(bridge->gatt_table+j-1); /* PCI Posting. */
11001da177e4SLinus Torvalds
11011da177e4SLinus Torvalds bridge->driver->tlb_flush(mem);
11021da177e4SLinus Torvalds return 0;
11031da177e4SLinus Torvalds }
11041da177e4SLinus Torvalds EXPORT_SYMBOL(agp_generic_insert_memory);
11051da177e4SLinus Torvalds
11061da177e4SLinus Torvalds
agp_generic_remove_memory(struct agp_memory * mem,off_t pg_start,int type)11071da177e4SLinus Torvalds int agp_generic_remove_memory(struct agp_memory *mem, off_t pg_start, int type)
11081da177e4SLinus Torvalds {
11091da177e4SLinus Torvalds size_t i;
11101da177e4SLinus Torvalds struct agp_bridge_data *bridge;
1111194b3da8SVasiliy Kulikov int mask_type, num_entries;
11121da177e4SLinus Torvalds
11131da177e4SLinus Torvalds bridge = mem->bridge;
11141da177e4SLinus Torvalds if (!bridge)
11151da177e4SLinus Torvalds return -EINVAL;
11161da177e4SLinus Torvalds
11175aa80c72SThomas Hellstrom if (mem->page_count == 0)
11185aa80c72SThomas Hellstrom return 0;
11195aa80c72SThomas Hellstrom
1120a030ce44SThomas Hellstrom if (type != mem->type)
1121a030ce44SThomas Hellstrom return -EINVAL;
1122a030ce44SThomas Hellstrom
1123194b3da8SVasiliy Kulikov num_entries = agp_num_entries();
1124194b3da8SVasiliy Kulikov if (((pg_start + mem->page_count) > num_entries) ||
1125194b3da8SVasiliy Kulikov ((pg_start + mem->page_count) < pg_start))
1126194b3da8SVasiliy Kulikov return -EINVAL;
1127194b3da8SVasiliy Kulikov
1128a030ce44SThomas Hellstrom mask_type = bridge->driver->agp_type_to_mask_type(bridge, type);
1129a030ce44SThomas Hellstrom if (mask_type != 0) {
11301da177e4SLinus Torvalds /* The generic routines know nothing of memory types */
11311da177e4SLinus Torvalds return -EINVAL;
11321da177e4SLinus Torvalds }
11331da177e4SLinus Torvalds
11341da177e4SLinus Torvalds /* AK: bogus, should encode addresses > 4GB */
11351da177e4SLinus Torvalds for (i = pg_start; i < (mem->page_count + pg_start); i++) {
11361da177e4SLinus Torvalds writel(bridge->scratch_page, bridge->gatt_table+i);
11371da177e4SLinus Torvalds }
11385aa80c72SThomas Hellstrom readl(bridge->gatt_table+i-1); /* PCI Posting. */
11391da177e4SLinus Torvalds
11401da177e4SLinus Torvalds bridge->driver->tlb_flush(mem);
11411da177e4SLinus Torvalds return 0;
11421da177e4SLinus Torvalds }
11431da177e4SLinus Torvalds EXPORT_SYMBOL(agp_generic_remove_memory);
11441da177e4SLinus Torvalds
agp_generic_alloc_by_type(size_t page_count,int type)11451da177e4SLinus Torvalds struct agp_memory *agp_generic_alloc_by_type(size_t page_count, int type)
11461da177e4SLinus Torvalds {
11471da177e4SLinus Torvalds return NULL;
11481da177e4SLinus Torvalds }
11491da177e4SLinus Torvalds EXPORT_SYMBOL(agp_generic_alloc_by_type);
11501da177e4SLinus Torvalds
agp_generic_free_by_type(struct agp_memory * curr)11511da177e4SLinus Torvalds void agp_generic_free_by_type(struct agp_memory *curr)
11521da177e4SLinus Torvalds {
1153a030ce44SThomas Hellstrom agp_free_page_array(curr);
11541da177e4SLinus Torvalds agp_free_key(curr->key);
11551da177e4SLinus Torvalds kfree(curr);
11561da177e4SLinus Torvalds }
11571da177e4SLinus Torvalds EXPORT_SYMBOL(agp_generic_free_by_type);
11581da177e4SLinus Torvalds
agp_generic_alloc_user(size_t page_count,int type)1159a030ce44SThomas Hellstrom struct agp_memory *agp_generic_alloc_user(size_t page_count, int type)
1160a030ce44SThomas Hellstrom {
1161a030ce44SThomas Hellstrom struct agp_memory *new;
1162a030ce44SThomas Hellstrom int i;
1163a030ce44SThomas Hellstrom int pages;
1164a030ce44SThomas Hellstrom
1165a030ce44SThomas Hellstrom pages = (page_count + ENTRIES_PER_PAGE - 1) / ENTRIES_PER_PAGE;
1166a030ce44SThomas Hellstrom new = agp_create_user_memory(page_count);
1167a030ce44SThomas Hellstrom if (new == NULL)
1168a030ce44SThomas Hellstrom return NULL;
1169a030ce44SThomas Hellstrom
11701c14cfbbSAndrew Morton for (i = 0; i < page_count; i++)
117183897badSBill Pemberton new->pages[i] = NULL;
1172a030ce44SThomas Hellstrom new->page_count = 0;
1173a030ce44SThomas Hellstrom new->type = type;
1174a030ce44SThomas Hellstrom new->num_scratch_pages = pages;
1175a030ce44SThomas Hellstrom
1176a030ce44SThomas Hellstrom return new;
1177a030ce44SThomas Hellstrom }
1178a030ce44SThomas Hellstrom EXPORT_SYMBOL(agp_generic_alloc_user);
1179a030ce44SThomas Hellstrom
11801da177e4SLinus Torvalds /*
11811da177e4SLinus Torvalds * Basic Page Allocation Routines -
11821da177e4SLinus Torvalds * These routines handle page allocation and by default they reserve the allocated
11831da177e4SLinus Torvalds * memory. They also handle incrementing the current_memory_agp value, Which is checked
11841da177e4SLinus Torvalds * against a maximum value.
11851da177e4SLinus Torvalds */
11861da177e4SLinus Torvalds
agp_generic_alloc_pages(struct agp_bridge_data * bridge,struct agp_memory * mem,size_t num_pages)118737acee10SShaohua Li int agp_generic_alloc_pages(struct agp_bridge_data *bridge, struct agp_memory *mem, size_t num_pages)
118837acee10SShaohua Li {
118937acee10SShaohua Li struct page * page;
119037acee10SShaohua Li int i, ret = -ENOMEM;
119137acee10SShaohua Li
119237acee10SShaohua Li for (i = 0; i < num_pages; i++) {
119359de2bebSShaohua Li page = alloc_page(GFP_KERNEL | GFP_DMA32 | __GFP_ZERO);
119437acee10SShaohua Li /* agp_free_memory() needs gart address */
119537acee10SShaohua Li if (page == NULL)
119637acee10SShaohua Li goto out;
119737acee10SShaohua Li
119837acee10SShaohua Li #ifndef CONFIG_X86
119937acee10SShaohua Li map_page_into_agp(page);
120037acee10SShaohua Li #endif
120137acee10SShaohua Li get_page(page);
120237acee10SShaohua Li atomic_inc(&agp_bridge->current_memory_agp);
120337acee10SShaohua Li
120407613ba2SDave Airlie mem->pages[i] = page;
120537acee10SShaohua Li mem->page_count++;
120637acee10SShaohua Li }
120737acee10SShaohua Li
120837acee10SShaohua Li #ifdef CONFIG_X86
120907613ba2SDave Airlie set_pages_array_uc(mem->pages, num_pages);
121037acee10SShaohua Li #endif
121137acee10SShaohua Li ret = 0;
121237acee10SShaohua Li out:
121337acee10SShaohua Li return ret;
121437acee10SShaohua Li }
121537acee10SShaohua Li EXPORT_SYMBOL(agp_generic_alloc_pages);
121637acee10SShaohua Li
agp_generic_alloc_page(struct agp_bridge_data * bridge)121707613ba2SDave Airlie struct page *agp_generic_alloc_page(struct agp_bridge_data *bridge)
12181da177e4SLinus Torvalds {
12191da177e4SLinus Torvalds struct page * page;
12201da177e4SLinus Torvalds
122159de2bebSShaohua Li page = alloc_page(GFP_KERNEL | GFP_DMA32 | __GFP_ZERO);
12221da177e4SLinus Torvalds if (page == NULL)
12231da177e4SLinus Torvalds return NULL;
12241da177e4SLinus Torvalds
12259326d61bSIngo Molnar map_page_into_agp(page);
12261da177e4SLinus Torvalds
12271da177e4SLinus Torvalds get_page(page);
12281da177e4SLinus Torvalds atomic_inc(&agp_bridge->current_memory_agp);
122907613ba2SDave Airlie return page;
12301da177e4SLinus Torvalds }
12311da177e4SLinus Torvalds EXPORT_SYMBOL(agp_generic_alloc_page);
12321da177e4SLinus Torvalds
agp_generic_destroy_pages(struct agp_memory * mem)1233bd07928cSShaohua Li void agp_generic_destroy_pages(struct agp_memory *mem)
1234bd07928cSShaohua Li {
1235bd07928cSShaohua Li int i;
1236bd07928cSShaohua Li struct page *page;
1237bd07928cSShaohua Li
1238bd07928cSShaohua Li if (!mem)
1239bd07928cSShaohua Li return;
1240bd07928cSShaohua Li
1241bd07928cSShaohua Li #ifdef CONFIG_X86
124207613ba2SDave Airlie set_pages_array_wb(mem->pages, mem->page_count);
1243bd07928cSShaohua Li #endif
1244bd07928cSShaohua Li
1245bd07928cSShaohua Li for (i = 0; i < mem->page_count; i++) {
124607613ba2SDave Airlie page = mem->pages[i];
1247bd07928cSShaohua Li
1248bd07928cSShaohua Li #ifndef CONFIG_X86
1249bd07928cSShaohua Li unmap_page_from_agp(page);
1250bd07928cSShaohua Li #endif
1251bd07928cSShaohua Li put_page(page);
125207613ba2SDave Airlie __free_page(page);
1253bd07928cSShaohua Li atomic_dec(&agp_bridge->current_memory_agp);
125407613ba2SDave Airlie mem->pages[i] = NULL;
1255bd07928cSShaohua Li }
1256bd07928cSShaohua Li }
1257bd07928cSShaohua Li EXPORT_SYMBOL(agp_generic_destroy_pages);
12581da177e4SLinus Torvalds
agp_generic_destroy_page(struct page * page,int flags)125907613ba2SDave Airlie void agp_generic_destroy_page(struct page *page, int flags)
12601da177e4SLinus Torvalds {
126107613ba2SDave Airlie if (page == NULL)
12621da177e4SLinus Torvalds return;
12631da177e4SLinus Torvalds
1264a2721e99SDave Airlie if (flags & AGP_PAGE_DESTROY_UNMAP)
12651da177e4SLinus Torvalds unmap_page_from_agp(page);
1266a2721e99SDave Airlie
1267a2721e99SDave Airlie if (flags & AGP_PAGE_DESTROY_FREE) {
12681da177e4SLinus Torvalds put_page(page);
126907613ba2SDave Airlie __free_page(page);
12701da177e4SLinus Torvalds atomic_dec(&agp_bridge->current_memory_agp);
12711da177e4SLinus Torvalds }
1272a2721e99SDave Airlie }
12731da177e4SLinus Torvalds EXPORT_SYMBOL(agp_generic_destroy_page);
12741da177e4SLinus Torvalds
12751da177e4SLinus Torvalds /* End Basic Page Allocation Routines */
12761da177e4SLinus Torvalds
12771da177e4SLinus Torvalds
12781da177e4SLinus Torvalds /**
12791da177e4SLinus Torvalds * agp_enable - initialise the agp point-to-point connection.
12801da177e4SLinus Torvalds *
12815f448266SCorentin Labbe * @bridge: an agp_bridge_data struct allocated for the AGP host bridge.
12821da177e4SLinus Torvalds * @mode: agp mode register value to configure with.
12831da177e4SLinus Torvalds */
agp_enable(struct agp_bridge_data * bridge,u32 mode)12841da177e4SLinus Torvalds void agp_enable(struct agp_bridge_data *bridge, u32 mode)
12851da177e4SLinus Torvalds {
12861da177e4SLinus Torvalds if (!bridge)
12871da177e4SLinus Torvalds return;
12881da177e4SLinus Torvalds bridge->driver->agp_enable(bridge, mode);
12891da177e4SLinus Torvalds }
12901da177e4SLinus Torvalds EXPORT_SYMBOL(agp_enable);
12911da177e4SLinus Torvalds
12921da177e4SLinus Torvalds /* When we remove the global variable agp_bridge from all drivers
12931da177e4SLinus Torvalds * then agp_alloc_bridge and agp_generic_find_bridge need to be updated
12941da177e4SLinus Torvalds */
12951da177e4SLinus Torvalds
agp_generic_find_bridge(struct pci_dev * pdev)12961da177e4SLinus Torvalds struct agp_bridge_data *agp_generic_find_bridge(struct pci_dev *pdev)
12971da177e4SLinus Torvalds {
12981da177e4SLinus Torvalds if (list_empty(&agp_bridges))
12991da177e4SLinus Torvalds return NULL;
13001da177e4SLinus Torvalds
13011da177e4SLinus Torvalds return agp_bridge;
13021da177e4SLinus Torvalds }
13031da177e4SLinus Torvalds
ipi_handler(void * null)13041da177e4SLinus Torvalds static void ipi_handler(void *null)
13051da177e4SLinus Torvalds {
13061da177e4SLinus Torvalds flush_agp_cache();
13071da177e4SLinus Torvalds }
13081da177e4SLinus Torvalds
global_cache_flush(void)13091da177e4SLinus Torvalds void global_cache_flush(void)
13101da177e4SLinus Torvalds {
1311caa75932SNadav Amit on_each_cpu(ipi_handler, NULL, 1);
13121da177e4SLinus Torvalds }
13131da177e4SLinus Torvalds EXPORT_SYMBOL(global_cache_flush);
13141da177e4SLinus Torvalds
agp_generic_mask_memory(struct agp_bridge_data * bridge,dma_addr_t addr,int type)13151da177e4SLinus Torvalds unsigned long agp_generic_mask_memory(struct agp_bridge_data *bridge,
13162a4ceb6dSDavid Woodhouse dma_addr_t addr, int type)
13171da177e4SLinus Torvalds {
13181da177e4SLinus Torvalds /* memory type is ignored in the generic routine */
13191da177e4SLinus Torvalds if (bridge->driver->masks)
13201da177e4SLinus Torvalds return addr | bridge->driver->masks[0].mask;
13211da177e4SLinus Torvalds else
13221da177e4SLinus Torvalds return addr;
13231da177e4SLinus Torvalds }
13241da177e4SLinus Torvalds EXPORT_SYMBOL(agp_generic_mask_memory);
13251da177e4SLinus Torvalds
agp_generic_type_to_mask_type(struct agp_bridge_data * bridge,int type)1326a030ce44SThomas Hellstrom int agp_generic_type_to_mask_type(struct agp_bridge_data *bridge,
1327a030ce44SThomas Hellstrom int type)
1328a030ce44SThomas Hellstrom {
1329a030ce44SThomas Hellstrom if (type >= AGP_USER_TYPES)
1330a030ce44SThomas Hellstrom return 0;
1331a030ce44SThomas Hellstrom return type;
1332a030ce44SThomas Hellstrom }
1333a030ce44SThomas Hellstrom EXPORT_SYMBOL(agp_generic_type_to_mask_type);
1334a030ce44SThomas Hellstrom
13351da177e4SLinus Torvalds /*
13361da177e4SLinus Torvalds * These functions are implemented according to the AGPv3 spec,
13371da177e4SLinus Torvalds * which covers implementation details that had previously been
13381da177e4SLinus Torvalds * left open.
13391da177e4SLinus Torvalds */
13401da177e4SLinus Torvalds
agp3_generic_fetch_size(void)13411da177e4SLinus Torvalds int agp3_generic_fetch_size(void)
13421da177e4SLinus Torvalds {
13431da177e4SLinus Torvalds u16 temp_size;
13441da177e4SLinus Torvalds int i;
13451da177e4SLinus Torvalds struct aper_size_info_16 *values;
13461da177e4SLinus Torvalds
13471da177e4SLinus Torvalds pci_read_config_word(agp_bridge->dev, agp_bridge->capndx+AGPAPSIZE, &temp_size);
13481da177e4SLinus Torvalds values = A_SIZE_16(agp_bridge->driver->aperture_sizes);
13491da177e4SLinus Torvalds
13501da177e4SLinus Torvalds for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++) {
13511da177e4SLinus Torvalds if (temp_size == values[i].size_value) {
13521da177e4SLinus Torvalds agp_bridge->previous_size =
13531da177e4SLinus Torvalds agp_bridge->current_size = (void *) (values + i);
13541da177e4SLinus Torvalds
13551da177e4SLinus Torvalds agp_bridge->aperture_size_idx = i;
13561da177e4SLinus Torvalds return values[i].size;
13571da177e4SLinus Torvalds }
13581da177e4SLinus Torvalds }
13591da177e4SLinus Torvalds return 0;
13601da177e4SLinus Torvalds }
13611da177e4SLinus Torvalds EXPORT_SYMBOL(agp3_generic_fetch_size);
13621da177e4SLinus Torvalds
agp3_generic_tlbflush(struct agp_memory * mem)13631da177e4SLinus Torvalds void agp3_generic_tlbflush(struct agp_memory *mem)
13641da177e4SLinus Torvalds {
13651da177e4SLinus Torvalds u32 ctrl;
13661da177e4SLinus Torvalds pci_read_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, &ctrl);
13671da177e4SLinus Torvalds pci_write_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, ctrl & ~AGPCTRL_GTLBEN);
13681da177e4SLinus Torvalds pci_write_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, ctrl);
13691da177e4SLinus Torvalds }
13701da177e4SLinus Torvalds EXPORT_SYMBOL(agp3_generic_tlbflush);
13711da177e4SLinus Torvalds
agp3_generic_configure(void)13721da177e4SLinus Torvalds int agp3_generic_configure(void)
13731da177e4SLinus Torvalds {
13741da177e4SLinus Torvalds u32 temp;
13751da177e4SLinus Torvalds struct aper_size_info_16 *current_size;
13761da177e4SLinus Torvalds
13771da177e4SLinus Torvalds current_size = A_SIZE_16(agp_bridge->current_size);
13781da177e4SLinus Torvalds
1379e501b3d8SBjorn Helgaas agp_bridge->gart_bus_addr = pci_bus_address(agp_bridge->dev,
1380e501b3d8SBjorn Helgaas AGP_APERTURE_BAR);
13811da177e4SLinus Torvalds
13821da177e4SLinus Torvalds /* set aperture size */
13831da177e4SLinus Torvalds pci_write_config_word(agp_bridge->dev, agp_bridge->capndx+AGPAPSIZE, current_size->size_value);
13841da177e4SLinus Torvalds /* set gart pointer */
13851da177e4SLinus Torvalds pci_write_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPGARTLO, agp_bridge->gatt_bus_addr);
13861da177e4SLinus Torvalds /* enable aperture and GTLB */
13871da177e4SLinus Torvalds pci_read_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, &temp);
13881da177e4SLinus Torvalds pci_write_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, temp | AGPCTRL_APERENB | AGPCTRL_GTLBEN);
13891da177e4SLinus Torvalds return 0;
13901da177e4SLinus Torvalds }
13911da177e4SLinus Torvalds EXPORT_SYMBOL(agp3_generic_configure);
13921da177e4SLinus Torvalds
agp3_generic_cleanup(void)13931da177e4SLinus Torvalds void agp3_generic_cleanup(void)
13941da177e4SLinus Torvalds {
13951da177e4SLinus Torvalds u32 ctrl;
13961da177e4SLinus Torvalds pci_read_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, &ctrl);
13971da177e4SLinus Torvalds pci_write_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, ctrl & ~AGPCTRL_APERENB);
13981da177e4SLinus Torvalds }
13991da177e4SLinus Torvalds EXPORT_SYMBOL(agp3_generic_cleanup);
14001da177e4SLinus Torvalds
1401e5524f35SDave Jones const struct aper_size_info_16 agp3_generic_sizes[AGP_GENERIC_SIZES_ENTRIES] =
14021da177e4SLinus Torvalds {
14031da177e4SLinus Torvalds {4096, 1048576, 10,0x000},
14041da177e4SLinus Torvalds {2048, 524288, 9, 0x800},
14051da177e4SLinus Torvalds {1024, 262144, 8, 0xc00},
14061da177e4SLinus Torvalds { 512, 131072, 7, 0xe00},
14071da177e4SLinus Torvalds { 256, 65536, 6, 0xf00},
14081da177e4SLinus Torvalds { 128, 32768, 5, 0xf20},
14091da177e4SLinus Torvalds { 64, 16384, 4, 0xf30},
14101da177e4SLinus Torvalds { 32, 8192, 3, 0xf38},
14111da177e4SLinus Torvalds { 16, 4096, 2, 0xf3c},
14121da177e4SLinus Torvalds { 8, 2048, 1, 0xf3e},
14131da177e4SLinus Torvalds { 4, 1024, 0, 0xf3f}
14141da177e4SLinus Torvalds };
14151da177e4SLinus Torvalds EXPORT_SYMBOL(agp3_generic_sizes);
14161da177e4SLinus Torvalds
1417