18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Transmeta's Efficeon AGPGART driver. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Based upon a diff by Linus around November '02. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Ported to the 2.6 kernel by Carlos Puchol <cpglinux@puchol.com> 78c2ecf20Sopenharmony_ci * and H. Peter Anvin <hpa@transmeta.com>. 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci/* 118c2ecf20Sopenharmony_ci * NOTE-cpg-040217: 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * - when compiled as a module, after loading the module, 148c2ecf20Sopenharmony_ci * it will refuse to unload, indicating it is in use, 158c2ecf20Sopenharmony_ci * when it is not. 168c2ecf20Sopenharmony_ci * - no s3 (suspend to ram) testing. 178c2ecf20Sopenharmony_ci * - tested on the efficeon integrated nothbridge for tens 188c2ecf20Sopenharmony_ci * of iterations of starting x and glxgears. 198c2ecf20Sopenharmony_ci * - tested with radeon 9000 and radeon mobility m9 cards 208c2ecf20Sopenharmony_ci * - tested with c3/c4 enabled (with the mobility m9 card) 218c2ecf20Sopenharmony_ci */ 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include <linux/module.h> 248c2ecf20Sopenharmony_ci#include <linux/pci.h> 258c2ecf20Sopenharmony_ci#include <linux/init.h> 268c2ecf20Sopenharmony_ci#include <linux/agp_backend.h> 278c2ecf20Sopenharmony_ci#include <linux/gfp.h> 288c2ecf20Sopenharmony_ci#include <linux/page-flags.h> 298c2ecf20Sopenharmony_ci#include <linux/mm.h> 308c2ecf20Sopenharmony_ci#include "agp.h" 318c2ecf20Sopenharmony_ci#include "intel-agp.h" 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci/* 348c2ecf20Sopenharmony_ci * The real differences to the generic AGP code is 358c2ecf20Sopenharmony_ci * in the GART mappings - a two-level setup with the 368c2ecf20Sopenharmony_ci * first level being an on-chip 64-entry table. 378c2ecf20Sopenharmony_ci * 388c2ecf20Sopenharmony_ci * The page array is filled through the ATTPAGE register 398c2ecf20Sopenharmony_ci * (Aperture Translation Table Page Register) at 0xB8. Bits: 408c2ecf20Sopenharmony_ci * 31:20: physical page address 418c2ecf20Sopenharmony_ci * 11:9: Page Attribute Table Index (PATI) 428c2ecf20Sopenharmony_ci * must match the PAT index for the 438c2ecf20Sopenharmony_ci * mapped pages (the 2nd level page table pages 448c2ecf20Sopenharmony_ci * themselves should be just regular WB-cacheable, 458c2ecf20Sopenharmony_ci * so this is normally zero.) 468c2ecf20Sopenharmony_ci * 8: Present 478c2ecf20Sopenharmony_ci * 7:6: reserved, write as zero 488c2ecf20Sopenharmony_ci * 5:0: GATT directory index: which 1st-level entry 498c2ecf20Sopenharmony_ci * 508c2ecf20Sopenharmony_ci * The Efficeon AGP spec requires pages to be WB-cacheable 518c2ecf20Sopenharmony_ci * but to be explicitly CLFLUSH'd after any changes. 528c2ecf20Sopenharmony_ci */ 538c2ecf20Sopenharmony_ci#define EFFICEON_ATTPAGE 0xb8 548c2ecf20Sopenharmony_ci#define EFFICEON_L1_SIZE 64 /* Number of PDE pages */ 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci#define EFFICEON_PATI (0 << 9) 578c2ecf20Sopenharmony_ci#define EFFICEON_PRESENT (1 << 8) 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic struct _efficeon_private { 608c2ecf20Sopenharmony_ci unsigned long l1_table[EFFICEON_L1_SIZE]; 618c2ecf20Sopenharmony_ci} efficeon_private; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic const struct gatt_mask efficeon_generic_masks[] = 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci {.mask = 0x00000001, .type = 0} 668c2ecf20Sopenharmony_ci}; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/* This function does the same thing as mask_memory() for this chipset... */ 698c2ecf20Sopenharmony_cistatic inline unsigned long efficeon_mask_memory(struct page *page) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci unsigned long addr = page_to_phys(page); 728c2ecf20Sopenharmony_ci return addr | 0x00000001; 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic const struct aper_size_info_lvl2 efficeon_generic_sizes[4] = 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci {256, 65536, 0}, 788c2ecf20Sopenharmony_ci {128, 32768, 32}, 798c2ecf20Sopenharmony_ci {64, 16384, 48}, 808c2ecf20Sopenharmony_ci {32, 8192, 56} 818c2ecf20Sopenharmony_ci}; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci/* 848c2ecf20Sopenharmony_ci * Control interfaces are largely identical to 858c2ecf20Sopenharmony_ci * the legacy Intel 440BX.. 868c2ecf20Sopenharmony_ci */ 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic int efficeon_fetch_size(void) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci int i; 918c2ecf20Sopenharmony_ci u16 temp; 928c2ecf20Sopenharmony_ci struct aper_size_info_lvl2 *values; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci pci_read_config_word(agp_bridge->dev, INTEL_APSIZE, &temp); 958c2ecf20Sopenharmony_ci values = A_SIZE_LVL2(agp_bridge->driver->aperture_sizes); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++) { 988c2ecf20Sopenharmony_ci if (temp == values[i].size_value) { 998c2ecf20Sopenharmony_ci agp_bridge->previous_size = 1008c2ecf20Sopenharmony_ci agp_bridge->current_size = (void *) (values + i); 1018c2ecf20Sopenharmony_ci agp_bridge->aperture_size_idx = i; 1028c2ecf20Sopenharmony_ci return values[i].size; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci return 0; 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic void efficeon_tlbflush(struct agp_memory * mem) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci printk(KERN_DEBUG PFX "efficeon_tlbflush()\n"); 1128c2ecf20Sopenharmony_ci pci_write_config_dword(agp_bridge->dev, INTEL_AGPCTRL, 0x2200); 1138c2ecf20Sopenharmony_ci pci_write_config_dword(agp_bridge->dev, INTEL_AGPCTRL, 0x2280); 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic void efficeon_cleanup(void) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci u16 temp; 1198c2ecf20Sopenharmony_ci struct aper_size_info_lvl2 *previous_size; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci printk(KERN_DEBUG PFX "efficeon_cleanup()\n"); 1228c2ecf20Sopenharmony_ci previous_size = A_SIZE_LVL2(agp_bridge->previous_size); 1238c2ecf20Sopenharmony_ci pci_read_config_word(agp_bridge->dev, INTEL_NBXCFG, &temp); 1248c2ecf20Sopenharmony_ci pci_write_config_word(agp_bridge->dev, INTEL_NBXCFG, temp & ~(1 << 9)); 1258c2ecf20Sopenharmony_ci pci_write_config_word(agp_bridge->dev, INTEL_APSIZE, 1268c2ecf20Sopenharmony_ci previous_size->size_value); 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic int efficeon_configure(void) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci u16 temp2; 1328c2ecf20Sopenharmony_ci struct aper_size_info_lvl2 *current_size; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci printk(KERN_DEBUG PFX "efficeon_configure()\n"); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci current_size = A_SIZE_LVL2(agp_bridge->current_size); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci /* aperture size */ 1398c2ecf20Sopenharmony_ci pci_write_config_word(agp_bridge->dev, INTEL_APSIZE, 1408c2ecf20Sopenharmony_ci current_size->size_value); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci /* address to map to */ 1438c2ecf20Sopenharmony_ci agp_bridge->gart_bus_addr = pci_bus_address(agp_bridge->dev, 1448c2ecf20Sopenharmony_ci AGP_APERTURE_BAR); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci /* agpctrl */ 1478c2ecf20Sopenharmony_ci pci_write_config_dword(agp_bridge->dev, INTEL_AGPCTRL, 0x2280); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci /* paccfg/nbxcfg */ 1508c2ecf20Sopenharmony_ci pci_read_config_word(agp_bridge->dev, INTEL_NBXCFG, &temp2); 1518c2ecf20Sopenharmony_ci pci_write_config_word(agp_bridge->dev, INTEL_NBXCFG, 1528c2ecf20Sopenharmony_ci (temp2 & ~(1 << 10)) | (1 << 9) | (1 << 11)); 1538c2ecf20Sopenharmony_ci /* clear any possible error conditions */ 1548c2ecf20Sopenharmony_ci pci_write_config_byte(agp_bridge->dev, INTEL_ERRSTS + 1, 7); 1558c2ecf20Sopenharmony_ci return 0; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic int efficeon_free_gatt_table(struct agp_bridge_data *bridge) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci int index, freed = 0; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci for (index = 0; index < EFFICEON_L1_SIZE; index++) { 1638c2ecf20Sopenharmony_ci unsigned long page = efficeon_private.l1_table[index]; 1648c2ecf20Sopenharmony_ci if (page) { 1658c2ecf20Sopenharmony_ci efficeon_private.l1_table[index] = 0; 1668c2ecf20Sopenharmony_ci free_page(page); 1678c2ecf20Sopenharmony_ci freed++; 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci printk(KERN_DEBUG PFX "efficeon_free_gatt_table(%p, %02x, %08x)\n", 1708c2ecf20Sopenharmony_ci agp_bridge->dev, EFFICEON_ATTPAGE, index); 1718c2ecf20Sopenharmony_ci pci_write_config_dword(agp_bridge->dev, 1728c2ecf20Sopenharmony_ci EFFICEON_ATTPAGE, index); 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci printk(KERN_DEBUG PFX "efficeon_free_gatt_table() freed %d pages\n", freed); 1758c2ecf20Sopenharmony_ci return 0; 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci/* 1808c2ecf20Sopenharmony_ci * Since we don't need contiguous memory we just try 1818c2ecf20Sopenharmony_ci * to get the gatt table once 1828c2ecf20Sopenharmony_ci */ 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci#define GET_PAGE_DIR_OFF(addr) (addr >> 22) 1858c2ecf20Sopenharmony_ci#define GET_PAGE_DIR_IDX(addr) (GET_PAGE_DIR_OFF(addr) - \ 1868c2ecf20Sopenharmony_ci GET_PAGE_DIR_OFF(agp_bridge->gart_bus_addr)) 1878c2ecf20Sopenharmony_ci#define GET_GATT_OFF(addr) ((addr & 0x003ff000) >> 12) 1888c2ecf20Sopenharmony_ci#undef GET_GATT 1898c2ecf20Sopenharmony_ci#define GET_GATT(addr) (efficeon_private.gatt_pages[\ 1908c2ecf20Sopenharmony_ci GET_PAGE_DIR_IDX(addr)]->remapped) 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic int efficeon_create_gatt_table(struct agp_bridge_data *bridge) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci int index; 1958c2ecf20Sopenharmony_ci const int pati = EFFICEON_PATI; 1968c2ecf20Sopenharmony_ci const int present = EFFICEON_PRESENT; 1978c2ecf20Sopenharmony_ci const int clflush_chunk = ((cpuid_ebx(1) >> 8) & 0xff) << 3; 1988c2ecf20Sopenharmony_ci int num_entries, l1_pages; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci num_entries = A_SIZE_LVL2(agp_bridge->current_size)->num_entries; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci printk(KERN_DEBUG PFX "efficeon_create_gatt_table(%d)\n", num_entries); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci /* There are 2^10 PTE pages per PDE page */ 2058c2ecf20Sopenharmony_ci BUG_ON(num_entries & 0x3ff); 2068c2ecf20Sopenharmony_ci l1_pages = num_entries >> 10; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci for (index = 0 ; index < l1_pages ; index++) { 2098c2ecf20Sopenharmony_ci int offset; 2108c2ecf20Sopenharmony_ci unsigned long page; 2118c2ecf20Sopenharmony_ci unsigned long value; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci page = efficeon_private.l1_table[index]; 2148c2ecf20Sopenharmony_ci BUG_ON(page); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci page = get_zeroed_page(GFP_KERNEL); 2178c2ecf20Sopenharmony_ci if (!page) { 2188c2ecf20Sopenharmony_ci efficeon_free_gatt_table(agp_bridge); 2198c2ecf20Sopenharmony_ci return -ENOMEM; 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci for (offset = 0; offset < PAGE_SIZE; offset += clflush_chunk) 2238c2ecf20Sopenharmony_ci clflush((char *)page+offset); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci efficeon_private.l1_table[index] = page; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci value = virt_to_phys((unsigned long *)page) | pati | present | index; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci pci_write_config_dword(agp_bridge->dev, 2308c2ecf20Sopenharmony_ci EFFICEON_ATTPAGE, value); 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci return 0; 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic int efficeon_insert_memory(struct agp_memory * mem, off_t pg_start, int type) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci int i, count = mem->page_count, num_entries; 2398c2ecf20Sopenharmony_ci unsigned int *page, *last_page; 2408c2ecf20Sopenharmony_ci const int clflush_chunk = ((cpuid_ebx(1) >> 8) & 0xff) << 3; 2418c2ecf20Sopenharmony_ci const unsigned long clflush_mask = ~(clflush_chunk-1); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci printk(KERN_DEBUG PFX "efficeon_insert_memory(%lx, %d)\n", pg_start, count); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci num_entries = A_SIZE_LVL2(agp_bridge->current_size)->num_entries; 2468c2ecf20Sopenharmony_ci if ((pg_start + mem->page_count) > num_entries) 2478c2ecf20Sopenharmony_ci return -EINVAL; 2488c2ecf20Sopenharmony_ci if (type != 0 || mem->type != 0) 2498c2ecf20Sopenharmony_ci return -EINVAL; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci if (!mem->is_flushed) { 2528c2ecf20Sopenharmony_ci global_cache_flush(); 2538c2ecf20Sopenharmony_ci mem->is_flushed = true; 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci last_page = NULL; 2578c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 2588c2ecf20Sopenharmony_ci int index = pg_start + i; 2598c2ecf20Sopenharmony_ci unsigned long insert = efficeon_mask_memory(mem->pages[i]); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci page = (unsigned int *) efficeon_private.l1_table[index >> 10]; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci if (!page) 2648c2ecf20Sopenharmony_ci continue; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci page += (index & 0x3ff); 2678c2ecf20Sopenharmony_ci *page = insert; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci /* clflush is slow, so don't clflush until we have to */ 2708c2ecf20Sopenharmony_ci if (last_page && 2718c2ecf20Sopenharmony_ci (((unsigned long)page^(unsigned long)last_page) & 2728c2ecf20Sopenharmony_ci clflush_mask)) 2738c2ecf20Sopenharmony_ci clflush(last_page); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci last_page = page; 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci if ( last_page ) 2798c2ecf20Sopenharmony_ci clflush(last_page); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci agp_bridge->driver->tlb_flush(mem); 2828c2ecf20Sopenharmony_ci return 0; 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cistatic int efficeon_remove_memory(struct agp_memory * mem, off_t pg_start, int type) 2868c2ecf20Sopenharmony_ci{ 2878c2ecf20Sopenharmony_ci int i, count = mem->page_count, num_entries; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci printk(KERN_DEBUG PFX "efficeon_remove_memory(%lx, %d)\n", pg_start, count); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci num_entries = A_SIZE_LVL2(agp_bridge->current_size)->num_entries; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci if ((pg_start + mem->page_count) > num_entries) 2948c2ecf20Sopenharmony_ci return -EINVAL; 2958c2ecf20Sopenharmony_ci if (type != 0 || mem->type != 0) 2968c2ecf20Sopenharmony_ci return -EINVAL; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 2998c2ecf20Sopenharmony_ci int index = pg_start + i; 3008c2ecf20Sopenharmony_ci unsigned int *page = (unsigned int *) efficeon_private.l1_table[index >> 10]; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci if (!page) 3038c2ecf20Sopenharmony_ci continue; 3048c2ecf20Sopenharmony_ci page += (index & 0x3ff); 3058c2ecf20Sopenharmony_ci *page = 0; 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci agp_bridge->driver->tlb_flush(mem); 3088c2ecf20Sopenharmony_ci return 0; 3098c2ecf20Sopenharmony_ci} 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cistatic const struct agp_bridge_driver efficeon_driver = { 3138c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 3148c2ecf20Sopenharmony_ci .aperture_sizes = efficeon_generic_sizes, 3158c2ecf20Sopenharmony_ci .size_type = LVL2_APER_SIZE, 3168c2ecf20Sopenharmony_ci .num_aperture_sizes = 4, 3178c2ecf20Sopenharmony_ci .configure = efficeon_configure, 3188c2ecf20Sopenharmony_ci .fetch_size = efficeon_fetch_size, 3198c2ecf20Sopenharmony_ci .cleanup = efficeon_cleanup, 3208c2ecf20Sopenharmony_ci .tlb_flush = efficeon_tlbflush, 3218c2ecf20Sopenharmony_ci .mask_memory = agp_generic_mask_memory, 3228c2ecf20Sopenharmony_ci .masks = efficeon_generic_masks, 3238c2ecf20Sopenharmony_ci .agp_enable = agp_generic_enable, 3248c2ecf20Sopenharmony_ci .cache_flush = global_cache_flush, 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci // Efficeon-specific GATT table setup / populate / teardown 3278c2ecf20Sopenharmony_ci .create_gatt_table = efficeon_create_gatt_table, 3288c2ecf20Sopenharmony_ci .free_gatt_table = efficeon_free_gatt_table, 3298c2ecf20Sopenharmony_ci .insert_memory = efficeon_insert_memory, 3308c2ecf20Sopenharmony_ci .remove_memory = efficeon_remove_memory, 3318c2ecf20Sopenharmony_ci .cant_use_aperture = false, // true might be faster? 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci // Generic 3348c2ecf20Sopenharmony_ci .alloc_by_type = agp_generic_alloc_by_type, 3358c2ecf20Sopenharmony_ci .free_by_type = agp_generic_free_by_type, 3368c2ecf20Sopenharmony_ci .agp_alloc_page = agp_generic_alloc_page, 3378c2ecf20Sopenharmony_ci .agp_alloc_pages = agp_generic_alloc_pages, 3388c2ecf20Sopenharmony_ci .agp_destroy_page = agp_generic_destroy_page, 3398c2ecf20Sopenharmony_ci .agp_destroy_pages = agp_generic_destroy_pages, 3408c2ecf20Sopenharmony_ci .agp_type_to_mask_type = agp_generic_type_to_mask_type, 3418c2ecf20Sopenharmony_ci}; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_cistatic int agp_efficeon_probe(struct pci_dev *pdev, 3448c2ecf20Sopenharmony_ci const struct pci_device_id *ent) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci struct agp_bridge_data *bridge; 3478c2ecf20Sopenharmony_ci u8 cap_ptr; 3488c2ecf20Sopenharmony_ci struct resource *r; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP); 3518c2ecf20Sopenharmony_ci if (!cap_ptr) 3528c2ecf20Sopenharmony_ci return -ENODEV; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci /* Probe for Efficeon controller */ 3558c2ecf20Sopenharmony_ci if (pdev->device != PCI_DEVICE_ID_EFFICEON) { 3568c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "Unsupported Efficeon chipset (device id: %04x)\n", 3578c2ecf20Sopenharmony_ci pdev->device); 3588c2ecf20Sopenharmony_ci return -ENODEV; 3598c2ecf20Sopenharmony_ci } 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci printk(KERN_INFO PFX "Detected Transmeta Efficeon TM8000 series chipset\n"); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci bridge = agp_alloc_bridge(); 3648c2ecf20Sopenharmony_ci if (!bridge) 3658c2ecf20Sopenharmony_ci return -ENOMEM; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci bridge->driver = &efficeon_driver; 3688c2ecf20Sopenharmony_ci bridge->dev = pdev; 3698c2ecf20Sopenharmony_ci bridge->capndx = cap_ptr; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci /* 3728c2ecf20Sopenharmony_ci * If the device has not been properly setup, the following will catch 3738c2ecf20Sopenharmony_ci * the problem and should stop the system from crashing. 3748c2ecf20Sopenharmony_ci * 20030610 - hamish@zot.org 3758c2ecf20Sopenharmony_ci */ 3768c2ecf20Sopenharmony_ci if (pci_enable_device(pdev)) { 3778c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "Unable to Enable PCI device\n"); 3788c2ecf20Sopenharmony_ci agp_put_bridge(bridge); 3798c2ecf20Sopenharmony_ci return -ENODEV; 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci /* 3838c2ecf20Sopenharmony_ci * The following fixes the case where the BIOS has "forgotten" to 3848c2ecf20Sopenharmony_ci * provide an address range for the GART. 3858c2ecf20Sopenharmony_ci * 20030610 - hamish@zot.org 3868c2ecf20Sopenharmony_ci */ 3878c2ecf20Sopenharmony_ci r = &pdev->resource[0]; 3888c2ecf20Sopenharmony_ci if (!r->start && r->end) { 3898c2ecf20Sopenharmony_ci if (pci_assign_resource(pdev, 0)) { 3908c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "could not assign resource 0\n"); 3918c2ecf20Sopenharmony_ci agp_put_bridge(bridge); 3928c2ecf20Sopenharmony_ci return -ENODEV; 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci /* Fill in the mode register */ 3978c2ecf20Sopenharmony_ci if (cap_ptr) { 3988c2ecf20Sopenharmony_ci pci_read_config_dword(pdev, 3998c2ecf20Sopenharmony_ci bridge->capndx+PCI_AGP_STATUS, 4008c2ecf20Sopenharmony_ci &bridge->mode); 4018c2ecf20Sopenharmony_ci } 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, bridge); 4048c2ecf20Sopenharmony_ci return agp_add_bridge(bridge); 4058c2ecf20Sopenharmony_ci} 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cistatic void agp_efficeon_remove(struct pci_dev *pdev) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci struct agp_bridge_data *bridge = pci_get_drvdata(pdev); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci agp_remove_bridge(bridge); 4128c2ecf20Sopenharmony_ci agp_put_bridge(bridge); 4138c2ecf20Sopenharmony_ci} 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 4168c2ecf20Sopenharmony_cistatic int agp_efficeon_suspend(struct pci_dev *dev, pm_message_t state) 4178c2ecf20Sopenharmony_ci{ 4188c2ecf20Sopenharmony_ci return 0; 4198c2ecf20Sopenharmony_ci} 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_cistatic int agp_efficeon_resume(struct pci_dev *pdev) 4228c2ecf20Sopenharmony_ci{ 4238c2ecf20Sopenharmony_ci printk(KERN_DEBUG PFX "agp_efficeon_resume()\n"); 4248c2ecf20Sopenharmony_ci return efficeon_configure(); 4258c2ecf20Sopenharmony_ci} 4268c2ecf20Sopenharmony_ci#endif 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_cistatic const struct pci_device_id agp_efficeon_pci_table[] = { 4298c2ecf20Sopenharmony_ci { 4308c2ecf20Sopenharmony_ci .class = (PCI_CLASS_BRIDGE_HOST << 8), 4318c2ecf20Sopenharmony_ci .class_mask = ~0, 4328c2ecf20Sopenharmony_ci .vendor = PCI_VENDOR_ID_TRANSMETA, 4338c2ecf20Sopenharmony_ci .device = PCI_ANY_ID, 4348c2ecf20Sopenharmony_ci .subvendor = PCI_ANY_ID, 4358c2ecf20Sopenharmony_ci .subdevice = PCI_ANY_ID, 4368c2ecf20Sopenharmony_ci }, 4378c2ecf20Sopenharmony_ci { } 4388c2ecf20Sopenharmony_ci}; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, agp_efficeon_pci_table); 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cistatic struct pci_driver agp_efficeon_pci_driver = { 4438c2ecf20Sopenharmony_ci .name = "agpgart-efficeon", 4448c2ecf20Sopenharmony_ci .id_table = agp_efficeon_pci_table, 4458c2ecf20Sopenharmony_ci .probe = agp_efficeon_probe, 4468c2ecf20Sopenharmony_ci .remove = agp_efficeon_remove, 4478c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 4488c2ecf20Sopenharmony_ci .suspend = agp_efficeon_suspend, 4498c2ecf20Sopenharmony_ci .resume = agp_efficeon_resume, 4508c2ecf20Sopenharmony_ci#endif 4518c2ecf20Sopenharmony_ci}; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_cistatic int __init agp_efficeon_init(void) 4548c2ecf20Sopenharmony_ci{ 4558c2ecf20Sopenharmony_ci static int agp_initialised=0; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci if (agp_off) 4588c2ecf20Sopenharmony_ci return -EINVAL; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci if (agp_initialised == 1) 4618c2ecf20Sopenharmony_ci return 0; 4628c2ecf20Sopenharmony_ci agp_initialised=1; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci return pci_register_driver(&agp_efficeon_pci_driver); 4658c2ecf20Sopenharmony_ci} 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_cistatic void __exit agp_efficeon_cleanup(void) 4688c2ecf20Sopenharmony_ci{ 4698c2ecf20Sopenharmony_ci pci_unregister_driver(&agp_efficeon_pci_driver); 4708c2ecf20Sopenharmony_ci} 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_cimodule_init(agp_efficeon_init); 4738c2ecf20Sopenharmony_cimodule_exit(agp_efficeon_cleanup); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ciMODULE_AUTHOR("Carlos Puchol <cpglinux@puchol.com>"); 4768c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL and additional rights"); 477