18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * For documentation on the i460 AGP interface, see Chapter 7 (AGP Subsystem) of 38c2ecf20Sopenharmony_ci * the "Intel 460GTX Chipset Software Developer's Manual": 48c2ecf20Sopenharmony_ci * http://www.intel.com/design/archives/itanium/downloads/248704.htm 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci/* 78c2ecf20Sopenharmony_ci * 460GX support by Chris Ahna <christopher.j.ahna@intel.com> 88c2ecf20Sopenharmony_ci * Clean up & simplification by David Mosberger-Tang <davidm@hpl.hp.com> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/pci.h> 128c2ecf20Sopenharmony_ci#include <linux/init.h> 138c2ecf20Sopenharmony_ci#include <linux/string.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci#include <linux/agp_backend.h> 168c2ecf20Sopenharmony_ci#include <linux/log2.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include "agp.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define INTEL_I460_BAPBASE 0x98 218c2ecf20Sopenharmony_ci#define INTEL_I460_GXBCTL 0xa0 228c2ecf20Sopenharmony_ci#define INTEL_I460_AGPSIZ 0xa2 238c2ecf20Sopenharmony_ci#define INTEL_I460_ATTBASE 0xfe200000 248c2ecf20Sopenharmony_ci#define INTEL_I460_GATT_VALID (1UL << 24) 258c2ecf20Sopenharmony_ci#define INTEL_I460_GATT_COHERENT (1UL << 25) 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* 288c2ecf20Sopenharmony_ci * The i460 can operate with large (4MB) pages, but there is no sane way to support this 298c2ecf20Sopenharmony_ci * within the current kernel/DRM environment, so we disable the relevant code for now. 308c2ecf20Sopenharmony_ci * See also comments in ia64_alloc_page()... 318c2ecf20Sopenharmony_ci */ 328c2ecf20Sopenharmony_ci#define I460_LARGE_IO_PAGES 0 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#if I460_LARGE_IO_PAGES 358c2ecf20Sopenharmony_ci# define I460_IO_PAGE_SHIFT i460.io_page_shift 368c2ecf20Sopenharmony_ci#else 378c2ecf20Sopenharmony_ci# define I460_IO_PAGE_SHIFT 12 388c2ecf20Sopenharmony_ci#endif 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define I460_IOPAGES_PER_KPAGE (PAGE_SIZE >> I460_IO_PAGE_SHIFT) 418c2ecf20Sopenharmony_ci#define I460_KPAGES_PER_IOPAGE (1 << (I460_IO_PAGE_SHIFT - PAGE_SHIFT)) 428c2ecf20Sopenharmony_ci#define I460_SRAM_IO_DISABLE (1 << 4) 438c2ecf20Sopenharmony_ci#define I460_BAPBASE_ENABLE (1 << 3) 448c2ecf20Sopenharmony_ci#define I460_AGPSIZ_MASK 0x7 458c2ecf20Sopenharmony_ci#define I460_4M_PS (1 << 1) 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/* Control bits for Out-Of-GART coherency and Burst Write Combining */ 488c2ecf20Sopenharmony_ci#define I460_GXBCTL_OOG (1UL << 0) 498c2ecf20Sopenharmony_ci#define I460_GXBCTL_BWC (1UL << 2) 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* 528c2ecf20Sopenharmony_ci * gatt_table entries are 32-bits wide on the i460; the generic code ought to declare the 538c2ecf20Sopenharmony_ci * gatt_table and gatt_table_real pointers a "void *"... 548c2ecf20Sopenharmony_ci */ 558c2ecf20Sopenharmony_ci#define RD_GATT(index) readl((u32 *) i460.gatt + (index)) 568c2ecf20Sopenharmony_ci#define WR_GATT(index, val) writel((val), (u32 *) i460.gatt + (index)) 578c2ecf20Sopenharmony_ci/* 588c2ecf20Sopenharmony_ci * The 460 spec says we have to read the last location written to make sure that all 598c2ecf20Sopenharmony_ci * writes have taken effect 608c2ecf20Sopenharmony_ci */ 618c2ecf20Sopenharmony_ci#define WR_FLUSH_GATT(index) RD_GATT(index) 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic unsigned long i460_mask_memory (struct agp_bridge_data *bridge, 648c2ecf20Sopenharmony_ci dma_addr_t addr, int type); 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic struct { 678c2ecf20Sopenharmony_ci void *gatt; /* ioremap'd GATT area */ 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci /* i460 supports multiple GART page sizes, so GART pageshift is dynamic: */ 708c2ecf20Sopenharmony_ci u8 io_page_shift; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci /* BIOS configures chipset to one of 2 possible apbase values: */ 738c2ecf20Sopenharmony_ci u8 dynamic_apbase; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci /* structure for tracking partial use of 4MB GART pages: */ 768c2ecf20Sopenharmony_ci struct lp_desc { 778c2ecf20Sopenharmony_ci unsigned long *alloced_map; /* bitmap of kernel-pages in use */ 788c2ecf20Sopenharmony_ci int refcount; /* number of kernel pages using the large page */ 798c2ecf20Sopenharmony_ci u64 paddr; /* physical address of large page */ 808c2ecf20Sopenharmony_ci struct page *page; /* page pointer */ 818c2ecf20Sopenharmony_ci } *lp_desc; 828c2ecf20Sopenharmony_ci} i460; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic const struct aper_size_info_8 i460_sizes[3] = 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci /* 878c2ecf20Sopenharmony_ci * The 32GB aperture is only available with a 4M GART page size. Due to the 888c2ecf20Sopenharmony_ci * dynamic GART page size, we can't figure out page_order or num_entries until 898c2ecf20Sopenharmony_ci * runtime. 908c2ecf20Sopenharmony_ci */ 918c2ecf20Sopenharmony_ci {32768, 0, 0, 4}, 928c2ecf20Sopenharmony_ci {1024, 0, 0, 2}, 938c2ecf20Sopenharmony_ci {256, 0, 0, 1} 948c2ecf20Sopenharmony_ci}; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic struct gatt_mask i460_masks[] = 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci { 998c2ecf20Sopenharmony_ci .mask = INTEL_I460_GATT_VALID | INTEL_I460_GATT_COHERENT, 1008c2ecf20Sopenharmony_ci .type = 0 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci}; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic int i460_fetch_size (void) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci int i; 1078c2ecf20Sopenharmony_ci u8 temp; 1088c2ecf20Sopenharmony_ci struct aper_size_info_8 *values; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci /* Determine the GART page size */ 1118c2ecf20Sopenharmony_ci pci_read_config_byte(agp_bridge->dev, INTEL_I460_GXBCTL, &temp); 1128c2ecf20Sopenharmony_ci i460.io_page_shift = (temp & I460_4M_PS) ? 22 : 12; 1138c2ecf20Sopenharmony_ci pr_debug("i460_fetch_size: io_page_shift=%d\n", i460.io_page_shift); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci if (i460.io_page_shift != I460_IO_PAGE_SHIFT) { 1168c2ecf20Sopenharmony_ci printk(KERN_ERR PFX 1178c2ecf20Sopenharmony_ci "I/O (GART) page-size %luKB doesn't match expected " 1188c2ecf20Sopenharmony_ci "size %luKB\n", 1198c2ecf20Sopenharmony_ci 1UL << (i460.io_page_shift - 10), 1208c2ecf20Sopenharmony_ci 1UL << (I460_IO_PAGE_SHIFT)); 1218c2ecf20Sopenharmony_ci return 0; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci values = A_SIZE_8(agp_bridge->driver->aperture_sizes); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci pci_read_config_byte(agp_bridge->dev, INTEL_I460_AGPSIZ, &temp); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci /* Exit now if the IO drivers for the GART SRAMS are turned off */ 1298c2ecf20Sopenharmony_ci if (temp & I460_SRAM_IO_DISABLE) { 1308c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "GART SRAMS disabled on 460GX chipset\n"); 1318c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "AGPGART operation not possible\n"); 1328c2ecf20Sopenharmony_ci return 0; 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci /* Make sure we don't try to create an 2 ^ 23 entry GATT */ 1368c2ecf20Sopenharmony_ci if ((i460.io_page_shift == 0) && ((temp & I460_AGPSIZ_MASK) == 4)) { 1378c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "We can't have a 32GB aperture with 4KB GART pages\n"); 1388c2ecf20Sopenharmony_ci return 0; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci /* Determine the proper APBASE register */ 1428c2ecf20Sopenharmony_ci if (temp & I460_BAPBASE_ENABLE) 1438c2ecf20Sopenharmony_ci i460.dynamic_apbase = INTEL_I460_BAPBASE; 1448c2ecf20Sopenharmony_ci else 1458c2ecf20Sopenharmony_ci i460.dynamic_apbase = AGP_APBASE; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++) { 1488c2ecf20Sopenharmony_ci /* 1498c2ecf20Sopenharmony_ci * Dynamically calculate the proper num_entries and page_order values for 1508c2ecf20Sopenharmony_ci * the define aperture sizes. Take care not to shift off the end of 1518c2ecf20Sopenharmony_ci * values[i].size. 1528c2ecf20Sopenharmony_ci */ 1538c2ecf20Sopenharmony_ci values[i].num_entries = (values[i].size << 8) >> (I460_IO_PAGE_SHIFT - 12); 1548c2ecf20Sopenharmony_ci values[i].page_order = ilog2((sizeof(u32)*values[i].num_entries) >> PAGE_SHIFT); 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++) { 1588c2ecf20Sopenharmony_ci /* Neglect control bits when matching up size_value */ 1598c2ecf20Sopenharmony_ci if ((temp & I460_AGPSIZ_MASK) == values[i].size_value) { 1608c2ecf20Sopenharmony_ci agp_bridge->previous_size = agp_bridge->current_size = (void *) (values + i); 1618c2ecf20Sopenharmony_ci agp_bridge->aperture_size_idx = i; 1628c2ecf20Sopenharmony_ci return values[i].size; 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci return 0; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci/* There isn't anything to do here since 460 has no GART TLB. */ 1708c2ecf20Sopenharmony_cistatic void i460_tlb_flush (struct agp_memory *mem) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci return; 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci/* 1768c2ecf20Sopenharmony_ci * This utility function is needed to prevent corruption of the control bits 1778c2ecf20Sopenharmony_ci * which are stored along with the aperture size in 460's AGPSIZ register 1788c2ecf20Sopenharmony_ci */ 1798c2ecf20Sopenharmony_cistatic void i460_write_agpsiz (u8 size_value) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci u8 temp; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci pci_read_config_byte(agp_bridge->dev, INTEL_I460_AGPSIZ, &temp); 1848c2ecf20Sopenharmony_ci pci_write_config_byte(agp_bridge->dev, INTEL_I460_AGPSIZ, 1858c2ecf20Sopenharmony_ci ((temp & ~I460_AGPSIZ_MASK) | size_value)); 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic void i460_cleanup (void) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci struct aper_size_info_8 *previous_size; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci previous_size = A_SIZE_8(agp_bridge->previous_size); 1938c2ecf20Sopenharmony_ci i460_write_agpsiz(previous_size->size_value); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci if (I460_IO_PAGE_SHIFT > PAGE_SHIFT) 1968c2ecf20Sopenharmony_ci kfree(i460.lp_desc); 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cistatic int i460_configure (void) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci union { 2028c2ecf20Sopenharmony_ci u32 small[2]; 2038c2ecf20Sopenharmony_ci u64 large; 2048c2ecf20Sopenharmony_ci } temp; 2058c2ecf20Sopenharmony_ci size_t size; 2068c2ecf20Sopenharmony_ci u8 scratch; 2078c2ecf20Sopenharmony_ci struct aper_size_info_8 *current_size; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci temp.large = 0; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci current_size = A_SIZE_8(agp_bridge->current_size); 2128c2ecf20Sopenharmony_ci i460_write_agpsiz(current_size->size_value); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci /* 2158c2ecf20Sopenharmony_ci * Do the necessary rigmarole to read all eight bytes of APBASE. 2168c2ecf20Sopenharmony_ci * This has to be done since the AGP aperture can be above 4GB on 2178c2ecf20Sopenharmony_ci * 460 based systems. 2188c2ecf20Sopenharmony_ci */ 2198c2ecf20Sopenharmony_ci pci_read_config_dword(agp_bridge->dev, i460.dynamic_apbase, &(temp.small[0])); 2208c2ecf20Sopenharmony_ci pci_read_config_dword(agp_bridge->dev, i460.dynamic_apbase + 4, &(temp.small[1])); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci /* Clear BAR control bits */ 2238c2ecf20Sopenharmony_ci agp_bridge->gart_bus_addr = temp.large & ~((1UL << 3) - 1); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci pci_read_config_byte(agp_bridge->dev, INTEL_I460_GXBCTL, &scratch); 2268c2ecf20Sopenharmony_ci pci_write_config_byte(agp_bridge->dev, INTEL_I460_GXBCTL, 2278c2ecf20Sopenharmony_ci (scratch & 0x02) | I460_GXBCTL_OOG | I460_GXBCTL_BWC); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci /* 2308c2ecf20Sopenharmony_ci * Initialize partial allocation trackers if a GART page is bigger than a kernel 2318c2ecf20Sopenharmony_ci * page. 2328c2ecf20Sopenharmony_ci */ 2338c2ecf20Sopenharmony_ci if (I460_IO_PAGE_SHIFT > PAGE_SHIFT) { 2348c2ecf20Sopenharmony_ci size = current_size->num_entries * sizeof(i460.lp_desc[0]); 2358c2ecf20Sopenharmony_ci i460.lp_desc = kzalloc(size, GFP_KERNEL); 2368c2ecf20Sopenharmony_ci if (!i460.lp_desc) 2378c2ecf20Sopenharmony_ci return -ENOMEM; 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci return 0; 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic int i460_create_gatt_table (struct agp_bridge_data *bridge) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci int page_order, num_entries, i; 2458c2ecf20Sopenharmony_ci void *temp; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci /* 2488c2ecf20Sopenharmony_ci * Load up the fixed address of the GART SRAMS which hold our GATT table. 2498c2ecf20Sopenharmony_ci */ 2508c2ecf20Sopenharmony_ci temp = agp_bridge->current_size; 2518c2ecf20Sopenharmony_ci page_order = A_SIZE_8(temp)->page_order; 2528c2ecf20Sopenharmony_ci num_entries = A_SIZE_8(temp)->num_entries; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci i460.gatt = ioremap(INTEL_I460_ATTBASE, PAGE_SIZE << page_order); 2558c2ecf20Sopenharmony_ci if (!i460.gatt) { 2568c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "ioremap failed\n"); 2578c2ecf20Sopenharmony_ci return -ENOMEM; 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci /* These are no good, the should be removed from the agp_bridge strucure... */ 2618c2ecf20Sopenharmony_ci agp_bridge->gatt_table_real = NULL; 2628c2ecf20Sopenharmony_ci agp_bridge->gatt_table = NULL; 2638c2ecf20Sopenharmony_ci agp_bridge->gatt_bus_addr = 0; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci for (i = 0; i < num_entries; ++i) 2668c2ecf20Sopenharmony_ci WR_GATT(i, 0); 2678c2ecf20Sopenharmony_ci WR_FLUSH_GATT(i - 1); 2688c2ecf20Sopenharmony_ci return 0; 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic int i460_free_gatt_table (struct agp_bridge_data *bridge) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci int num_entries, i; 2748c2ecf20Sopenharmony_ci void *temp; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci temp = agp_bridge->current_size; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci num_entries = A_SIZE_8(temp)->num_entries; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci for (i = 0; i < num_entries; ++i) 2818c2ecf20Sopenharmony_ci WR_GATT(i, 0); 2828c2ecf20Sopenharmony_ci WR_FLUSH_GATT(num_entries - 1); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci iounmap(i460.gatt); 2858c2ecf20Sopenharmony_ci return 0; 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci/* 2898c2ecf20Sopenharmony_ci * The following functions are called when the I/O (GART) page size is smaller than 2908c2ecf20Sopenharmony_ci * PAGE_SIZE. 2918c2ecf20Sopenharmony_ci */ 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cistatic int i460_insert_memory_small_io_page (struct agp_memory *mem, 2948c2ecf20Sopenharmony_ci off_t pg_start, int type) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci unsigned long paddr, io_pg_start, io_page_size; 2978c2ecf20Sopenharmony_ci int i, j, k, num_entries; 2988c2ecf20Sopenharmony_ci void *temp; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci pr_debug("i460_insert_memory_small_io_page(mem=%p, pg_start=%ld, type=%d, paddr0=0x%lx)\n", 3018c2ecf20Sopenharmony_ci mem, pg_start, type, page_to_phys(mem->pages[0])); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci if (type >= AGP_USER_TYPES || mem->type >= AGP_USER_TYPES) 3048c2ecf20Sopenharmony_ci return -EINVAL; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci io_pg_start = I460_IOPAGES_PER_KPAGE * pg_start; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci temp = agp_bridge->current_size; 3098c2ecf20Sopenharmony_ci num_entries = A_SIZE_8(temp)->num_entries; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci if ((io_pg_start + I460_IOPAGES_PER_KPAGE * mem->page_count) > num_entries) { 3128c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "Looks like we're out of AGP memory\n"); 3138c2ecf20Sopenharmony_ci return -EINVAL; 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci j = io_pg_start; 3178c2ecf20Sopenharmony_ci while (j < (io_pg_start + I460_IOPAGES_PER_KPAGE * mem->page_count)) { 3188c2ecf20Sopenharmony_ci if (!PGE_EMPTY(agp_bridge, RD_GATT(j))) { 3198c2ecf20Sopenharmony_ci pr_debug("i460_insert_memory_small_io_page: GATT[%d]=0x%x is busy\n", 3208c2ecf20Sopenharmony_ci j, RD_GATT(j)); 3218c2ecf20Sopenharmony_ci return -EBUSY; 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci j++; 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci io_page_size = 1UL << I460_IO_PAGE_SHIFT; 3278c2ecf20Sopenharmony_ci for (i = 0, j = io_pg_start; i < mem->page_count; i++) { 3288c2ecf20Sopenharmony_ci paddr = page_to_phys(mem->pages[i]); 3298c2ecf20Sopenharmony_ci for (k = 0; k < I460_IOPAGES_PER_KPAGE; k++, j++, paddr += io_page_size) 3308c2ecf20Sopenharmony_ci WR_GATT(j, i460_mask_memory(agp_bridge, paddr, mem->type)); 3318c2ecf20Sopenharmony_ci } 3328c2ecf20Sopenharmony_ci WR_FLUSH_GATT(j - 1); 3338c2ecf20Sopenharmony_ci return 0; 3348c2ecf20Sopenharmony_ci} 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_cistatic int i460_remove_memory_small_io_page(struct agp_memory *mem, 3378c2ecf20Sopenharmony_ci off_t pg_start, int type) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci int i; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci pr_debug("i460_remove_memory_small_io_page(mem=%p, pg_start=%ld, type=%d)\n", 3428c2ecf20Sopenharmony_ci mem, pg_start, type); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci pg_start = I460_IOPAGES_PER_KPAGE * pg_start; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci for (i = pg_start; i < (pg_start + I460_IOPAGES_PER_KPAGE * mem->page_count); i++) 3478c2ecf20Sopenharmony_ci WR_GATT(i, 0); 3488c2ecf20Sopenharmony_ci WR_FLUSH_GATT(i - 1); 3498c2ecf20Sopenharmony_ci return 0; 3508c2ecf20Sopenharmony_ci} 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci#if I460_LARGE_IO_PAGES 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci/* 3558c2ecf20Sopenharmony_ci * These functions are called when the I/O (GART) page size exceeds PAGE_SIZE. 3568c2ecf20Sopenharmony_ci * 3578c2ecf20Sopenharmony_ci * This situation is interesting since AGP memory allocations that are smaller than a 3588c2ecf20Sopenharmony_ci * single GART page are possible. The i460.lp_desc array tracks partial allocation of the 3598c2ecf20Sopenharmony_ci * large GART pages to work around this issue. 3608c2ecf20Sopenharmony_ci * 3618c2ecf20Sopenharmony_ci * i460.lp_desc[pg_num].refcount tracks the number of kernel pages in use within GART page 3628c2ecf20Sopenharmony_ci * pg_num. i460.lp_desc[pg_num].paddr is the physical address of the large page and 3638c2ecf20Sopenharmony_ci * i460.lp_desc[pg_num].alloced_map is a bitmap of kernel pages that are in use (allocated). 3648c2ecf20Sopenharmony_ci */ 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_cistatic int i460_alloc_large_page (struct lp_desc *lp) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci unsigned long order = I460_IO_PAGE_SHIFT - PAGE_SHIFT; 3698c2ecf20Sopenharmony_ci size_t map_size; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci lp->page = alloc_pages(GFP_KERNEL, order); 3728c2ecf20Sopenharmony_ci if (!lp->page) { 3738c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "Couldn't alloc 4M GART page...\n"); 3748c2ecf20Sopenharmony_ci return -ENOMEM; 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci map_size = ((I460_KPAGES_PER_IOPAGE + BITS_PER_LONG - 1) & -BITS_PER_LONG)/8; 3788c2ecf20Sopenharmony_ci lp->alloced_map = kzalloc(map_size, GFP_KERNEL); 3798c2ecf20Sopenharmony_ci if (!lp->alloced_map) { 3808c2ecf20Sopenharmony_ci __free_pages(lp->page, order); 3818c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "Out of memory, we're in trouble...\n"); 3828c2ecf20Sopenharmony_ci return -ENOMEM; 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci lp->paddr = page_to_phys(lp->page); 3868c2ecf20Sopenharmony_ci lp->refcount = 0; 3878c2ecf20Sopenharmony_ci atomic_add(I460_KPAGES_PER_IOPAGE, &agp_bridge->current_memory_agp); 3888c2ecf20Sopenharmony_ci return 0; 3898c2ecf20Sopenharmony_ci} 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cistatic void i460_free_large_page (struct lp_desc *lp) 3928c2ecf20Sopenharmony_ci{ 3938c2ecf20Sopenharmony_ci kfree(lp->alloced_map); 3948c2ecf20Sopenharmony_ci lp->alloced_map = NULL; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci __free_pages(lp->page, I460_IO_PAGE_SHIFT - PAGE_SHIFT); 3978c2ecf20Sopenharmony_ci atomic_sub(I460_KPAGES_PER_IOPAGE, &agp_bridge->current_memory_agp); 3988c2ecf20Sopenharmony_ci} 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_cistatic int i460_insert_memory_large_io_page (struct agp_memory *mem, 4018c2ecf20Sopenharmony_ci off_t pg_start, int type) 4028c2ecf20Sopenharmony_ci{ 4038c2ecf20Sopenharmony_ci int i, start_offset, end_offset, idx, pg, num_entries; 4048c2ecf20Sopenharmony_ci struct lp_desc *start, *end, *lp; 4058c2ecf20Sopenharmony_ci void *temp; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci if (type >= AGP_USER_TYPES || mem->type >= AGP_USER_TYPES) 4088c2ecf20Sopenharmony_ci return -EINVAL; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci temp = agp_bridge->current_size; 4118c2ecf20Sopenharmony_ci num_entries = A_SIZE_8(temp)->num_entries; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci /* Figure out what pg_start means in terms of our large GART pages */ 4148c2ecf20Sopenharmony_ci start = &i460.lp_desc[pg_start / I460_KPAGES_PER_IOPAGE]; 4158c2ecf20Sopenharmony_ci end = &i460.lp_desc[(pg_start + mem->page_count - 1) / I460_KPAGES_PER_IOPAGE]; 4168c2ecf20Sopenharmony_ci start_offset = pg_start % I460_KPAGES_PER_IOPAGE; 4178c2ecf20Sopenharmony_ci end_offset = (pg_start + mem->page_count - 1) % I460_KPAGES_PER_IOPAGE; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci if (end > i460.lp_desc + num_entries) { 4208c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "Looks like we're out of AGP memory\n"); 4218c2ecf20Sopenharmony_ci return -EINVAL; 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci /* Check if the requested region of the aperture is free */ 4258c2ecf20Sopenharmony_ci for (lp = start; lp <= end; ++lp) { 4268c2ecf20Sopenharmony_ci if (!lp->alloced_map) 4278c2ecf20Sopenharmony_ci continue; /* OK, the entire large page is available... */ 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci for (idx = ((lp == start) ? start_offset : 0); 4308c2ecf20Sopenharmony_ci idx < ((lp == end) ? (end_offset + 1) : I460_KPAGES_PER_IOPAGE); 4318c2ecf20Sopenharmony_ci idx++) 4328c2ecf20Sopenharmony_ci { 4338c2ecf20Sopenharmony_ci if (test_bit(idx, lp->alloced_map)) 4348c2ecf20Sopenharmony_ci return -EBUSY; 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci for (lp = start, i = 0; lp <= end; ++lp) { 4398c2ecf20Sopenharmony_ci if (!lp->alloced_map) { 4408c2ecf20Sopenharmony_ci /* Allocate new GART pages... */ 4418c2ecf20Sopenharmony_ci if (i460_alloc_large_page(lp) < 0) 4428c2ecf20Sopenharmony_ci return -ENOMEM; 4438c2ecf20Sopenharmony_ci pg = lp - i460.lp_desc; 4448c2ecf20Sopenharmony_ci WR_GATT(pg, i460_mask_memory(agp_bridge, 4458c2ecf20Sopenharmony_ci lp->paddr, 0)); 4468c2ecf20Sopenharmony_ci WR_FLUSH_GATT(pg); 4478c2ecf20Sopenharmony_ci } 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci for (idx = ((lp == start) ? start_offset : 0); 4508c2ecf20Sopenharmony_ci idx < ((lp == end) ? (end_offset + 1) : I460_KPAGES_PER_IOPAGE); 4518c2ecf20Sopenharmony_ci idx++, i++) 4528c2ecf20Sopenharmony_ci { 4538c2ecf20Sopenharmony_ci mem->pages[i] = lp->page; 4548c2ecf20Sopenharmony_ci __set_bit(idx, lp->alloced_map); 4558c2ecf20Sopenharmony_ci ++lp->refcount; 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci return 0; 4598c2ecf20Sopenharmony_ci} 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_cistatic int i460_remove_memory_large_io_page (struct agp_memory *mem, 4628c2ecf20Sopenharmony_ci off_t pg_start, int type) 4638c2ecf20Sopenharmony_ci{ 4648c2ecf20Sopenharmony_ci int i, pg, start_offset, end_offset, idx, num_entries; 4658c2ecf20Sopenharmony_ci struct lp_desc *start, *end, *lp; 4668c2ecf20Sopenharmony_ci void *temp; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci temp = agp_bridge->current_size; 4698c2ecf20Sopenharmony_ci num_entries = A_SIZE_8(temp)->num_entries; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci /* Figure out what pg_start means in terms of our large GART pages */ 4728c2ecf20Sopenharmony_ci start = &i460.lp_desc[pg_start / I460_KPAGES_PER_IOPAGE]; 4738c2ecf20Sopenharmony_ci end = &i460.lp_desc[(pg_start + mem->page_count - 1) / I460_KPAGES_PER_IOPAGE]; 4748c2ecf20Sopenharmony_ci start_offset = pg_start % I460_KPAGES_PER_IOPAGE; 4758c2ecf20Sopenharmony_ci end_offset = (pg_start + mem->page_count - 1) % I460_KPAGES_PER_IOPAGE; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci for (i = 0, lp = start; lp <= end; ++lp) { 4788c2ecf20Sopenharmony_ci for (idx = ((lp == start) ? start_offset : 0); 4798c2ecf20Sopenharmony_ci idx < ((lp == end) ? (end_offset + 1) : I460_KPAGES_PER_IOPAGE); 4808c2ecf20Sopenharmony_ci idx++, i++) 4818c2ecf20Sopenharmony_ci { 4828c2ecf20Sopenharmony_ci mem->pages[i] = NULL; 4838c2ecf20Sopenharmony_ci __clear_bit(idx, lp->alloced_map); 4848c2ecf20Sopenharmony_ci --lp->refcount; 4858c2ecf20Sopenharmony_ci } 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci /* Free GART pages if they are unused */ 4888c2ecf20Sopenharmony_ci if (lp->refcount == 0) { 4898c2ecf20Sopenharmony_ci pg = lp - i460.lp_desc; 4908c2ecf20Sopenharmony_ci WR_GATT(pg, 0); 4918c2ecf20Sopenharmony_ci WR_FLUSH_GATT(pg); 4928c2ecf20Sopenharmony_ci i460_free_large_page(lp); 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci } 4958c2ecf20Sopenharmony_ci return 0; 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci/* Wrapper routines to call the approriate {small_io_page,large_io_page} function */ 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_cistatic int i460_insert_memory (struct agp_memory *mem, 5018c2ecf20Sopenharmony_ci off_t pg_start, int type) 5028c2ecf20Sopenharmony_ci{ 5038c2ecf20Sopenharmony_ci if (I460_IO_PAGE_SHIFT <= PAGE_SHIFT) 5048c2ecf20Sopenharmony_ci return i460_insert_memory_small_io_page(mem, pg_start, type); 5058c2ecf20Sopenharmony_ci else 5068c2ecf20Sopenharmony_ci return i460_insert_memory_large_io_page(mem, pg_start, type); 5078c2ecf20Sopenharmony_ci} 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_cistatic int i460_remove_memory (struct agp_memory *mem, 5108c2ecf20Sopenharmony_ci off_t pg_start, int type) 5118c2ecf20Sopenharmony_ci{ 5128c2ecf20Sopenharmony_ci if (I460_IO_PAGE_SHIFT <= PAGE_SHIFT) 5138c2ecf20Sopenharmony_ci return i460_remove_memory_small_io_page(mem, pg_start, type); 5148c2ecf20Sopenharmony_ci else 5158c2ecf20Sopenharmony_ci return i460_remove_memory_large_io_page(mem, pg_start, type); 5168c2ecf20Sopenharmony_ci} 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci/* 5198c2ecf20Sopenharmony_ci * If the I/O (GART) page size is bigger than the kernel page size, we don't want to 5208c2ecf20Sopenharmony_ci * allocate memory until we know where it is to be bound in the aperture (a 5218c2ecf20Sopenharmony_ci * multi-kernel-page alloc might fit inside of an already allocated GART page). 5228c2ecf20Sopenharmony_ci * 5238c2ecf20Sopenharmony_ci * Let's just hope nobody counts on the allocated AGP memory being there before bind time 5248c2ecf20Sopenharmony_ci * (I don't think current drivers do)... 5258c2ecf20Sopenharmony_ci */ 5268c2ecf20Sopenharmony_cistatic struct page *i460_alloc_page (struct agp_bridge_data *bridge) 5278c2ecf20Sopenharmony_ci{ 5288c2ecf20Sopenharmony_ci void *page; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci if (I460_IO_PAGE_SHIFT <= PAGE_SHIFT) { 5318c2ecf20Sopenharmony_ci page = agp_generic_alloc_page(agp_bridge); 5328c2ecf20Sopenharmony_ci } else 5338c2ecf20Sopenharmony_ci /* Returning NULL would cause problems */ 5348c2ecf20Sopenharmony_ci /* AK: really dubious code. */ 5358c2ecf20Sopenharmony_ci page = (void *)~0UL; 5368c2ecf20Sopenharmony_ci return page; 5378c2ecf20Sopenharmony_ci} 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_cistatic void i460_destroy_page (struct page *page, int flags) 5408c2ecf20Sopenharmony_ci{ 5418c2ecf20Sopenharmony_ci if (I460_IO_PAGE_SHIFT <= PAGE_SHIFT) { 5428c2ecf20Sopenharmony_ci agp_generic_destroy_page(page, flags); 5438c2ecf20Sopenharmony_ci } 5448c2ecf20Sopenharmony_ci} 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci#endif /* I460_LARGE_IO_PAGES */ 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_cistatic unsigned long i460_mask_memory (struct agp_bridge_data *bridge, 5498c2ecf20Sopenharmony_ci dma_addr_t addr, int type) 5508c2ecf20Sopenharmony_ci{ 5518c2ecf20Sopenharmony_ci /* Make sure the returned address is a valid GATT entry */ 5528c2ecf20Sopenharmony_ci return bridge->driver->masks[0].mask 5538c2ecf20Sopenharmony_ci | (((addr & ~((1 << I460_IO_PAGE_SHIFT) - 1)) & 0xfffff000) >> 12); 5548c2ecf20Sopenharmony_ci} 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ciconst struct agp_bridge_driver intel_i460_driver = { 5578c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 5588c2ecf20Sopenharmony_ci .aperture_sizes = i460_sizes, 5598c2ecf20Sopenharmony_ci .size_type = U8_APER_SIZE, 5608c2ecf20Sopenharmony_ci .num_aperture_sizes = 3, 5618c2ecf20Sopenharmony_ci .configure = i460_configure, 5628c2ecf20Sopenharmony_ci .fetch_size = i460_fetch_size, 5638c2ecf20Sopenharmony_ci .cleanup = i460_cleanup, 5648c2ecf20Sopenharmony_ci .tlb_flush = i460_tlb_flush, 5658c2ecf20Sopenharmony_ci .mask_memory = i460_mask_memory, 5668c2ecf20Sopenharmony_ci .masks = i460_masks, 5678c2ecf20Sopenharmony_ci .agp_enable = agp_generic_enable, 5688c2ecf20Sopenharmony_ci .cache_flush = global_cache_flush, 5698c2ecf20Sopenharmony_ci .create_gatt_table = i460_create_gatt_table, 5708c2ecf20Sopenharmony_ci .free_gatt_table = i460_free_gatt_table, 5718c2ecf20Sopenharmony_ci#if I460_LARGE_IO_PAGES 5728c2ecf20Sopenharmony_ci .insert_memory = i460_insert_memory, 5738c2ecf20Sopenharmony_ci .remove_memory = i460_remove_memory, 5748c2ecf20Sopenharmony_ci .agp_alloc_page = i460_alloc_page, 5758c2ecf20Sopenharmony_ci .agp_destroy_page = i460_destroy_page, 5768c2ecf20Sopenharmony_ci#else 5778c2ecf20Sopenharmony_ci .insert_memory = i460_insert_memory_small_io_page, 5788c2ecf20Sopenharmony_ci .remove_memory = i460_remove_memory_small_io_page, 5798c2ecf20Sopenharmony_ci .agp_alloc_page = agp_generic_alloc_page, 5808c2ecf20Sopenharmony_ci .agp_alloc_pages = agp_generic_alloc_pages, 5818c2ecf20Sopenharmony_ci .agp_destroy_page = agp_generic_destroy_page, 5828c2ecf20Sopenharmony_ci .agp_destroy_pages = agp_generic_destroy_pages, 5838c2ecf20Sopenharmony_ci#endif 5848c2ecf20Sopenharmony_ci .alloc_by_type = agp_generic_alloc_by_type, 5858c2ecf20Sopenharmony_ci .free_by_type = agp_generic_free_by_type, 5868c2ecf20Sopenharmony_ci .agp_type_to_mask_type = agp_generic_type_to_mask_type, 5878c2ecf20Sopenharmony_ci .cant_use_aperture = true, 5888c2ecf20Sopenharmony_ci}; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_cistatic int agp_intel_i460_probe(struct pci_dev *pdev, 5918c2ecf20Sopenharmony_ci const struct pci_device_id *ent) 5928c2ecf20Sopenharmony_ci{ 5938c2ecf20Sopenharmony_ci struct agp_bridge_data *bridge; 5948c2ecf20Sopenharmony_ci u8 cap_ptr; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP); 5978c2ecf20Sopenharmony_ci if (!cap_ptr) 5988c2ecf20Sopenharmony_ci return -ENODEV; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci bridge = agp_alloc_bridge(); 6018c2ecf20Sopenharmony_ci if (!bridge) 6028c2ecf20Sopenharmony_ci return -ENOMEM; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci bridge->driver = &intel_i460_driver; 6058c2ecf20Sopenharmony_ci bridge->dev = pdev; 6068c2ecf20Sopenharmony_ci bridge->capndx = cap_ptr; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci printk(KERN_INFO PFX "Detected Intel 460GX chipset\n"); 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, bridge); 6118c2ecf20Sopenharmony_ci return agp_add_bridge(bridge); 6128c2ecf20Sopenharmony_ci} 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_cistatic void agp_intel_i460_remove(struct pci_dev *pdev) 6158c2ecf20Sopenharmony_ci{ 6168c2ecf20Sopenharmony_ci struct agp_bridge_data *bridge = pci_get_drvdata(pdev); 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci agp_remove_bridge(bridge); 6198c2ecf20Sopenharmony_ci agp_put_bridge(bridge); 6208c2ecf20Sopenharmony_ci} 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_cistatic struct pci_device_id agp_intel_i460_pci_table[] = { 6238c2ecf20Sopenharmony_ci { 6248c2ecf20Sopenharmony_ci .class = (PCI_CLASS_BRIDGE_HOST << 8), 6258c2ecf20Sopenharmony_ci .class_mask = ~0, 6268c2ecf20Sopenharmony_ci .vendor = PCI_VENDOR_ID_INTEL, 6278c2ecf20Sopenharmony_ci .device = PCI_DEVICE_ID_INTEL_84460GX, 6288c2ecf20Sopenharmony_ci .subvendor = PCI_ANY_ID, 6298c2ecf20Sopenharmony_ci .subdevice = PCI_ANY_ID, 6308c2ecf20Sopenharmony_ci }, 6318c2ecf20Sopenharmony_ci { } 6328c2ecf20Sopenharmony_ci}; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, agp_intel_i460_pci_table); 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_cistatic struct pci_driver agp_intel_i460_pci_driver = { 6378c2ecf20Sopenharmony_ci .name = "agpgart-intel-i460", 6388c2ecf20Sopenharmony_ci .id_table = agp_intel_i460_pci_table, 6398c2ecf20Sopenharmony_ci .probe = agp_intel_i460_probe, 6408c2ecf20Sopenharmony_ci .remove = agp_intel_i460_remove, 6418c2ecf20Sopenharmony_ci}; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_cistatic int __init agp_intel_i460_init(void) 6448c2ecf20Sopenharmony_ci{ 6458c2ecf20Sopenharmony_ci if (agp_off) 6468c2ecf20Sopenharmony_ci return -EINVAL; 6478c2ecf20Sopenharmony_ci return pci_register_driver(&agp_intel_i460_pci_driver); 6488c2ecf20Sopenharmony_ci} 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_cistatic void __exit agp_intel_i460_cleanup(void) 6518c2ecf20Sopenharmony_ci{ 6528c2ecf20Sopenharmony_ci pci_unregister_driver(&agp_intel_i460_pci_driver); 6538c2ecf20Sopenharmony_ci} 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_cimodule_init(agp_intel_i460_init); 6568c2ecf20Sopenharmony_cimodule_exit(agp_intel_i460_cleanup); 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ciMODULE_AUTHOR("Chris Ahna <Christopher.J.Ahna@intel.com>"); 6598c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL and additional rights"); 660