162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * AMD K7 AGPGART routines. 362306a36Sopenharmony_ci */ 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci#include <linux/module.h> 662306a36Sopenharmony_ci#include <linux/pci.h> 762306a36Sopenharmony_ci#include <linux/init.h> 862306a36Sopenharmony_ci#include <linux/agp_backend.h> 962306a36Sopenharmony_ci#include <linux/page-flags.h> 1062306a36Sopenharmony_ci#include <linux/mm.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <asm/set_memory.h> 1362306a36Sopenharmony_ci#include "agp.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define AMD_MMBASE_BAR 1 1662306a36Sopenharmony_ci#define AMD_APSIZE 0xac 1762306a36Sopenharmony_ci#define AMD_MODECNTL 0xb0 1862306a36Sopenharmony_ci#define AMD_MODECNTL2 0xb2 1962306a36Sopenharmony_ci#define AMD_GARTENABLE 0x02 /* In mmio region (16-bit register) */ 2062306a36Sopenharmony_ci#define AMD_ATTBASE 0x04 /* In mmio region (32-bit register) */ 2162306a36Sopenharmony_ci#define AMD_TLBFLUSH 0x0c /* In mmio region (32-bit register) */ 2262306a36Sopenharmony_ci#define AMD_CACHEENTRY 0x10 /* In mmio region (32-bit register) */ 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic const struct pci_device_id agp_amdk7_pci_table[]; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistruct amd_page_map { 2762306a36Sopenharmony_ci unsigned long *real; 2862306a36Sopenharmony_ci unsigned long __iomem *remapped; 2962306a36Sopenharmony_ci}; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic struct _amd_irongate_private { 3262306a36Sopenharmony_ci volatile u8 __iomem *registers; 3362306a36Sopenharmony_ci struct amd_page_map **gatt_pages; 3462306a36Sopenharmony_ci int num_tables; 3562306a36Sopenharmony_ci} amd_irongate_private; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic int amd_create_page_map(struct amd_page_map *page_map) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci int i; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci page_map->real = (unsigned long *) __get_free_page(GFP_KERNEL); 4262306a36Sopenharmony_ci if (page_map->real == NULL) 4362306a36Sopenharmony_ci return -ENOMEM; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci set_memory_uc((unsigned long)page_map->real, 1); 4662306a36Sopenharmony_ci page_map->remapped = page_map->real; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci for (i = 0; i < PAGE_SIZE / sizeof(unsigned long); i++) { 4962306a36Sopenharmony_ci writel(agp_bridge->scratch_page, page_map->remapped+i); 5062306a36Sopenharmony_ci readl(page_map->remapped+i); /* PCI Posting. */ 5162306a36Sopenharmony_ci } 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci return 0; 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic void amd_free_page_map(struct amd_page_map *page_map) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci set_memory_wb((unsigned long)page_map->real, 1); 5962306a36Sopenharmony_ci free_page((unsigned long) page_map->real); 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic void amd_free_gatt_pages(void) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci int i; 6562306a36Sopenharmony_ci struct amd_page_map **tables; 6662306a36Sopenharmony_ci struct amd_page_map *entry; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci tables = amd_irongate_private.gatt_pages; 6962306a36Sopenharmony_ci for (i = 0; i < amd_irongate_private.num_tables; i++) { 7062306a36Sopenharmony_ci entry = tables[i]; 7162306a36Sopenharmony_ci if (entry != NULL) { 7262306a36Sopenharmony_ci if (entry->real != NULL) 7362306a36Sopenharmony_ci amd_free_page_map(entry); 7462306a36Sopenharmony_ci kfree(entry); 7562306a36Sopenharmony_ci } 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci kfree(tables); 7862306a36Sopenharmony_ci amd_irongate_private.gatt_pages = NULL; 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic int amd_create_gatt_pages(int nr_tables) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci struct amd_page_map **tables; 8462306a36Sopenharmony_ci struct amd_page_map *entry; 8562306a36Sopenharmony_ci int retval = 0; 8662306a36Sopenharmony_ci int i; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci tables = kcalloc(nr_tables + 1, sizeof(struct amd_page_map *), 8962306a36Sopenharmony_ci GFP_KERNEL); 9062306a36Sopenharmony_ci if (tables == NULL) 9162306a36Sopenharmony_ci return -ENOMEM; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci for (i = 0; i < nr_tables; i++) { 9462306a36Sopenharmony_ci entry = kzalloc(sizeof(struct amd_page_map), GFP_KERNEL); 9562306a36Sopenharmony_ci tables[i] = entry; 9662306a36Sopenharmony_ci if (entry == NULL) { 9762306a36Sopenharmony_ci retval = -ENOMEM; 9862306a36Sopenharmony_ci break; 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci retval = amd_create_page_map(entry); 10162306a36Sopenharmony_ci if (retval != 0) 10262306a36Sopenharmony_ci break; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci amd_irongate_private.num_tables = i; 10562306a36Sopenharmony_ci amd_irongate_private.gatt_pages = tables; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci if (retval != 0) 10862306a36Sopenharmony_ci amd_free_gatt_pages(); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci return retval; 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci/* Since we don't need contiguous memory we just try 11462306a36Sopenharmony_ci * to get the gatt table once 11562306a36Sopenharmony_ci */ 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci#define GET_PAGE_DIR_OFF(addr) (addr >> 22) 11862306a36Sopenharmony_ci#define GET_PAGE_DIR_IDX(addr) (GET_PAGE_DIR_OFF(addr) - \ 11962306a36Sopenharmony_ci GET_PAGE_DIR_OFF(agp_bridge->gart_bus_addr)) 12062306a36Sopenharmony_ci#define GET_GATT_OFF(addr) ((addr & 0x003ff000) >> 12) 12162306a36Sopenharmony_ci#define GET_GATT(addr) (amd_irongate_private.gatt_pages[\ 12262306a36Sopenharmony_ci GET_PAGE_DIR_IDX(addr)]->remapped) 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic int amd_create_gatt_table(struct agp_bridge_data *bridge) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci struct aper_size_info_lvl2 *value; 12762306a36Sopenharmony_ci struct amd_page_map page_dir; 12862306a36Sopenharmony_ci unsigned long __iomem *cur_gatt; 12962306a36Sopenharmony_ci unsigned long addr; 13062306a36Sopenharmony_ci int retval; 13162306a36Sopenharmony_ci int i; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci value = A_SIZE_LVL2(agp_bridge->current_size); 13462306a36Sopenharmony_ci retval = amd_create_page_map(&page_dir); 13562306a36Sopenharmony_ci if (retval != 0) 13662306a36Sopenharmony_ci return retval; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci retval = amd_create_gatt_pages(value->num_entries / 1024); 13962306a36Sopenharmony_ci if (retval != 0) { 14062306a36Sopenharmony_ci amd_free_page_map(&page_dir); 14162306a36Sopenharmony_ci return retval; 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci agp_bridge->gatt_table_real = (u32 *)page_dir.real; 14562306a36Sopenharmony_ci agp_bridge->gatt_table = (u32 __iomem *)page_dir.remapped; 14662306a36Sopenharmony_ci agp_bridge->gatt_bus_addr = virt_to_phys(page_dir.real); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci /* Get the address for the gart region. 14962306a36Sopenharmony_ci * This is a bus address even on the alpha, b/c its 15062306a36Sopenharmony_ci * used to program the agp master not the cpu 15162306a36Sopenharmony_ci */ 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci addr = pci_bus_address(agp_bridge->dev, AGP_APERTURE_BAR); 15462306a36Sopenharmony_ci agp_bridge->gart_bus_addr = addr; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci /* Calculate the agp offset */ 15762306a36Sopenharmony_ci for (i = 0; i < value->num_entries / 1024; i++, addr += 0x00400000) { 15862306a36Sopenharmony_ci writel(virt_to_phys(amd_irongate_private.gatt_pages[i]->real) | 1, 15962306a36Sopenharmony_ci page_dir.remapped+GET_PAGE_DIR_OFF(addr)); 16062306a36Sopenharmony_ci readl(page_dir.remapped+GET_PAGE_DIR_OFF(addr)); /* PCI Posting. */ 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci for (i = 0; i < value->num_entries; i++) { 16462306a36Sopenharmony_ci addr = (i * PAGE_SIZE) + agp_bridge->gart_bus_addr; 16562306a36Sopenharmony_ci cur_gatt = GET_GATT(addr); 16662306a36Sopenharmony_ci writel(agp_bridge->scratch_page, cur_gatt+GET_GATT_OFF(addr)); 16762306a36Sopenharmony_ci readl(cur_gatt+GET_GATT_OFF(addr)); /* PCI Posting. */ 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci return 0; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic int amd_free_gatt_table(struct agp_bridge_data *bridge) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci struct amd_page_map page_dir; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci page_dir.real = (unsigned long *)agp_bridge->gatt_table_real; 17862306a36Sopenharmony_ci page_dir.remapped = (unsigned long __iomem *)agp_bridge->gatt_table; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci amd_free_gatt_pages(); 18162306a36Sopenharmony_ci amd_free_page_map(&page_dir); 18262306a36Sopenharmony_ci return 0; 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic int amd_irongate_fetch_size(void) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci int i; 18862306a36Sopenharmony_ci u32 temp; 18962306a36Sopenharmony_ci struct aper_size_info_lvl2 *values; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci pci_read_config_dword(agp_bridge->dev, AMD_APSIZE, &temp); 19262306a36Sopenharmony_ci temp = (temp & 0x0000000e); 19362306a36Sopenharmony_ci values = A_SIZE_LVL2(agp_bridge->driver->aperture_sizes); 19462306a36Sopenharmony_ci for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++) { 19562306a36Sopenharmony_ci if (temp == values[i].size_value) { 19662306a36Sopenharmony_ci agp_bridge->previous_size = 19762306a36Sopenharmony_ci agp_bridge->current_size = (void *) (values + i); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci agp_bridge->aperture_size_idx = i; 20062306a36Sopenharmony_ci return values[i].size; 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci return 0; 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic int amd_irongate_configure(void) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci struct aper_size_info_lvl2 *current_size; 21062306a36Sopenharmony_ci phys_addr_t reg; 21162306a36Sopenharmony_ci u32 temp; 21262306a36Sopenharmony_ci u16 enable_reg; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci current_size = A_SIZE_LVL2(agp_bridge->current_size); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci if (!amd_irongate_private.registers) { 21762306a36Sopenharmony_ci /* Get the memory mapped registers */ 21862306a36Sopenharmony_ci reg = pci_resource_start(agp_bridge->dev, AMD_MMBASE_BAR); 21962306a36Sopenharmony_ci amd_irongate_private.registers = (volatile u8 __iomem *) ioremap(reg, 4096); 22062306a36Sopenharmony_ci if (!amd_irongate_private.registers) 22162306a36Sopenharmony_ci return -ENOMEM; 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci /* Write out the address of the gatt table */ 22562306a36Sopenharmony_ci writel(agp_bridge->gatt_bus_addr, amd_irongate_private.registers+AMD_ATTBASE); 22662306a36Sopenharmony_ci readl(amd_irongate_private.registers+AMD_ATTBASE); /* PCI Posting. */ 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci /* Write the Sync register */ 22962306a36Sopenharmony_ci pci_write_config_byte(agp_bridge->dev, AMD_MODECNTL, 0x80); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci /* Set indexing mode */ 23262306a36Sopenharmony_ci pci_write_config_byte(agp_bridge->dev, AMD_MODECNTL2, 0x00); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci /* Write the enable register */ 23562306a36Sopenharmony_ci enable_reg = readw(amd_irongate_private.registers+AMD_GARTENABLE); 23662306a36Sopenharmony_ci enable_reg = (enable_reg | 0x0004); 23762306a36Sopenharmony_ci writew(enable_reg, amd_irongate_private.registers+AMD_GARTENABLE); 23862306a36Sopenharmony_ci readw(amd_irongate_private.registers+AMD_GARTENABLE); /* PCI Posting. */ 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci /* Write out the size register */ 24162306a36Sopenharmony_ci pci_read_config_dword(agp_bridge->dev, AMD_APSIZE, &temp); 24262306a36Sopenharmony_ci temp = (((temp & ~(0x0000000e)) | current_size->size_value) | 1); 24362306a36Sopenharmony_ci pci_write_config_dword(agp_bridge->dev, AMD_APSIZE, temp); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci /* Flush the tlb */ 24662306a36Sopenharmony_ci writel(1, amd_irongate_private.registers+AMD_TLBFLUSH); 24762306a36Sopenharmony_ci readl(amd_irongate_private.registers+AMD_TLBFLUSH); /* PCI Posting.*/ 24862306a36Sopenharmony_ci return 0; 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic void amd_irongate_cleanup(void) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci struct aper_size_info_lvl2 *previous_size; 25462306a36Sopenharmony_ci u32 temp; 25562306a36Sopenharmony_ci u16 enable_reg; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci previous_size = A_SIZE_LVL2(agp_bridge->previous_size); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci enable_reg = readw(amd_irongate_private.registers+AMD_GARTENABLE); 26062306a36Sopenharmony_ci enable_reg = (enable_reg & ~(0x0004)); 26162306a36Sopenharmony_ci writew(enable_reg, amd_irongate_private.registers+AMD_GARTENABLE); 26262306a36Sopenharmony_ci readw(amd_irongate_private.registers+AMD_GARTENABLE); /* PCI Posting. */ 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci /* Write back the previous size and disable gart translation */ 26562306a36Sopenharmony_ci pci_read_config_dword(agp_bridge->dev, AMD_APSIZE, &temp); 26662306a36Sopenharmony_ci temp = ((temp & ~(0x0000000f)) | previous_size->size_value); 26762306a36Sopenharmony_ci pci_write_config_dword(agp_bridge->dev, AMD_APSIZE, temp); 26862306a36Sopenharmony_ci iounmap((void __iomem *) amd_irongate_private.registers); 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci/* 27262306a36Sopenharmony_ci * This routine could be implemented by taking the addresses 27362306a36Sopenharmony_ci * written to the GATT, and flushing them individually. However 27462306a36Sopenharmony_ci * currently it just flushes the whole table. Which is probably 27562306a36Sopenharmony_ci * more efficient, since agp_memory blocks can be a large number of 27662306a36Sopenharmony_ci * entries. 27762306a36Sopenharmony_ci */ 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cistatic void amd_irongate_tlbflush(struct agp_memory *temp) 28062306a36Sopenharmony_ci{ 28162306a36Sopenharmony_ci writel(1, amd_irongate_private.registers+AMD_TLBFLUSH); 28262306a36Sopenharmony_ci readl(amd_irongate_private.registers+AMD_TLBFLUSH); /* PCI Posting. */ 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_cistatic int amd_insert_memory(struct agp_memory *mem, off_t pg_start, int type) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci int i, j, num_entries; 28862306a36Sopenharmony_ci unsigned long __iomem *cur_gatt; 28962306a36Sopenharmony_ci unsigned long addr; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci num_entries = A_SIZE_LVL2(agp_bridge->current_size)->num_entries; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci if (type != mem->type || 29462306a36Sopenharmony_ci agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type)) 29562306a36Sopenharmony_ci return -EINVAL; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci if ((pg_start + mem->page_count) > num_entries) 29862306a36Sopenharmony_ci return -EINVAL; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci j = pg_start; 30162306a36Sopenharmony_ci while (j < (pg_start + mem->page_count)) { 30262306a36Sopenharmony_ci addr = (j * PAGE_SIZE) + agp_bridge->gart_bus_addr; 30362306a36Sopenharmony_ci cur_gatt = GET_GATT(addr); 30462306a36Sopenharmony_ci if (!PGE_EMPTY(agp_bridge, readl(cur_gatt+GET_GATT_OFF(addr)))) 30562306a36Sopenharmony_ci return -EBUSY; 30662306a36Sopenharmony_ci j++; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci if (!mem->is_flushed) { 31062306a36Sopenharmony_ci global_cache_flush(); 31162306a36Sopenharmony_ci mem->is_flushed = true; 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { 31562306a36Sopenharmony_ci addr = (j * PAGE_SIZE) + agp_bridge->gart_bus_addr; 31662306a36Sopenharmony_ci cur_gatt = GET_GATT(addr); 31762306a36Sopenharmony_ci writel(agp_generic_mask_memory(agp_bridge, 31862306a36Sopenharmony_ci page_to_phys(mem->pages[i]), 31962306a36Sopenharmony_ci mem->type), 32062306a36Sopenharmony_ci cur_gatt+GET_GATT_OFF(addr)); 32162306a36Sopenharmony_ci readl(cur_gatt+GET_GATT_OFF(addr)); /* PCI Posting. */ 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci amd_irongate_tlbflush(mem); 32462306a36Sopenharmony_ci return 0; 32562306a36Sopenharmony_ci} 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_cistatic int amd_remove_memory(struct agp_memory *mem, off_t pg_start, int type) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci int i; 33062306a36Sopenharmony_ci unsigned long __iomem *cur_gatt; 33162306a36Sopenharmony_ci unsigned long addr; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci if (type != mem->type || 33462306a36Sopenharmony_ci agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type)) 33562306a36Sopenharmony_ci return -EINVAL; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci for (i = pg_start; i < (mem->page_count + pg_start); i++) { 33862306a36Sopenharmony_ci addr = (i * PAGE_SIZE) + agp_bridge->gart_bus_addr; 33962306a36Sopenharmony_ci cur_gatt = GET_GATT(addr); 34062306a36Sopenharmony_ci writel(agp_bridge->scratch_page, cur_gatt+GET_GATT_OFF(addr)); 34162306a36Sopenharmony_ci readl(cur_gatt+GET_GATT_OFF(addr)); /* PCI Posting. */ 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci amd_irongate_tlbflush(mem); 34562306a36Sopenharmony_ci return 0; 34662306a36Sopenharmony_ci} 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_cistatic const struct aper_size_info_lvl2 amd_irongate_sizes[7] = 34962306a36Sopenharmony_ci{ 35062306a36Sopenharmony_ci {2048, 524288, 0x0000000c}, 35162306a36Sopenharmony_ci {1024, 262144, 0x0000000a}, 35262306a36Sopenharmony_ci {512, 131072, 0x00000008}, 35362306a36Sopenharmony_ci {256, 65536, 0x00000006}, 35462306a36Sopenharmony_ci {128, 32768, 0x00000004}, 35562306a36Sopenharmony_ci {64, 16384, 0x00000002}, 35662306a36Sopenharmony_ci {32, 8192, 0x00000000} 35762306a36Sopenharmony_ci}; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_cistatic const struct gatt_mask amd_irongate_masks[] = 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci {.mask = 1, .type = 0} 36262306a36Sopenharmony_ci}; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_cistatic const struct agp_bridge_driver amd_irongate_driver = { 36562306a36Sopenharmony_ci .owner = THIS_MODULE, 36662306a36Sopenharmony_ci .aperture_sizes = amd_irongate_sizes, 36762306a36Sopenharmony_ci .size_type = LVL2_APER_SIZE, 36862306a36Sopenharmony_ci .num_aperture_sizes = 7, 36962306a36Sopenharmony_ci .needs_scratch_page = true, 37062306a36Sopenharmony_ci .configure = amd_irongate_configure, 37162306a36Sopenharmony_ci .fetch_size = amd_irongate_fetch_size, 37262306a36Sopenharmony_ci .cleanup = amd_irongate_cleanup, 37362306a36Sopenharmony_ci .tlb_flush = amd_irongate_tlbflush, 37462306a36Sopenharmony_ci .mask_memory = agp_generic_mask_memory, 37562306a36Sopenharmony_ci .masks = amd_irongate_masks, 37662306a36Sopenharmony_ci .agp_enable = agp_generic_enable, 37762306a36Sopenharmony_ci .cache_flush = global_cache_flush, 37862306a36Sopenharmony_ci .create_gatt_table = amd_create_gatt_table, 37962306a36Sopenharmony_ci .free_gatt_table = amd_free_gatt_table, 38062306a36Sopenharmony_ci .insert_memory = amd_insert_memory, 38162306a36Sopenharmony_ci .remove_memory = amd_remove_memory, 38262306a36Sopenharmony_ci .alloc_by_type = agp_generic_alloc_by_type, 38362306a36Sopenharmony_ci .free_by_type = agp_generic_free_by_type, 38462306a36Sopenharmony_ci .agp_alloc_page = agp_generic_alloc_page, 38562306a36Sopenharmony_ci .agp_alloc_pages = agp_generic_alloc_pages, 38662306a36Sopenharmony_ci .agp_destroy_page = agp_generic_destroy_page, 38762306a36Sopenharmony_ci .agp_destroy_pages = agp_generic_destroy_pages, 38862306a36Sopenharmony_ci .agp_type_to_mask_type = agp_generic_type_to_mask_type, 38962306a36Sopenharmony_ci}; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_cistatic struct agp_device_ids amd_agp_device_ids[] = 39262306a36Sopenharmony_ci{ 39362306a36Sopenharmony_ci { 39462306a36Sopenharmony_ci .device_id = PCI_DEVICE_ID_AMD_FE_GATE_7006, 39562306a36Sopenharmony_ci .chipset_name = "Irongate", 39662306a36Sopenharmony_ci }, 39762306a36Sopenharmony_ci { 39862306a36Sopenharmony_ci .device_id = PCI_DEVICE_ID_AMD_FE_GATE_700E, 39962306a36Sopenharmony_ci .chipset_name = "761", 40062306a36Sopenharmony_ci }, 40162306a36Sopenharmony_ci { 40262306a36Sopenharmony_ci .device_id = PCI_DEVICE_ID_AMD_FE_GATE_700C, 40362306a36Sopenharmony_ci .chipset_name = "760MP", 40462306a36Sopenharmony_ci }, 40562306a36Sopenharmony_ci { }, /* dummy final entry, always present */ 40662306a36Sopenharmony_ci}; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_cistatic int agp_amdk7_probe(struct pci_dev *pdev, 40962306a36Sopenharmony_ci const struct pci_device_id *ent) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci struct agp_bridge_data *bridge; 41262306a36Sopenharmony_ci u8 cap_ptr; 41362306a36Sopenharmony_ci int j; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP); 41662306a36Sopenharmony_ci if (!cap_ptr) 41762306a36Sopenharmony_ci return -ENODEV; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci j = ent - agp_amdk7_pci_table; 42062306a36Sopenharmony_ci dev_info(&pdev->dev, "AMD %s chipset\n", 42162306a36Sopenharmony_ci amd_agp_device_ids[j].chipset_name); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci bridge = agp_alloc_bridge(); 42462306a36Sopenharmony_ci if (!bridge) 42562306a36Sopenharmony_ci return -ENOMEM; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci bridge->driver = &amd_irongate_driver; 42862306a36Sopenharmony_ci bridge->dev_private_data = &amd_irongate_private; 42962306a36Sopenharmony_ci bridge->dev = pdev; 43062306a36Sopenharmony_ci bridge->capndx = cap_ptr; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci /* 751 Errata (22564_B-1.PDF) 43362306a36Sopenharmony_ci erratum 20: strobe glitch with Nvidia NV10 GeForce cards. 43462306a36Sopenharmony_ci system controller may experience noise due to strong drive strengths 43562306a36Sopenharmony_ci */ 43662306a36Sopenharmony_ci if (agp_bridge->dev->device == PCI_DEVICE_ID_AMD_FE_GATE_7006) { 43762306a36Sopenharmony_ci struct pci_dev *gfxcard=NULL; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci cap_ptr = 0; 44062306a36Sopenharmony_ci while (!cap_ptr) { 44162306a36Sopenharmony_ci gfxcard = pci_get_class(PCI_CLASS_DISPLAY_VGA<<8, gfxcard); 44262306a36Sopenharmony_ci if (!gfxcard) { 44362306a36Sopenharmony_ci dev_info(&pdev->dev, "no AGP VGA controller\n"); 44462306a36Sopenharmony_ci return -ENODEV; 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci cap_ptr = pci_find_capability(gfxcard, PCI_CAP_ID_AGP); 44762306a36Sopenharmony_ci } 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci /* With so many variants of NVidia cards, it's simpler just 45062306a36Sopenharmony_ci to blacklist them all, and then whitelist them as needed 45162306a36Sopenharmony_ci (if necessary at all). */ 45262306a36Sopenharmony_ci if (gfxcard->vendor == PCI_VENDOR_ID_NVIDIA) { 45362306a36Sopenharmony_ci agp_bridge->flags |= AGP_ERRATA_1X; 45462306a36Sopenharmony_ci dev_info(&pdev->dev, "AMD 751 chipset with NVidia GeForce; forcing 1X due to errata\n"); 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci pci_dev_put(gfxcard); 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci /* 761 Errata (23613_F.pdf) 46062306a36Sopenharmony_ci * Revisions B0/B1 were a disaster. 46162306a36Sopenharmony_ci * erratum 44: SYSCLK/AGPCLK skew causes 2X failures -- Force mode to 1X 46262306a36Sopenharmony_ci * erratum 45: Timing problem prevents fast writes -- Disable fast write. 46362306a36Sopenharmony_ci * erratum 46: Setup violation on AGP SBA pins - Disable side band addressing. 46462306a36Sopenharmony_ci * With this lot disabled, we should prevent lockups. */ 46562306a36Sopenharmony_ci if (agp_bridge->dev->device == PCI_DEVICE_ID_AMD_FE_GATE_700E) { 46662306a36Sopenharmony_ci if (pdev->revision == 0x10 || pdev->revision == 0x11) { 46762306a36Sopenharmony_ci agp_bridge->flags = AGP_ERRATA_FASTWRITES; 46862306a36Sopenharmony_ci agp_bridge->flags |= AGP_ERRATA_SBA; 46962306a36Sopenharmony_ci agp_bridge->flags |= AGP_ERRATA_1X; 47062306a36Sopenharmony_ci dev_info(&pdev->dev, "AMD 761 chipset with errata; disabling AGP fast writes & SBA and forcing to 1X\n"); 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci /* Fill in the mode register */ 47562306a36Sopenharmony_ci pci_read_config_dword(pdev, 47662306a36Sopenharmony_ci bridge->capndx+PCI_AGP_STATUS, 47762306a36Sopenharmony_ci &bridge->mode); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci pci_set_drvdata(pdev, bridge); 48062306a36Sopenharmony_ci return agp_add_bridge(bridge); 48162306a36Sopenharmony_ci} 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_cistatic void agp_amdk7_remove(struct pci_dev *pdev) 48462306a36Sopenharmony_ci{ 48562306a36Sopenharmony_ci struct agp_bridge_data *bridge = pci_get_drvdata(pdev); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci agp_remove_bridge(bridge); 48862306a36Sopenharmony_ci agp_put_bridge(bridge); 48962306a36Sopenharmony_ci} 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_cistatic int agp_amdk7_resume(struct device *dev) 49262306a36Sopenharmony_ci{ 49362306a36Sopenharmony_ci return amd_irongate_driver.configure(); 49462306a36Sopenharmony_ci} 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci/* must be the same order as name table above */ 49762306a36Sopenharmony_cistatic const struct pci_device_id agp_amdk7_pci_table[] = { 49862306a36Sopenharmony_ci { 49962306a36Sopenharmony_ci .class = (PCI_CLASS_BRIDGE_HOST << 8), 50062306a36Sopenharmony_ci .class_mask = ~0, 50162306a36Sopenharmony_ci .vendor = PCI_VENDOR_ID_AMD, 50262306a36Sopenharmony_ci .device = PCI_DEVICE_ID_AMD_FE_GATE_7006, 50362306a36Sopenharmony_ci .subvendor = PCI_ANY_ID, 50462306a36Sopenharmony_ci .subdevice = PCI_ANY_ID, 50562306a36Sopenharmony_ci }, 50662306a36Sopenharmony_ci { 50762306a36Sopenharmony_ci .class = (PCI_CLASS_BRIDGE_HOST << 8), 50862306a36Sopenharmony_ci .class_mask = ~0, 50962306a36Sopenharmony_ci .vendor = PCI_VENDOR_ID_AMD, 51062306a36Sopenharmony_ci .device = PCI_DEVICE_ID_AMD_FE_GATE_700E, 51162306a36Sopenharmony_ci .subvendor = PCI_ANY_ID, 51262306a36Sopenharmony_ci .subdevice = PCI_ANY_ID, 51362306a36Sopenharmony_ci }, 51462306a36Sopenharmony_ci { 51562306a36Sopenharmony_ci .class = (PCI_CLASS_BRIDGE_HOST << 8), 51662306a36Sopenharmony_ci .class_mask = ~0, 51762306a36Sopenharmony_ci .vendor = PCI_VENDOR_ID_AMD, 51862306a36Sopenharmony_ci .device = PCI_DEVICE_ID_AMD_FE_GATE_700C, 51962306a36Sopenharmony_ci .subvendor = PCI_ANY_ID, 52062306a36Sopenharmony_ci .subdevice = PCI_ANY_ID, 52162306a36Sopenharmony_ci }, 52262306a36Sopenharmony_ci { } 52362306a36Sopenharmony_ci}; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, agp_amdk7_pci_table); 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_cistatic DEFINE_SIMPLE_DEV_PM_OPS(agp_amdk7_pm_ops, NULL, agp_amdk7_resume); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_cistatic struct pci_driver agp_amdk7_pci_driver = { 53062306a36Sopenharmony_ci .name = "agpgart-amdk7", 53162306a36Sopenharmony_ci .id_table = agp_amdk7_pci_table, 53262306a36Sopenharmony_ci .probe = agp_amdk7_probe, 53362306a36Sopenharmony_ci .remove = agp_amdk7_remove, 53462306a36Sopenharmony_ci .driver.pm = &agp_amdk7_pm_ops, 53562306a36Sopenharmony_ci}; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_cistatic int __init agp_amdk7_init(void) 53862306a36Sopenharmony_ci{ 53962306a36Sopenharmony_ci if (agp_off) 54062306a36Sopenharmony_ci return -EINVAL; 54162306a36Sopenharmony_ci return pci_register_driver(&agp_amdk7_pci_driver); 54262306a36Sopenharmony_ci} 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_cistatic void __exit agp_amdk7_cleanup(void) 54562306a36Sopenharmony_ci{ 54662306a36Sopenharmony_ci pci_unregister_driver(&agp_amdk7_pci_driver); 54762306a36Sopenharmony_ci} 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_cimodule_init(agp_amdk7_init); 55062306a36Sopenharmony_cimodule_exit(agp_amdk7_cleanup); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ciMODULE_LICENSE("GPL and additional rights"); 553