| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | * AGPGART driver. | 
|  | 3 | * Copyright (C) 2004 Silicon Graphics, Inc. | 
|  | 4 | * Copyright (C) 2002-2005 Dave Jones. | 
|  | 5 | * Copyright (C) 1999 Jeff Hartmann. | 
|  | 6 | * Copyright (C) 1999 Precision Insight, Inc. | 
|  | 7 | * Copyright (C) 1999 Xi Graphics, Inc. | 
|  | 8 | * | 
|  | 9 | * Permission is hereby granted, free of charge, to any person obtaining a | 
|  | 10 | * copy of this software and associated documentation files (the "Software"), | 
|  | 11 | * to deal in the Software without restriction, including without limitation | 
|  | 12 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | 
|  | 13 | * and/or sell copies of the Software, and to permit persons to whom the | 
|  | 14 | * Software is furnished to do so, subject to the following conditions: | 
|  | 15 | * | 
|  | 16 | * The above copyright notice and this permission notice shall be included | 
|  | 17 | * in all copies or substantial portions of the Software. | 
|  | 18 | * | 
|  | 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | 
|  | 20 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
|  | 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL | 
|  | 22 | * JEFF HARTMANN, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM, | 
|  | 23 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | 
|  | 24 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE | 
|  | 25 | * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | 
|  | 26 | * | 
|  | 27 | * TODO: | 
|  | 28 | * - Allocate more than order 0 pages to avoid too much linear map splitting. | 
|  | 29 | */ | 
|  | 30 | #include <linux/config.h> | 
|  | 31 | #include <linux/module.h> | 
|  | 32 | #include <linux/pci.h> | 
|  | 33 | #include <linux/init.h> | 
|  | 34 | #include <linux/pagemap.h> | 
|  | 35 | #include <linux/miscdevice.h> | 
|  | 36 | #include <linux/pm.h> | 
|  | 37 | #include <linux/agp_backend.h> | 
|  | 38 | #include <linux/vmalloc.h> | 
|  | 39 | #include <linux/dma-mapping.h> | 
|  | 40 | #include <linux/mm.h> | 
|  | 41 | #include <asm/io.h> | 
|  | 42 | #include <asm/cacheflush.h> | 
|  | 43 | #include <asm/pgtable.h> | 
|  | 44 | #include "agp.h" | 
|  | 45 |  | 
|  | 46 | __u32 *agp_gatt_table; | 
|  | 47 | int agp_memory_reserved; | 
|  | 48 |  | 
|  | 49 | /* | 
|  | 50 | * Needed by the Nforce GART driver for the time being. Would be | 
|  | 51 | * nice to do this some other way instead of needing this export. | 
|  | 52 | */ | 
|  | 53 | EXPORT_SYMBOL_GPL(agp_memory_reserved); | 
|  | 54 |  | 
|  | 55 | #if defined(CONFIG_X86) | 
|  | 56 | int map_page_into_agp(struct page *page) | 
|  | 57 | { | 
|  | 58 | int i; | 
|  | 59 | i = change_page_attr(page, 1, PAGE_KERNEL_NOCACHE); | 
| Alan Hourihane | 88d5196 | 2005-11-06 23:35:34 -0800 | [diff] [blame] | 60 | /* Caller's responsibility to call global_flush_tlb() for | 
|  | 61 | * performance reasons */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 62 | return i; | 
|  | 63 | } | 
|  | 64 | EXPORT_SYMBOL_GPL(map_page_into_agp); | 
|  | 65 |  | 
|  | 66 | int unmap_page_from_agp(struct page *page) | 
|  | 67 | { | 
|  | 68 | int i; | 
|  | 69 | i = change_page_attr(page, 1, PAGE_KERNEL); | 
| Alan Hourihane | 88d5196 | 2005-11-06 23:35:34 -0800 | [diff] [blame] | 70 | /* Caller's responsibility to call global_flush_tlb() for | 
|  | 71 | * performance reasons */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 72 | return i; | 
|  | 73 | } | 
|  | 74 | EXPORT_SYMBOL_GPL(unmap_page_from_agp); | 
|  | 75 | #endif | 
|  | 76 |  | 
|  | 77 | /* | 
|  | 78 | * Generic routines for handling agp_memory structures - | 
|  | 79 | * They use the basic page allocation routines to do the brunt of the work. | 
|  | 80 | */ | 
|  | 81 |  | 
|  | 82 | void agp_free_key(int key) | 
|  | 83 | { | 
|  | 84 | if (key < 0) | 
|  | 85 | return; | 
|  | 86 |  | 
|  | 87 | if (key < MAXKEY) | 
|  | 88 | clear_bit(key, agp_bridge->key_list); | 
|  | 89 | } | 
|  | 90 | EXPORT_SYMBOL(agp_free_key); | 
|  | 91 |  | 
|  | 92 |  | 
|  | 93 | static int agp_get_key(void) | 
|  | 94 | { | 
|  | 95 | int bit; | 
|  | 96 |  | 
|  | 97 | bit = find_first_zero_bit(agp_bridge->key_list, MAXKEY); | 
|  | 98 | if (bit < MAXKEY) { | 
|  | 99 | set_bit(bit, agp_bridge->key_list); | 
|  | 100 | return bit; | 
|  | 101 | } | 
|  | 102 | return -1; | 
|  | 103 | } | 
|  | 104 |  | 
|  | 105 |  | 
|  | 106 | struct agp_memory *agp_create_memory(int scratch_pages) | 
|  | 107 | { | 
|  | 108 | struct agp_memory *new; | 
|  | 109 |  | 
| Dave Jones | 0ea27d9 | 2005-10-20 15:12:16 -0700 | [diff] [blame] | 110 | new = kzalloc(sizeof(struct agp_memory), GFP_KERNEL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 111 | if (new == NULL) | 
|  | 112 | return NULL; | 
|  | 113 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 114 | new->key = agp_get_key(); | 
|  | 115 |  | 
|  | 116 | if (new->key < 0) { | 
|  | 117 | kfree(new); | 
|  | 118 | return NULL; | 
|  | 119 | } | 
|  | 120 | new->memory = vmalloc(PAGE_SIZE * scratch_pages); | 
|  | 121 |  | 
|  | 122 | if (new->memory == NULL) { | 
|  | 123 | agp_free_key(new->key); | 
|  | 124 | kfree(new); | 
|  | 125 | return NULL; | 
|  | 126 | } | 
|  | 127 | new->num_scratch_pages = scratch_pages; | 
|  | 128 | return new; | 
|  | 129 | } | 
|  | 130 | EXPORT_SYMBOL(agp_create_memory); | 
|  | 131 |  | 
|  | 132 | /** | 
|  | 133 | *	agp_free_memory - free memory associated with an agp_memory pointer. | 
|  | 134 | * | 
|  | 135 | *	@curr:		agp_memory pointer to be freed. | 
|  | 136 | * | 
|  | 137 | *	It is the only function that can be called when the backend is not owned | 
|  | 138 | *	by the caller.  (So it can free memory on client death.) | 
|  | 139 | */ | 
|  | 140 | void agp_free_memory(struct agp_memory *curr) | 
|  | 141 | { | 
|  | 142 | size_t i; | 
|  | 143 |  | 
|  | 144 | if (curr == NULL) | 
|  | 145 | return; | 
|  | 146 |  | 
|  | 147 | if (curr->is_bound == TRUE) | 
|  | 148 | agp_unbind_memory(curr); | 
|  | 149 |  | 
|  | 150 | if (curr->type != 0) { | 
|  | 151 | curr->bridge->driver->free_by_type(curr); | 
|  | 152 | return; | 
|  | 153 | } | 
|  | 154 | if (curr->page_count != 0) { | 
|  | 155 | for (i = 0; i < curr->page_count; i++) { | 
| Keir Fraser | 07eee78 | 2005-03-30 13:17:04 -0800 | [diff] [blame] | 156 | curr->bridge->driver->agp_destroy_page(gart_to_virt(curr->memory[i])); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 157 | } | 
| Linus Torvalds | 6730c3c | 2005-11-09 14:56:00 -0800 | [diff] [blame] | 158 | flush_agp_mappings(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 159 | } | 
|  | 160 | agp_free_key(curr->key); | 
|  | 161 | vfree(curr->memory); | 
|  | 162 | kfree(curr); | 
|  | 163 | } | 
|  | 164 | EXPORT_SYMBOL(agp_free_memory); | 
|  | 165 |  | 
|  | 166 | #define ENTRIES_PER_PAGE		(PAGE_SIZE / sizeof(unsigned long)) | 
|  | 167 |  | 
|  | 168 | /** | 
|  | 169 | *	agp_allocate_memory  -  allocate a group of pages of a certain type. | 
|  | 170 | * | 
|  | 171 | *	@page_count:	size_t argument of the number of pages | 
|  | 172 | *	@type:	u32 argument of the type of memory to be allocated. | 
|  | 173 | * | 
|  | 174 | *	Every agp bridge device will allow you to allocate AGP_NORMAL_MEMORY which | 
|  | 175 | *	maps to physical ram.  Any other type is device dependent. | 
|  | 176 | * | 
|  | 177 | *	It returns NULL whenever memory is unavailable. | 
|  | 178 | */ | 
|  | 179 | struct agp_memory *agp_allocate_memory(struct agp_bridge_data *bridge, | 
|  | 180 | size_t page_count, u32 type) | 
|  | 181 | { | 
|  | 182 | int scratch_pages; | 
|  | 183 | struct agp_memory *new; | 
|  | 184 | size_t i; | 
|  | 185 |  | 
|  | 186 | if (!bridge) | 
|  | 187 | return NULL; | 
|  | 188 |  | 
|  | 189 | if ((atomic_read(&bridge->current_memory_agp) + page_count) > bridge->max_memory_agp) | 
|  | 190 | return NULL; | 
|  | 191 |  | 
|  | 192 | if (type != 0) { | 
|  | 193 | new = bridge->driver->alloc_by_type(page_count, type); | 
|  | 194 | if (new) | 
|  | 195 | new->bridge = bridge; | 
|  | 196 | return new; | 
|  | 197 | } | 
|  | 198 |  | 
|  | 199 | scratch_pages = (page_count + ENTRIES_PER_PAGE - 1) / ENTRIES_PER_PAGE; | 
|  | 200 |  | 
|  | 201 | new = agp_create_memory(scratch_pages); | 
|  | 202 |  | 
|  | 203 | if (new == NULL) | 
|  | 204 | return NULL; | 
|  | 205 |  | 
|  | 206 | for (i = 0; i < page_count; i++) { | 
|  | 207 | void *addr = bridge->driver->agp_alloc_page(bridge); | 
|  | 208 |  | 
|  | 209 | if (addr == NULL) { | 
|  | 210 | agp_free_memory(new); | 
|  | 211 | return NULL; | 
|  | 212 | } | 
| Keir Fraser | 07eee78 | 2005-03-30 13:17:04 -0800 | [diff] [blame] | 213 | new->memory[i] = virt_to_gart(addr); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 214 | new->page_count++; | 
|  | 215 | } | 
| Alan Hourihane | 88d5196 | 2005-11-06 23:35:34 -0800 | [diff] [blame] | 216 | new->bridge = bridge; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 217 |  | 
|  | 218 | flush_agp_mappings(); | 
|  | 219 |  | 
|  | 220 | return new; | 
|  | 221 | } | 
|  | 222 | EXPORT_SYMBOL(agp_allocate_memory); | 
|  | 223 |  | 
|  | 224 |  | 
|  | 225 | /* End - Generic routines for handling agp_memory structures */ | 
|  | 226 |  | 
|  | 227 |  | 
|  | 228 | static int agp_return_size(void) | 
|  | 229 | { | 
|  | 230 | int current_size; | 
|  | 231 | void *temp; | 
|  | 232 |  | 
|  | 233 | temp = agp_bridge->current_size; | 
|  | 234 |  | 
|  | 235 | switch (agp_bridge->driver->size_type) { | 
|  | 236 | case U8_APER_SIZE: | 
|  | 237 | current_size = A_SIZE_8(temp)->size; | 
|  | 238 | break; | 
|  | 239 | case U16_APER_SIZE: | 
|  | 240 | current_size = A_SIZE_16(temp)->size; | 
|  | 241 | break; | 
|  | 242 | case U32_APER_SIZE: | 
|  | 243 | current_size = A_SIZE_32(temp)->size; | 
|  | 244 | break; | 
|  | 245 | case LVL2_APER_SIZE: | 
|  | 246 | current_size = A_SIZE_LVL2(temp)->size; | 
|  | 247 | break; | 
|  | 248 | case FIXED_APER_SIZE: | 
|  | 249 | current_size = A_SIZE_FIX(temp)->size; | 
|  | 250 | break; | 
|  | 251 | default: | 
|  | 252 | current_size = 0; | 
|  | 253 | break; | 
|  | 254 | } | 
|  | 255 |  | 
|  | 256 | current_size -= (agp_memory_reserved / (1024*1024)); | 
|  | 257 | if (current_size <0) | 
|  | 258 | current_size = 0; | 
|  | 259 | return current_size; | 
|  | 260 | } | 
|  | 261 |  | 
|  | 262 |  | 
|  | 263 | int agp_num_entries(void) | 
|  | 264 | { | 
|  | 265 | int num_entries; | 
|  | 266 | void *temp; | 
|  | 267 |  | 
|  | 268 | temp = agp_bridge->current_size; | 
|  | 269 |  | 
|  | 270 | switch (agp_bridge->driver->size_type) { | 
|  | 271 | case U8_APER_SIZE: | 
|  | 272 | num_entries = A_SIZE_8(temp)->num_entries; | 
|  | 273 | break; | 
|  | 274 | case U16_APER_SIZE: | 
|  | 275 | num_entries = A_SIZE_16(temp)->num_entries; | 
|  | 276 | break; | 
|  | 277 | case U32_APER_SIZE: | 
|  | 278 | num_entries = A_SIZE_32(temp)->num_entries; | 
|  | 279 | break; | 
|  | 280 | case LVL2_APER_SIZE: | 
|  | 281 | num_entries = A_SIZE_LVL2(temp)->num_entries; | 
|  | 282 | break; | 
|  | 283 | case FIXED_APER_SIZE: | 
|  | 284 | num_entries = A_SIZE_FIX(temp)->num_entries; | 
|  | 285 | break; | 
|  | 286 | default: | 
|  | 287 | num_entries = 0; | 
|  | 288 | break; | 
|  | 289 | } | 
|  | 290 |  | 
|  | 291 | num_entries -= agp_memory_reserved>>PAGE_SHIFT; | 
|  | 292 | if (num_entries<0) | 
|  | 293 | num_entries = 0; | 
|  | 294 | return num_entries; | 
|  | 295 | } | 
|  | 296 | EXPORT_SYMBOL_GPL(agp_num_entries); | 
|  | 297 |  | 
|  | 298 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 299 | /** | 
|  | 300 | *	agp_copy_info  -  copy bridge state information | 
|  | 301 | * | 
|  | 302 | *	@info:		agp_kern_info pointer.  The caller should insure that this pointer is valid. | 
|  | 303 | * | 
|  | 304 | *	This function copies information about the agp bridge device and the state of | 
|  | 305 | *	the agp backend into an agp_kern_info pointer. | 
|  | 306 | */ | 
|  | 307 | int agp_copy_info(struct agp_bridge_data *bridge, struct agp_kern_info *info) | 
|  | 308 | { | 
|  | 309 | memset(info, 0, sizeof(struct agp_kern_info)); | 
|  | 310 | if (!bridge) { | 
|  | 311 | info->chipset = NOT_SUPPORTED; | 
|  | 312 | return -EIO; | 
|  | 313 | } | 
|  | 314 |  | 
|  | 315 | info->version.major = bridge->version->major; | 
|  | 316 | info->version.minor = bridge->version->minor; | 
|  | 317 | info->chipset = SUPPORTED; | 
|  | 318 | info->device = bridge->dev; | 
| David Mosberger | 66bb8bf | 2005-04-04 13:29:43 -0700 | [diff] [blame] | 319 | if (bridge->mode & AGPSTAT_MODE_3_0) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 320 | info->mode = bridge->mode & ~AGP3_RESERVED_MASK; | 
|  | 321 | else | 
|  | 322 | info->mode = bridge->mode & ~AGP2_RESERVED_MASK; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 323 | info->aper_base = bridge->gart_bus_addr; | 
|  | 324 | info->aper_size = agp_return_size(); | 
|  | 325 | info->max_memory = bridge->max_memory_agp; | 
|  | 326 | info->current_memory = atomic_read(&bridge->current_memory_agp); | 
|  | 327 | info->cant_use_aperture = bridge->driver->cant_use_aperture; | 
|  | 328 | info->vm_ops = bridge->vm_ops; | 
|  | 329 | info->page_mask = ~0UL; | 
|  | 330 | return 0; | 
|  | 331 | } | 
|  | 332 | EXPORT_SYMBOL(agp_copy_info); | 
|  | 333 |  | 
|  | 334 | /* End - Routine to copy over information structure */ | 
|  | 335 |  | 
|  | 336 | /* | 
|  | 337 | * Routines for handling swapping of agp_memory into the GATT - | 
|  | 338 | * These routines take agp_memory and insert them into the GATT. | 
|  | 339 | * They call device specific routines to actually write to the GATT. | 
|  | 340 | */ | 
|  | 341 |  | 
|  | 342 | /** | 
|  | 343 | *	agp_bind_memory  -  Bind an agp_memory structure into the GATT. | 
|  | 344 | * | 
|  | 345 | *	@curr:		agp_memory pointer | 
|  | 346 | *	@pg_start:	an offset into the graphics aperture translation table | 
|  | 347 | * | 
|  | 348 | *	It returns -EINVAL if the pointer == NULL. | 
|  | 349 | *	It returns -EBUSY if the area of the table requested is already in use. | 
|  | 350 | */ | 
|  | 351 | int agp_bind_memory(struct agp_memory *curr, off_t pg_start) | 
|  | 352 | { | 
|  | 353 | int ret_val; | 
|  | 354 |  | 
|  | 355 | if (curr == NULL) | 
|  | 356 | return -EINVAL; | 
|  | 357 |  | 
|  | 358 | if (curr->is_bound == TRUE) { | 
| Dave Jones | 8c8b838 | 2005-08-17 23:08:11 -0700 | [diff] [blame] | 359 | printk(KERN_INFO PFX "memory %p is already bound!\n", curr); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 360 | return -EINVAL; | 
|  | 361 | } | 
|  | 362 | if (curr->is_flushed == FALSE) { | 
|  | 363 | curr->bridge->driver->cache_flush(); | 
|  | 364 | curr->is_flushed = TRUE; | 
|  | 365 | } | 
|  | 366 | ret_val = curr->bridge->driver->insert_memory(curr, pg_start, curr->type); | 
|  | 367 |  | 
|  | 368 | if (ret_val != 0) | 
|  | 369 | return ret_val; | 
|  | 370 |  | 
|  | 371 | curr->is_bound = TRUE; | 
|  | 372 | curr->pg_start = pg_start; | 
|  | 373 | return 0; | 
|  | 374 | } | 
|  | 375 | EXPORT_SYMBOL(agp_bind_memory); | 
|  | 376 |  | 
|  | 377 |  | 
|  | 378 | /** | 
|  | 379 | *	agp_unbind_memory  -  Removes an agp_memory structure from the GATT | 
|  | 380 | * | 
|  | 381 | * @curr:	agp_memory pointer to be removed from the GATT. | 
|  | 382 | * | 
|  | 383 | * It returns -EINVAL if this piece of agp_memory is not currently bound to | 
|  | 384 | * the graphics aperture translation table or if the agp_memory pointer == NULL | 
|  | 385 | */ | 
|  | 386 | int agp_unbind_memory(struct agp_memory *curr) | 
|  | 387 | { | 
|  | 388 | int ret_val; | 
|  | 389 |  | 
|  | 390 | if (curr == NULL) | 
|  | 391 | return -EINVAL; | 
|  | 392 |  | 
|  | 393 | if (curr->is_bound != TRUE) { | 
| Dave Jones | 8c8b838 | 2005-08-17 23:08:11 -0700 | [diff] [blame] | 394 | printk(KERN_INFO PFX "memory %p was not bound!\n", curr); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 395 | return -EINVAL; | 
|  | 396 | } | 
|  | 397 |  | 
|  | 398 | ret_val = curr->bridge->driver->remove_memory(curr, curr->pg_start, curr->type); | 
|  | 399 |  | 
|  | 400 | if (ret_val != 0) | 
|  | 401 | return ret_val; | 
|  | 402 |  | 
|  | 403 | curr->is_bound = FALSE; | 
|  | 404 | curr->pg_start = 0; | 
|  | 405 | return 0; | 
|  | 406 | } | 
|  | 407 | EXPORT_SYMBOL(agp_unbind_memory); | 
|  | 408 |  | 
|  | 409 | /* End - Routines for handling swapping of agp_memory into the GATT */ | 
|  | 410 |  | 
|  | 411 |  | 
|  | 412 | /* Generic Agp routines - Start */ | 
|  | 413 | static void agp_v2_parse_one(u32 *requested_mode, u32 *bridge_agpstat, u32 *vga_agpstat) | 
|  | 414 | { | 
|  | 415 | u32 tmp; | 
|  | 416 |  | 
|  | 417 | if (*requested_mode & AGP2_RESERVED_MASK) { | 
| Dave Jones | c4dd458 | 2005-11-04 15:18:56 -0800 | [diff] [blame] | 418 | printk(KERN_INFO PFX "reserved bits set (%x) in mode 0x%x. Fixed.\n", | 
|  | 419 | *requested_mode & AGP2_RESERVED_MASK, *requested_mode); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 420 | *requested_mode &= ~AGP2_RESERVED_MASK; | 
|  | 421 | } | 
|  | 422 |  | 
|  | 423 | /* Check the speed bits make sense. Only one should be set. */ | 
|  | 424 | tmp = *requested_mode & 7; | 
|  | 425 | switch (tmp) { | 
|  | 426 | case 0: | 
| Dave Jones | 8c8b838 | 2005-08-17 23:08:11 -0700 | [diff] [blame] | 427 | printk(KERN_INFO PFX "%s tried to set rate=x0. Setting to x1 mode.\n", current->comm); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 428 | *requested_mode |= AGPSTAT2_1X; | 
|  | 429 | break; | 
|  | 430 | case 1: | 
|  | 431 | case 2: | 
|  | 432 | break; | 
|  | 433 | case 3: | 
|  | 434 | *requested_mode &= ~(AGPSTAT2_1X);	/* rate=2 */ | 
|  | 435 | break; | 
|  | 436 | case 4: | 
|  | 437 | break; | 
|  | 438 | case 5: | 
|  | 439 | case 6: | 
|  | 440 | case 7: | 
|  | 441 | *requested_mode &= ~(AGPSTAT2_1X|AGPSTAT2_2X); /* rate=4*/ | 
|  | 442 | break; | 
|  | 443 | } | 
|  | 444 |  | 
|  | 445 | /* disable SBA if it's not supported */ | 
|  | 446 | if (!((*bridge_agpstat & AGPSTAT_SBA) && (*vga_agpstat & AGPSTAT_SBA) && (*requested_mode & AGPSTAT_SBA))) | 
|  | 447 | *bridge_agpstat &= ~AGPSTAT_SBA; | 
|  | 448 |  | 
|  | 449 | /* Set rate */ | 
|  | 450 | if (!((*bridge_agpstat & AGPSTAT2_4X) && (*vga_agpstat & AGPSTAT2_4X) && (*requested_mode & AGPSTAT2_4X))) | 
|  | 451 | *bridge_agpstat &= ~AGPSTAT2_4X; | 
|  | 452 |  | 
|  | 453 | if (!((*bridge_agpstat & AGPSTAT2_2X) && (*vga_agpstat & AGPSTAT2_2X) && (*requested_mode & AGPSTAT2_2X))) | 
|  | 454 | *bridge_agpstat &= ~AGPSTAT2_2X; | 
|  | 455 |  | 
|  | 456 | if (!((*bridge_agpstat & AGPSTAT2_1X) && (*vga_agpstat & AGPSTAT2_1X) && (*requested_mode & AGPSTAT2_1X))) | 
|  | 457 | *bridge_agpstat &= ~AGPSTAT2_1X; | 
|  | 458 |  | 
|  | 459 | /* Now we know what mode it should be, clear out the unwanted bits. */ | 
|  | 460 | if (*bridge_agpstat & AGPSTAT2_4X) | 
|  | 461 | *bridge_agpstat &= ~(AGPSTAT2_1X | AGPSTAT2_2X);	/* 4X */ | 
|  | 462 |  | 
|  | 463 | if (*bridge_agpstat & AGPSTAT2_2X) | 
|  | 464 | *bridge_agpstat &= ~(AGPSTAT2_1X | AGPSTAT2_4X);	/* 2X */ | 
|  | 465 |  | 
|  | 466 | if (*bridge_agpstat & AGPSTAT2_1X) | 
|  | 467 | *bridge_agpstat &= ~(AGPSTAT2_2X | AGPSTAT2_4X);	/* 1X */ | 
|  | 468 |  | 
|  | 469 | /* Apply any errata. */ | 
|  | 470 | if (agp_bridge->flags & AGP_ERRATA_FASTWRITES) | 
|  | 471 | *bridge_agpstat &= ~AGPSTAT_FW; | 
|  | 472 |  | 
|  | 473 | if (agp_bridge->flags & AGP_ERRATA_SBA) | 
|  | 474 | *bridge_agpstat &= ~AGPSTAT_SBA; | 
|  | 475 |  | 
|  | 476 | if (agp_bridge->flags & AGP_ERRATA_1X) { | 
|  | 477 | *bridge_agpstat &= ~(AGPSTAT2_2X | AGPSTAT2_4X); | 
|  | 478 | *bridge_agpstat |= AGPSTAT2_1X; | 
|  | 479 | } | 
|  | 480 |  | 
|  | 481 | /* If we've dropped down to 1X, disable fast writes. */ | 
|  | 482 | if (*bridge_agpstat & AGPSTAT2_1X) | 
|  | 483 | *bridge_agpstat &= ~AGPSTAT_FW; | 
|  | 484 | } | 
|  | 485 |  | 
|  | 486 | /* | 
|  | 487 | * requested_mode = Mode requested by (typically) X. | 
|  | 488 | * bridge_agpstat = PCI_AGP_STATUS from agp bridge. | 
|  | 489 | * vga_agpstat = PCI_AGP_STATUS from graphic card. | 
|  | 490 | */ | 
|  | 491 | static void agp_v3_parse_one(u32 *requested_mode, u32 *bridge_agpstat, u32 *vga_agpstat) | 
|  | 492 | { | 
|  | 493 | u32 origbridge=*bridge_agpstat, origvga=*vga_agpstat; | 
|  | 494 | u32 tmp; | 
|  | 495 |  | 
|  | 496 | if (*requested_mode & AGP3_RESERVED_MASK) { | 
| Dave Jones | c4dd458 | 2005-11-04 15:18:56 -0800 | [diff] [blame] | 497 | printk(KERN_INFO PFX "reserved bits set (%x) in mode 0x%x. Fixed.\n", | 
|  | 498 | *requested_mode & AGP3_RESERVED_MASK, *requested_mode); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 499 | *requested_mode &= ~AGP3_RESERVED_MASK; | 
|  | 500 | } | 
|  | 501 |  | 
|  | 502 | /* Check the speed bits make sense. */ | 
|  | 503 | tmp = *requested_mode & 7; | 
|  | 504 | if (tmp == 0) { | 
| Dave Jones | 8c8b838 | 2005-08-17 23:08:11 -0700 | [diff] [blame] | 505 | printk(KERN_INFO PFX "%s tried to set rate=x0. Setting to AGP3 x4 mode.\n", current->comm); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 506 | *requested_mode |= AGPSTAT3_4X; | 
|  | 507 | } | 
|  | 508 | if (tmp >= 3) { | 
| Dave Jones | 8c8b838 | 2005-08-17 23:08:11 -0700 | [diff] [blame] | 509 | printk(KERN_INFO PFX "%s tried to set rate=x%d. Setting to AGP3 x8 mode.\n", current->comm, tmp * 4); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 510 | *requested_mode = (*requested_mode & ~7) | AGPSTAT3_8X; | 
|  | 511 | } | 
|  | 512 |  | 
|  | 513 | /* ARQSZ - Set the value to the maximum one. | 
|  | 514 | * Don't allow the mode register to override values. */ | 
|  | 515 | *bridge_agpstat = ((*bridge_agpstat & ~AGPSTAT_ARQSZ) | | 
|  | 516 | max_t(u32,(*bridge_agpstat & AGPSTAT_ARQSZ),(*vga_agpstat & AGPSTAT_ARQSZ))); | 
|  | 517 |  | 
|  | 518 | /* Calibration cycle. | 
|  | 519 | * Don't allow the mode register to override values. */ | 
|  | 520 | *bridge_agpstat = ((*bridge_agpstat & ~AGPSTAT_CAL_MASK) | | 
|  | 521 | min_t(u32,(*bridge_agpstat & AGPSTAT_CAL_MASK),(*vga_agpstat & AGPSTAT_CAL_MASK))); | 
|  | 522 |  | 
|  | 523 | /* SBA *must* be supported for AGP v3 */ | 
|  | 524 | *bridge_agpstat |= AGPSTAT_SBA; | 
|  | 525 |  | 
|  | 526 | /* | 
|  | 527 | * Set speed. | 
|  | 528 | * Check for invalid speeds. This can happen when applications | 
|  | 529 | * written before the AGP 3.0 standard pass AGP2.x modes to AGP3 hardware | 
|  | 530 | */ | 
|  | 531 | if (*requested_mode & AGPSTAT_MODE_3_0) { | 
|  | 532 | /* | 
|  | 533 | * Caller hasn't a clue what it is doing. Bridge is in 3.0 mode, | 
|  | 534 | * have been passed a 3.0 mode, but with 2.x speed bits set. | 
|  | 535 | * AGP2.x 4x -> AGP3.0 4x. | 
|  | 536 | */ | 
|  | 537 | if (*requested_mode & AGPSTAT2_4X) { | 
| Dave Jones | 8c8b838 | 2005-08-17 23:08:11 -0700 | [diff] [blame] | 538 | printk(KERN_INFO PFX "%s passes broken AGP3 flags (%x). Fixed.\n", | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 539 | current->comm, *requested_mode); | 
|  | 540 | *requested_mode &= ~AGPSTAT2_4X; | 
|  | 541 | *requested_mode |= AGPSTAT3_4X; | 
|  | 542 | } | 
|  | 543 | } else { | 
|  | 544 | /* | 
|  | 545 | * The caller doesn't know what they are doing. We are in 3.0 mode, | 
|  | 546 | * but have been passed an AGP 2.x mode. | 
|  | 547 | * Convert AGP 1x,2x,4x -> AGP 3.0 4x. | 
|  | 548 | */ | 
| Dave Jones | 8c8b838 | 2005-08-17 23:08:11 -0700 | [diff] [blame] | 549 | printk(KERN_INFO PFX "%s passes broken AGP2 flags (%x) in AGP3 mode. Fixed.\n", | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 550 | current->comm, *requested_mode); | 
|  | 551 | *requested_mode &= ~(AGPSTAT2_4X | AGPSTAT2_2X | AGPSTAT2_1X); | 
|  | 552 | *requested_mode |= AGPSTAT3_4X; | 
|  | 553 | } | 
|  | 554 |  | 
|  | 555 | if (*requested_mode & AGPSTAT3_8X) { | 
|  | 556 | if (!(*bridge_agpstat & AGPSTAT3_8X)) { | 
|  | 557 | *bridge_agpstat &= ~(AGPSTAT3_8X | AGPSTAT3_RSVD); | 
|  | 558 | *bridge_agpstat |= AGPSTAT3_4X; | 
| Dave Jones | 8c8b838 | 2005-08-17 23:08:11 -0700 | [diff] [blame] | 559 | printk(KERN_INFO PFX "%s requested AGPx8 but bridge not capable.\n", current->comm); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 560 | return; | 
|  | 561 | } | 
|  | 562 | if (!(*vga_agpstat & AGPSTAT3_8X)) { | 
|  | 563 | *bridge_agpstat &= ~(AGPSTAT3_8X | AGPSTAT3_RSVD); | 
|  | 564 | *bridge_agpstat |= AGPSTAT3_4X; | 
| Dave Jones | 8c8b838 | 2005-08-17 23:08:11 -0700 | [diff] [blame] | 565 | printk(KERN_INFO PFX "%s requested AGPx8 but graphic card not capable.\n", current->comm); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 566 | return; | 
|  | 567 | } | 
|  | 568 | /* All set, bridge & device can do AGP x8*/ | 
|  | 569 | *bridge_agpstat &= ~(AGPSTAT3_4X | AGPSTAT3_RSVD); | 
|  | 570 | goto done; | 
|  | 571 |  | 
|  | 572 | } else { | 
|  | 573 |  | 
|  | 574 | /* | 
|  | 575 | * If we didn't specify AGPx8, we can only do x4. | 
|  | 576 | * If the hardware can't do x4, we're up shit creek, and never | 
|  | 577 | *  should have got this far. | 
|  | 578 | */ | 
|  | 579 | *bridge_agpstat &= ~(AGPSTAT3_8X | AGPSTAT3_RSVD); | 
|  | 580 | if ((*bridge_agpstat & AGPSTAT3_4X) && (*vga_agpstat & AGPSTAT3_4X)) | 
|  | 581 | *bridge_agpstat |= AGPSTAT3_4X; | 
|  | 582 | else { | 
| Dave Jones | 8c8b838 | 2005-08-17 23:08:11 -0700 | [diff] [blame] | 583 | printk(KERN_INFO PFX "Badness. Don't know which AGP mode to set. " | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 584 | "[bridge_agpstat:%x vga_agpstat:%x fell back to:- bridge_agpstat:%x vga_agpstat:%x]\n", | 
|  | 585 | origbridge, origvga, *bridge_agpstat, *vga_agpstat); | 
|  | 586 | if (!(*bridge_agpstat & AGPSTAT3_4X)) | 
| Dave Jones | 8c8b838 | 2005-08-17 23:08:11 -0700 | [diff] [blame] | 587 | printk(KERN_INFO PFX "Bridge couldn't do AGP x4.\n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 588 | if (!(*vga_agpstat & AGPSTAT3_4X)) | 
| Dave Jones | 8c8b838 | 2005-08-17 23:08:11 -0700 | [diff] [blame] | 589 | printk(KERN_INFO PFX "Graphic card couldn't do AGP x4.\n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 590 | return; | 
|  | 591 | } | 
|  | 592 | } | 
|  | 593 |  | 
|  | 594 | done: | 
|  | 595 | /* Apply any errata. */ | 
|  | 596 | if (agp_bridge->flags & AGP_ERRATA_FASTWRITES) | 
|  | 597 | *bridge_agpstat &= ~AGPSTAT_FW; | 
|  | 598 |  | 
|  | 599 | if (agp_bridge->flags & AGP_ERRATA_SBA) | 
|  | 600 | *bridge_agpstat &= ~AGPSTAT_SBA; | 
|  | 601 |  | 
|  | 602 | if (agp_bridge->flags & AGP_ERRATA_1X) { | 
|  | 603 | *bridge_agpstat &= ~(AGPSTAT2_2X | AGPSTAT2_4X); | 
|  | 604 | *bridge_agpstat |= AGPSTAT2_1X; | 
|  | 605 | } | 
|  | 606 | } | 
|  | 607 |  | 
|  | 608 |  | 
|  | 609 | /** | 
|  | 610 | * agp_collect_device_status - determine correct agp_cmd from various agp_stat's | 
|  | 611 | * @bridge: an agp_bridge_data struct allocated for the AGP host bridge. | 
|  | 612 | * @requested_mode: requested agp_stat from userspace (Typically from X) | 
|  | 613 | * @bridge_agpstat: current agp_stat from AGP bridge. | 
|  | 614 | * | 
|  | 615 | * This function will hunt for an AGP graphics card, and try to match | 
|  | 616 | * the requested mode to the capabilities of both the bridge and the card. | 
|  | 617 | */ | 
|  | 618 | u32 agp_collect_device_status(struct agp_bridge_data *bridge, u32 requested_mode, u32 bridge_agpstat) | 
|  | 619 | { | 
|  | 620 | struct pci_dev *device = NULL; | 
|  | 621 | u32 vga_agpstat; | 
|  | 622 | u8 cap_ptr; | 
|  | 623 |  | 
|  | 624 | for (;;) { | 
|  | 625 | device = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, device); | 
|  | 626 | if (!device) { | 
| Dave Jones | 8c8b838 | 2005-08-17 23:08:11 -0700 | [diff] [blame] | 627 | printk(KERN_INFO PFX "Couldn't find an AGP VGA controller.\n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 628 | return 0; | 
|  | 629 | } | 
|  | 630 | cap_ptr = pci_find_capability(device, PCI_CAP_ID_AGP); | 
|  | 631 | if (cap_ptr) | 
|  | 632 | break; | 
|  | 633 | } | 
|  | 634 |  | 
|  | 635 | /* | 
|  | 636 | * Ok, here we have a AGP device. Disable impossible | 
|  | 637 | * settings, and adjust the readqueue to the minimum. | 
|  | 638 | */ | 
|  | 639 | pci_read_config_dword(device, cap_ptr+PCI_AGP_STATUS, &vga_agpstat); | 
|  | 640 |  | 
|  | 641 | /* adjust RQ depth */ | 
|  | 642 | bridge_agpstat = ((bridge_agpstat & ~AGPSTAT_RQ_DEPTH) | | 
|  | 643 | min_t(u32, (requested_mode & AGPSTAT_RQ_DEPTH), | 
|  | 644 | min_t(u32, (bridge_agpstat & AGPSTAT_RQ_DEPTH), (vga_agpstat & AGPSTAT_RQ_DEPTH)))); | 
|  | 645 |  | 
|  | 646 | /* disable FW if it's not supported */ | 
|  | 647 | if (!((bridge_agpstat & AGPSTAT_FW) && | 
|  | 648 | (vga_agpstat & AGPSTAT_FW) && | 
|  | 649 | (requested_mode & AGPSTAT_FW))) | 
|  | 650 | bridge_agpstat &= ~AGPSTAT_FW; | 
|  | 651 |  | 
|  | 652 | /* Check to see if we are operating in 3.0 mode */ | 
| David Mosberger | 66bb8bf | 2005-04-04 13:29:43 -0700 | [diff] [blame] | 653 | if (agp_bridge->mode & AGPSTAT_MODE_3_0) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 654 | agp_v3_parse_one(&requested_mode, &bridge_agpstat, &vga_agpstat); | 
|  | 655 | else | 
|  | 656 | agp_v2_parse_one(&requested_mode, &bridge_agpstat, &vga_agpstat); | 
|  | 657 |  | 
|  | 658 | pci_dev_put(device); | 
|  | 659 | return bridge_agpstat; | 
|  | 660 | } | 
|  | 661 | EXPORT_SYMBOL(agp_collect_device_status); | 
|  | 662 |  | 
|  | 663 |  | 
|  | 664 | void agp_device_command(u32 bridge_agpstat, int agp_v3) | 
|  | 665 | { | 
|  | 666 | struct pci_dev *device = NULL; | 
|  | 667 | int mode; | 
|  | 668 |  | 
|  | 669 | mode = bridge_agpstat & 0x7; | 
|  | 670 | if (agp_v3) | 
|  | 671 | mode *= 4; | 
|  | 672 |  | 
|  | 673 | for_each_pci_dev(device) { | 
|  | 674 | u8 agp = pci_find_capability(device, PCI_CAP_ID_AGP); | 
|  | 675 | if (!agp) | 
|  | 676 | continue; | 
|  | 677 |  | 
|  | 678 | printk(KERN_INFO PFX "Putting AGP V%d device at %s into %dx mode\n", | 
|  | 679 | agp_v3 ? 3 : 2, pci_name(device), mode); | 
|  | 680 | pci_write_config_dword(device, agp + PCI_AGP_COMMAND, bridge_agpstat); | 
|  | 681 | } | 
|  | 682 | } | 
|  | 683 | EXPORT_SYMBOL(agp_device_command); | 
|  | 684 |  | 
|  | 685 |  | 
|  | 686 | void get_agp_version(struct agp_bridge_data *bridge) | 
|  | 687 | { | 
|  | 688 | u32 ncapid; | 
|  | 689 |  | 
|  | 690 | /* Exit early if already set by errata workarounds. */ | 
|  | 691 | if (bridge->major_version != 0) | 
|  | 692 | return; | 
|  | 693 |  | 
|  | 694 | pci_read_config_dword(bridge->dev, bridge->capndx, &ncapid); | 
|  | 695 | bridge->major_version = (ncapid >> AGP_MAJOR_VERSION_SHIFT) & 0xf; | 
|  | 696 | bridge->minor_version = (ncapid >> AGP_MINOR_VERSION_SHIFT) & 0xf; | 
|  | 697 | } | 
|  | 698 | EXPORT_SYMBOL(get_agp_version); | 
|  | 699 |  | 
|  | 700 |  | 
|  | 701 | void agp_generic_enable(struct agp_bridge_data *bridge, u32 requested_mode) | 
|  | 702 | { | 
|  | 703 | u32 bridge_agpstat, temp; | 
|  | 704 |  | 
|  | 705 | get_agp_version(agp_bridge); | 
|  | 706 |  | 
|  | 707 | printk(KERN_INFO PFX "Found an AGP %d.%d compliant device at %s.\n", | 
|  | 708 | agp_bridge->major_version, | 
|  | 709 | agp_bridge->minor_version, | 
|  | 710 | pci_name(agp_bridge->dev)); | 
|  | 711 |  | 
|  | 712 | pci_read_config_dword(agp_bridge->dev, | 
|  | 713 | agp_bridge->capndx + PCI_AGP_STATUS, &bridge_agpstat); | 
|  | 714 |  | 
|  | 715 | bridge_agpstat = agp_collect_device_status(agp_bridge, requested_mode, bridge_agpstat); | 
|  | 716 | if (bridge_agpstat == 0) | 
|  | 717 | /* Something bad happened. FIXME: Return error code? */ | 
|  | 718 | return; | 
|  | 719 |  | 
|  | 720 | bridge_agpstat |= AGPSTAT_AGP_ENABLE; | 
|  | 721 |  | 
|  | 722 | /* Do AGP version specific frobbing. */ | 
|  | 723 | if (bridge->major_version >= 3) { | 
| David Mosberger | 66bb8bf | 2005-04-04 13:29:43 -0700 | [diff] [blame] | 724 | if (bridge->mode & AGPSTAT_MODE_3_0) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 725 | /* If we have 3.5, we can do the isoch stuff. */ | 
|  | 726 | if (bridge->minor_version >= 5) | 
|  | 727 | agp_3_5_enable(bridge); | 
|  | 728 | agp_device_command(bridge_agpstat, TRUE); | 
|  | 729 | return; | 
|  | 730 | } else { | 
|  | 731 | /* Disable calibration cycle in RX91<1> when not in AGP3.0 mode of operation.*/ | 
|  | 732 | bridge_agpstat &= ~(7<<10) ; | 
|  | 733 | pci_read_config_dword(bridge->dev, | 
|  | 734 | bridge->capndx+AGPCTRL, &temp); | 
|  | 735 | temp |= (1<<9); | 
|  | 736 | pci_write_config_dword(bridge->dev, | 
|  | 737 | bridge->capndx+AGPCTRL, temp); | 
|  | 738 |  | 
| Dave Jones | 8c8b838 | 2005-08-17 23:08:11 -0700 | [diff] [blame] | 739 | printk(KERN_INFO PFX "Device is in legacy mode," | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 740 | " falling back to 2.x\n"); | 
|  | 741 | } | 
|  | 742 | } | 
|  | 743 |  | 
|  | 744 | /* AGP v<3 */ | 
|  | 745 | agp_device_command(bridge_agpstat, FALSE); | 
|  | 746 | } | 
|  | 747 | EXPORT_SYMBOL(agp_generic_enable); | 
|  | 748 |  | 
|  | 749 |  | 
|  | 750 | int agp_generic_create_gatt_table(struct agp_bridge_data *bridge) | 
|  | 751 | { | 
|  | 752 | char *table; | 
|  | 753 | char *table_end; | 
|  | 754 | int size; | 
|  | 755 | int page_order; | 
|  | 756 | int num_entries; | 
|  | 757 | int i; | 
|  | 758 | void *temp; | 
|  | 759 | struct page *page; | 
|  | 760 |  | 
|  | 761 | /* The generic routines can't handle 2 level gatt's */ | 
|  | 762 | if (bridge->driver->size_type == LVL2_APER_SIZE) | 
|  | 763 | return -EINVAL; | 
|  | 764 |  | 
|  | 765 | table = NULL; | 
|  | 766 | i = bridge->aperture_size_idx; | 
|  | 767 | temp = bridge->current_size; | 
|  | 768 | size = page_order = num_entries = 0; | 
|  | 769 |  | 
|  | 770 | if (bridge->driver->size_type != FIXED_APER_SIZE) { | 
|  | 771 | do { | 
|  | 772 | switch (bridge->driver->size_type) { | 
|  | 773 | case U8_APER_SIZE: | 
|  | 774 | size = A_SIZE_8(temp)->size; | 
|  | 775 | page_order = | 
|  | 776 | A_SIZE_8(temp)->page_order; | 
|  | 777 | num_entries = | 
|  | 778 | A_SIZE_8(temp)->num_entries; | 
|  | 779 | break; | 
|  | 780 | case U16_APER_SIZE: | 
|  | 781 | size = A_SIZE_16(temp)->size; | 
|  | 782 | page_order = A_SIZE_16(temp)->page_order; | 
|  | 783 | num_entries = A_SIZE_16(temp)->num_entries; | 
|  | 784 | break; | 
|  | 785 | case U32_APER_SIZE: | 
|  | 786 | size = A_SIZE_32(temp)->size; | 
|  | 787 | page_order = A_SIZE_32(temp)->page_order; | 
|  | 788 | num_entries = A_SIZE_32(temp)->num_entries; | 
|  | 789 | break; | 
|  | 790 | /* This case will never really happen. */ | 
|  | 791 | case FIXED_APER_SIZE: | 
|  | 792 | case LVL2_APER_SIZE: | 
|  | 793 | default: | 
|  | 794 | size = page_order = num_entries = 0; | 
|  | 795 | break; | 
|  | 796 | } | 
|  | 797 |  | 
| Keir Fraser | 07eee78 | 2005-03-30 13:17:04 -0800 | [diff] [blame] | 798 | table = alloc_gatt_pages(page_order); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 799 |  | 
|  | 800 | if (table == NULL) { | 
|  | 801 | i++; | 
|  | 802 | switch (bridge->driver->size_type) { | 
|  | 803 | case U8_APER_SIZE: | 
|  | 804 | bridge->current_size = A_IDX8(bridge); | 
|  | 805 | break; | 
|  | 806 | case U16_APER_SIZE: | 
|  | 807 | bridge->current_size = A_IDX16(bridge); | 
|  | 808 | break; | 
|  | 809 | case U32_APER_SIZE: | 
|  | 810 | bridge->current_size = A_IDX32(bridge); | 
|  | 811 | break; | 
|  | 812 | /* This case will never really happen. */ | 
|  | 813 | case FIXED_APER_SIZE: | 
|  | 814 | case LVL2_APER_SIZE: | 
|  | 815 | default: | 
|  | 816 | bridge->current_size = | 
|  | 817 | bridge->current_size; | 
|  | 818 | break; | 
|  | 819 | } | 
|  | 820 | temp = bridge->current_size; | 
|  | 821 | } else { | 
|  | 822 | bridge->aperture_size_idx = i; | 
|  | 823 | } | 
|  | 824 | } while (!table && (i < bridge->driver->num_aperture_sizes)); | 
|  | 825 | } else { | 
|  | 826 | size = ((struct aper_size_info_fixed *) temp)->size; | 
|  | 827 | page_order = ((struct aper_size_info_fixed *) temp)->page_order; | 
|  | 828 | num_entries = ((struct aper_size_info_fixed *) temp)->num_entries; | 
| Keir Fraser | 07eee78 | 2005-03-30 13:17:04 -0800 | [diff] [blame] | 829 | table = alloc_gatt_pages(page_order); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 830 | } | 
|  | 831 |  | 
|  | 832 | if (table == NULL) | 
|  | 833 | return -ENOMEM; | 
|  | 834 |  | 
|  | 835 | table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1); | 
|  | 836 |  | 
|  | 837 | for (page = virt_to_page(table); page <= virt_to_page(table_end); page++) | 
|  | 838 | SetPageReserved(page); | 
|  | 839 |  | 
|  | 840 | bridge->gatt_table_real = (u32 *) table; | 
|  | 841 | agp_gatt_table = (void *)table; | 
|  | 842 |  | 
|  | 843 | bridge->driver->cache_flush(); | 
| Keir Fraser | 07eee78 | 2005-03-30 13:17:04 -0800 | [diff] [blame] | 844 | bridge->gatt_table = ioremap_nocache(virt_to_gart(table), | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 845 | (PAGE_SIZE * (1 << page_order))); | 
|  | 846 | bridge->driver->cache_flush(); | 
|  | 847 |  | 
|  | 848 | if (bridge->gatt_table == NULL) { | 
|  | 849 | for (page = virt_to_page(table); page <= virt_to_page(table_end); page++) | 
|  | 850 | ClearPageReserved(page); | 
|  | 851 |  | 
| Keir Fraser | 07eee78 | 2005-03-30 13:17:04 -0800 | [diff] [blame] | 852 | free_gatt_pages(table, page_order); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 853 |  | 
|  | 854 | return -ENOMEM; | 
|  | 855 | } | 
| Keir Fraser | 07eee78 | 2005-03-30 13:17:04 -0800 | [diff] [blame] | 856 | bridge->gatt_bus_addr = virt_to_gart(bridge->gatt_table_real); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 857 |  | 
|  | 858 | /* AK: bogus, should encode addresses > 4GB */ | 
|  | 859 | for (i = 0; i < num_entries; i++) { | 
|  | 860 | writel(bridge->scratch_page, bridge->gatt_table+i); | 
|  | 861 | readl(bridge->gatt_table+i);	/* PCI Posting. */ | 
|  | 862 | } | 
|  | 863 |  | 
|  | 864 | return 0; | 
|  | 865 | } | 
|  | 866 | EXPORT_SYMBOL(agp_generic_create_gatt_table); | 
|  | 867 |  | 
|  | 868 | int agp_generic_free_gatt_table(struct agp_bridge_data *bridge) | 
|  | 869 | { | 
|  | 870 | int page_order; | 
|  | 871 | char *table, *table_end; | 
|  | 872 | void *temp; | 
|  | 873 | struct page *page; | 
|  | 874 |  | 
|  | 875 | temp = bridge->current_size; | 
|  | 876 |  | 
|  | 877 | switch (bridge->driver->size_type) { | 
|  | 878 | case U8_APER_SIZE: | 
|  | 879 | page_order = A_SIZE_8(temp)->page_order; | 
|  | 880 | break; | 
|  | 881 | case U16_APER_SIZE: | 
|  | 882 | page_order = A_SIZE_16(temp)->page_order; | 
|  | 883 | break; | 
|  | 884 | case U32_APER_SIZE: | 
|  | 885 | page_order = A_SIZE_32(temp)->page_order; | 
|  | 886 | break; | 
|  | 887 | case FIXED_APER_SIZE: | 
|  | 888 | page_order = A_SIZE_FIX(temp)->page_order; | 
|  | 889 | break; | 
|  | 890 | case LVL2_APER_SIZE: | 
|  | 891 | /* The generic routines can't deal with 2 level gatt's */ | 
|  | 892 | return -EINVAL; | 
|  | 893 | break; | 
|  | 894 | default: | 
|  | 895 | page_order = 0; | 
|  | 896 | break; | 
|  | 897 | } | 
|  | 898 |  | 
|  | 899 | /* Do not worry about freeing memory, because if this is | 
|  | 900 | * called, then all agp memory is deallocated and removed | 
|  | 901 | * from the table. */ | 
|  | 902 |  | 
|  | 903 | iounmap(bridge->gatt_table); | 
|  | 904 | table = (char *) bridge->gatt_table_real; | 
|  | 905 | table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1); | 
|  | 906 |  | 
|  | 907 | for (page = virt_to_page(table); page <= virt_to_page(table_end); page++) | 
|  | 908 | ClearPageReserved(page); | 
|  | 909 |  | 
| Keir Fraser | 07eee78 | 2005-03-30 13:17:04 -0800 | [diff] [blame] | 910 | free_gatt_pages(bridge->gatt_table_real, page_order); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 911 |  | 
|  | 912 | agp_gatt_table = NULL; | 
|  | 913 | bridge->gatt_table = NULL; | 
|  | 914 | bridge->gatt_table_real = NULL; | 
|  | 915 | bridge->gatt_bus_addr = 0; | 
|  | 916 |  | 
|  | 917 | return 0; | 
|  | 918 | } | 
|  | 919 | EXPORT_SYMBOL(agp_generic_free_gatt_table); | 
|  | 920 |  | 
|  | 921 |  | 
|  | 922 | int agp_generic_insert_memory(struct agp_memory * mem, off_t pg_start, int type) | 
|  | 923 | { | 
|  | 924 | int num_entries; | 
|  | 925 | size_t i; | 
|  | 926 | off_t j; | 
|  | 927 | void *temp; | 
|  | 928 | struct agp_bridge_data *bridge; | 
|  | 929 |  | 
|  | 930 | bridge = mem->bridge; | 
|  | 931 | if (!bridge) | 
|  | 932 | return -EINVAL; | 
|  | 933 |  | 
|  | 934 | temp = bridge->current_size; | 
|  | 935 |  | 
|  | 936 | switch (bridge->driver->size_type) { | 
|  | 937 | case U8_APER_SIZE: | 
|  | 938 | num_entries = A_SIZE_8(temp)->num_entries; | 
|  | 939 | break; | 
|  | 940 | case U16_APER_SIZE: | 
|  | 941 | num_entries = A_SIZE_16(temp)->num_entries; | 
|  | 942 | break; | 
|  | 943 | case U32_APER_SIZE: | 
|  | 944 | num_entries = A_SIZE_32(temp)->num_entries; | 
|  | 945 | break; | 
|  | 946 | case FIXED_APER_SIZE: | 
|  | 947 | num_entries = A_SIZE_FIX(temp)->num_entries; | 
|  | 948 | break; | 
|  | 949 | case LVL2_APER_SIZE: | 
|  | 950 | /* The generic routines can't deal with 2 level gatt's */ | 
|  | 951 | return -EINVAL; | 
|  | 952 | break; | 
|  | 953 | default: | 
|  | 954 | num_entries = 0; | 
|  | 955 | break; | 
|  | 956 | } | 
|  | 957 |  | 
|  | 958 | num_entries -= agp_memory_reserved/PAGE_SIZE; | 
|  | 959 | if (num_entries < 0) num_entries = 0; | 
|  | 960 |  | 
|  | 961 | if (type != 0 || mem->type != 0) { | 
|  | 962 | /* The generic routines know nothing of memory types */ | 
|  | 963 | return -EINVAL; | 
|  | 964 | } | 
|  | 965 |  | 
|  | 966 | /* AK: could wrap */ | 
|  | 967 | if ((pg_start + mem->page_count) > num_entries) | 
|  | 968 | return -EINVAL; | 
|  | 969 |  | 
|  | 970 | j = pg_start; | 
|  | 971 |  | 
|  | 972 | while (j < (pg_start + mem->page_count)) { | 
|  | 973 | if (!PGE_EMPTY(bridge, readl(bridge->gatt_table+j))) | 
|  | 974 | return -EBUSY; | 
|  | 975 | j++; | 
|  | 976 | } | 
|  | 977 |  | 
|  | 978 | if (mem->is_flushed == FALSE) { | 
|  | 979 | bridge->driver->cache_flush(); | 
|  | 980 | mem->is_flushed = TRUE; | 
|  | 981 | } | 
|  | 982 |  | 
|  | 983 | for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { | 
|  | 984 | writel(bridge->driver->mask_memory(bridge, mem->memory[i], mem->type), bridge->gatt_table+j); | 
|  | 985 | readl(bridge->gatt_table+j);	/* PCI Posting. */ | 
|  | 986 | } | 
|  | 987 |  | 
|  | 988 | bridge->driver->tlb_flush(mem); | 
|  | 989 | return 0; | 
|  | 990 | } | 
|  | 991 | EXPORT_SYMBOL(agp_generic_insert_memory); | 
|  | 992 |  | 
|  | 993 |  | 
|  | 994 | int agp_generic_remove_memory(struct agp_memory *mem, off_t pg_start, int type) | 
|  | 995 | { | 
|  | 996 | size_t i; | 
|  | 997 | struct agp_bridge_data *bridge; | 
|  | 998 |  | 
|  | 999 | bridge = mem->bridge; | 
|  | 1000 | if (!bridge) | 
|  | 1001 | return -EINVAL; | 
|  | 1002 |  | 
|  | 1003 | if (type != 0 || mem->type != 0) { | 
|  | 1004 | /* The generic routines know nothing of memory types */ | 
|  | 1005 | return -EINVAL; | 
|  | 1006 | } | 
|  | 1007 |  | 
|  | 1008 | /* AK: bogus, should encode addresses > 4GB */ | 
|  | 1009 | for (i = pg_start; i < (mem->page_count + pg_start); i++) { | 
|  | 1010 | writel(bridge->scratch_page, bridge->gatt_table+i); | 
|  | 1011 | readl(bridge->gatt_table+i);	/* PCI Posting. */ | 
|  | 1012 | } | 
|  | 1013 |  | 
|  | 1014 | global_cache_flush(); | 
|  | 1015 | bridge->driver->tlb_flush(mem); | 
|  | 1016 | return 0; | 
|  | 1017 | } | 
|  | 1018 | EXPORT_SYMBOL(agp_generic_remove_memory); | 
|  | 1019 |  | 
|  | 1020 |  | 
|  | 1021 | struct agp_memory *agp_generic_alloc_by_type(size_t page_count, int type) | 
|  | 1022 | { | 
|  | 1023 | return NULL; | 
|  | 1024 | } | 
|  | 1025 | EXPORT_SYMBOL(agp_generic_alloc_by_type); | 
|  | 1026 |  | 
|  | 1027 |  | 
|  | 1028 | void agp_generic_free_by_type(struct agp_memory *curr) | 
|  | 1029 | { | 
|  | 1030 | vfree(curr->memory); | 
|  | 1031 | agp_free_key(curr->key); | 
|  | 1032 | kfree(curr); | 
|  | 1033 | } | 
|  | 1034 | EXPORT_SYMBOL(agp_generic_free_by_type); | 
|  | 1035 |  | 
|  | 1036 |  | 
|  | 1037 | /* | 
|  | 1038 | * Basic Page Allocation Routines - | 
|  | 1039 | * These routines handle page allocation and by default they reserve the allocated | 
|  | 1040 | * memory.  They also handle incrementing the current_memory_agp value, Which is checked | 
|  | 1041 | * against a maximum value. | 
|  | 1042 | */ | 
|  | 1043 |  | 
|  | 1044 | void *agp_generic_alloc_page(struct agp_bridge_data *bridge) | 
|  | 1045 | { | 
|  | 1046 | struct page * page; | 
|  | 1047 |  | 
|  | 1048 | page = alloc_page(GFP_KERNEL); | 
|  | 1049 | if (page == NULL) | 
|  | 1050 | return NULL; | 
|  | 1051 |  | 
|  | 1052 | map_page_into_agp(page); | 
|  | 1053 |  | 
|  | 1054 | get_page(page); | 
|  | 1055 | SetPageLocked(page); | 
|  | 1056 | atomic_inc(&agp_bridge->current_memory_agp); | 
|  | 1057 | return page_address(page); | 
|  | 1058 | } | 
|  | 1059 | EXPORT_SYMBOL(agp_generic_alloc_page); | 
|  | 1060 |  | 
|  | 1061 |  | 
|  | 1062 | void agp_generic_destroy_page(void *addr) | 
|  | 1063 | { | 
|  | 1064 | struct page *page; | 
|  | 1065 |  | 
|  | 1066 | if (addr == NULL) | 
|  | 1067 | return; | 
|  | 1068 |  | 
|  | 1069 | page = virt_to_page(addr); | 
|  | 1070 | unmap_page_from_agp(page); | 
|  | 1071 | put_page(page); | 
|  | 1072 | unlock_page(page); | 
|  | 1073 | free_page((unsigned long)addr); | 
|  | 1074 | atomic_dec(&agp_bridge->current_memory_agp); | 
|  | 1075 | } | 
|  | 1076 | EXPORT_SYMBOL(agp_generic_destroy_page); | 
|  | 1077 |  | 
|  | 1078 | /* End Basic Page Allocation Routines */ | 
|  | 1079 |  | 
|  | 1080 |  | 
|  | 1081 | /** | 
|  | 1082 | * agp_enable  -  initialise the agp point-to-point connection. | 
|  | 1083 | * | 
|  | 1084 | * @mode:	agp mode register value to configure with. | 
|  | 1085 | */ | 
|  | 1086 | void agp_enable(struct agp_bridge_data *bridge, u32 mode) | 
|  | 1087 | { | 
|  | 1088 | if (!bridge) | 
|  | 1089 | return; | 
|  | 1090 | bridge->driver->agp_enable(bridge, mode); | 
|  | 1091 | } | 
|  | 1092 | EXPORT_SYMBOL(agp_enable); | 
|  | 1093 |  | 
|  | 1094 | /* When we remove the global variable agp_bridge from all drivers | 
|  | 1095 | * then agp_alloc_bridge and agp_generic_find_bridge need to be updated | 
|  | 1096 | */ | 
|  | 1097 |  | 
|  | 1098 | struct agp_bridge_data *agp_generic_find_bridge(struct pci_dev *pdev) | 
|  | 1099 | { | 
|  | 1100 | if (list_empty(&agp_bridges)) | 
|  | 1101 | return NULL; | 
|  | 1102 |  | 
|  | 1103 | return agp_bridge; | 
|  | 1104 | } | 
|  | 1105 |  | 
|  | 1106 | static void ipi_handler(void *null) | 
|  | 1107 | { | 
|  | 1108 | flush_agp_cache(); | 
|  | 1109 | } | 
|  | 1110 |  | 
|  | 1111 | void global_cache_flush(void) | 
|  | 1112 | { | 
|  | 1113 | if (on_each_cpu(ipi_handler, NULL, 1, 1) != 0) | 
|  | 1114 | panic(PFX "timed out waiting for the other CPUs!\n"); | 
|  | 1115 | } | 
|  | 1116 | EXPORT_SYMBOL(global_cache_flush); | 
|  | 1117 |  | 
|  | 1118 | unsigned long agp_generic_mask_memory(struct agp_bridge_data *bridge, | 
|  | 1119 | unsigned long addr, int type) | 
|  | 1120 | { | 
|  | 1121 | /* memory type is ignored in the generic routine */ | 
|  | 1122 | if (bridge->driver->masks) | 
|  | 1123 | return addr | bridge->driver->masks[0].mask; | 
|  | 1124 | else | 
|  | 1125 | return addr; | 
|  | 1126 | } | 
|  | 1127 | EXPORT_SYMBOL(agp_generic_mask_memory); | 
|  | 1128 |  | 
|  | 1129 | /* | 
|  | 1130 | * These functions are implemented according to the AGPv3 spec, | 
|  | 1131 | * which covers implementation details that had previously been | 
|  | 1132 | * left open. | 
|  | 1133 | */ | 
|  | 1134 |  | 
|  | 1135 | int agp3_generic_fetch_size(void) | 
|  | 1136 | { | 
|  | 1137 | u16 temp_size; | 
|  | 1138 | int i; | 
|  | 1139 | struct aper_size_info_16 *values; | 
|  | 1140 |  | 
|  | 1141 | pci_read_config_word(agp_bridge->dev, agp_bridge->capndx+AGPAPSIZE, &temp_size); | 
|  | 1142 | values = A_SIZE_16(agp_bridge->driver->aperture_sizes); | 
|  | 1143 |  | 
|  | 1144 | for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++) { | 
|  | 1145 | if (temp_size == values[i].size_value) { | 
|  | 1146 | agp_bridge->previous_size = | 
|  | 1147 | agp_bridge->current_size = (void *) (values + i); | 
|  | 1148 |  | 
|  | 1149 | agp_bridge->aperture_size_idx = i; | 
|  | 1150 | return values[i].size; | 
|  | 1151 | } | 
|  | 1152 | } | 
|  | 1153 | return 0; | 
|  | 1154 | } | 
|  | 1155 | EXPORT_SYMBOL(agp3_generic_fetch_size); | 
|  | 1156 |  | 
|  | 1157 | void agp3_generic_tlbflush(struct agp_memory *mem) | 
|  | 1158 | { | 
|  | 1159 | u32 ctrl; | 
|  | 1160 | pci_read_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, &ctrl); | 
|  | 1161 | pci_write_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, ctrl & ~AGPCTRL_GTLBEN); | 
|  | 1162 | pci_write_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, ctrl); | 
|  | 1163 | } | 
|  | 1164 | EXPORT_SYMBOL(agp3_generic_tlbflush); | 
|  | 1165 |  | 
|  | 1166 | int agp3_generic_configure(void) | 
|  | 1167 | { | 
|  | 1168 | u32 temp; | 
|  | 1169 | struct aper_size_info_16 *current_size; | 
|  | 1170 |  | 
|  | 1171 | current_size = A_SIZE_16(agp_bridge->current_size); | 
|  | 1172 |  | 
|  | 1173 | pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp); | 
|  | 1174 | agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); | 
|  | 1175 |  | 
|  | 1176 | /* set aperture size */ | 
|  | 1177 | pci_write_config_word(agp_bridge->dev, agp_bridge->capndx+AGPAPSIZE, current_size->size_value); | 
|  | 1178 | /* set gart pointer */ | 
|  | 1179 | pci_write_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPGARTLO, agp_bridge->gatt_bus_addr); | 
|  | 1180 | /* enable aperture and GTLB */ | 
|  | 1181 | pci_read_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, &temp); | 
|  | 1182 | pci_write_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, temp | AGPCTRL_APERENB | AGPCTRL_GTLBEN); | 
|  | 1183 | return 0; | 
|  | 1184 | } | 
|  | 1185 | EXPORT_SYMBOL(agp3_generic_configure); | 
|  | 1186 |  | 
|  | 1187 | void agp3_generic_cleanup(void) | 
|  | 1188 | { | 
|  | 1189 | u32 ctrl; | 
|  | 1190 | pci_read_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, &ctrl); | 
|  | 1191 | pci_write_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, ctrl & ~AGPCTRL_APERENB); | 
|  | 1192 | } | 
|  | 1193 | EXPORT_SYMBOL(agp3_generic_cleanup); | 
|  | 1194 |  | 
|  | 1195 | struct aper_size_info_16 agp3_generic_sizes[AGP_GENERIC_SIZES_ENTRIES] = | 
|  | 1196 | { | 
|  | 1197 | {4096, 1048576, 10,0x000}, | 
|  | 1198 | {2048,  524288, 9, 0x800}, | 
|  | 1199 | {1024,  262144, 8, 0xc00}, | 
|  | 1200 | { 512,  131072, 7, 0xe00}, | 
|  | 1201 | { 256,   65536, 6, 0xf00}, | 
|  | 1202 | { 128,   32768, 5, 0xf20}, | 
|  | 1203 | {  64,   16384, 4, 0xf30}, | 
|  | 1204 | {  32,    8192, 3, 0xf38}, | 
|  | 1205 | {  16,    4096, 2, 0xf3c}, | 
|  | 1206 | {   8,    2048, 1, 0xf3e}, | 
|  | 1207 | {   4,    1024, 0, 0xf3f} | 
|  | 1208 | }; | 
|  | 1209 | EXPORT_SYMBOL(agp3_generic_sizes); | 
|  | 1210 |  |