162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Serverworks 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/string.h> 962306a36Sopenharmony_ci#include <linux/slab.h> 1062306a36Sopenharmony_ci#include <linux/jiffies.h> 1162306a36Sopenharmony_ci#include <linux/agp_backend.h> 1262306a36Sopenharmony_ci#include <asm/set_memory.h> 1362306a36Sopenharmony_ci#include "agp.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define SVWRKS_COMMAND 0x04 1662306a36Sopenharmony_ci#define SVWRKS_APSIZE 0x10 1762306a36Sopenharmony_ci#define SVWRKS_MMBASE 0x14 1862306a36Sopenharmony_ci#define SVWRKS_CACHING 0x4b 1962306a36Sopenharmony_ci#define SVWRKS_AGP_ENABLE 0x60 2062306a36Sopenharmony_ci#define SVWRKS_FEATURE 0x68 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define SVWRKS_SIZE_MASK 0xfe000000 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* Memory mapped registers */ 2562306a36Sopenharmony_ci#define SVWRKS_GART_CACHE 0x02 2662306a36Sopenharmony_ci#define SVWRKS_GATTBASE 0x04 2762306a36Sopenharmony_ci#define SVWRKS_TLBFLUSH 0x10 2862306a36Sopenharmony_ci#define SVWRKS_POSTFLUSH 0x14 2962306a36Sopenharmony_ci#define SVWRKS_DIRFLUSH 0x0c 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistruct serverworks_page_map { 3362306a36Sopenharmony_ci unsigned long *real; 3462306a36Sopenharmony_ci unsigned long __iomem *remapped; 3562306a36Sopenharmony_ci}; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic struct _serverworks_private { 3862306a36Sopenharmony_ci struct pci_dev *svrwrks_dev; /* device one */ 3962306a36Sopenharmony_ci volatile u8 __iomem *registers; 4062306a36Sopenharmony_ci struct serverworks_page_map **gatt_pages; 4162306a36Sopenharmony_ci int num_tables; 4262306a36Sopenharmony_ci struct serverworks_page_map scratch_dir; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci int gart_addr_ofs; 4562306a36Sopenharmony_ci int mm_addr_ofs; 4662306a36Sopenharmony_ci} serverworks_private; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic int serverworks_create_page_map(struct serverworks_page_map *page_map) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci int i; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci page_map->real = (unsigned long *) __get_free_page(GFP_KERNEL); 5362306a36Sopenharmony_ci if (page_map->real == NULL) { 5462306a36Sopenharmony_ci return -ENOMEM; 5562306a36Sopenharmony_ci } 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci set_memory_uc((unsigned long)page_map->real, 1); 5862306a36Sopenharmony_ci page_map->remapped = page_map->real; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci for (i = 0; i < PAGE_SIZE / sizeof(unsigned long); i++) 6162306a36Sopenharmony_ci writel(agp_bridge->scratch_page, page_map->remapped+i); 6262306a36Sopenharmony_ci /* Red Pen: Everyone else does pci posting flush here */ 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci return 0; 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic void serverworks_free_page_map(struct serverworks_page_map *page_map) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci set_memory_wb((unsigned long)page_map->real, 1); 7062306a36Sopenharmony_ci free_page((unsigned long) page_map->real); 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic void serverworks_free_gatt_pages(void) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci int i; 7662306a36Sopenharmony_ci struct serverworks_page_map **tables; 7762306a36Sopenharmony_ci struct serverworks_page_map *entry; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci tables = serverworks_private.gatt_pages; 8062306a36Sopenharmony_ci for (i = 0; i < serverworks_private.num_tables; i++) { 8162306a36Sopenharmony_ci entry = tables[i]; 8262306a36Sopenharmony_ci if (entry != NULL) { 8362306a36Sopenharmony_ci if (entry->real != NULL) { 8462306a36Sopenharmony_ci serverworks_free_page_map(entry); 8562306a36Sopenharmony_ci } 8662306a36Sopenharmony_ci kfree(entry); 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci } 8962306a36Sopenharmony_ci kfree(tables); 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic int serverworks_create_gatt_pages(int nr_tables) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci struct serverworks_page_map **tables; 9562306a36Sopenharmony_ci struct serverworks_page_map *entry; 9662306a36Sopenharmony_ci int retval = 0; 9762306a36Sopenharmony_ci int i; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci tables = kcalloc(nr_tables + 1, sizeof(struct serverworks_page_map *), 10062306a36Sopenharmony_ci GFP_KERNEL); 10162306a36Sopenharmony_ci if (tables == NULL) 10262306a36Sopenharmony_ci return -ENOMEM; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci for (i = 0; i < nr_tables; i++) { 10562306a36Sopenharmony_ci entry = kzalloc(sizeof(struct serverworks_page_map), GFP_KERNEL); 10662306a36Sopenharmony_ci if (entry == NULL) { 10762306a36Sopenharmony_ci retval = -ENOMEM; 10862306a36Sopenharmony_ci break; 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci tables[i] = entry; 11162306a36Sopenharmony_ci retval = serverworks_create_page_map(entry); 11262306a36Sopenharmony_ci if (retval != 0) break; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci serverworks_private.num_tables = nr_tables; 11562306a36Sopenharmony_ci serverworks_private.gatt_pages = tables; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci if (retval != 0) serverworks_free_gatt_pages(); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci return retval; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci#define SVRWRKS_GET_GATT(addr) (serverworks_private.gatt_pages[\ 12362306a36Sopenharmony_ci GET_PAGE_DIR_IDX(addr)]->remapped) 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci#ifndef GET_PAGE_DIR_OFF 12662306a36Sopenharmony_ci#define GET_PAGE_DIR_OFF(addr) (addr >> 22) 12762306a36Sopenharmony_ci#endif 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci#ifndef GET_PAGE_DIR_IDX 13062306a36Sopenharmony_ci#define GET_PAGE_DIR_IDX(addr) (GET_PAGE_DIR_OFF(addr) - \ 13162306a36Sopenharmony_ci GET_PAGE_DIR_OFF(agp_bridge->gart_bus_addr)) 13262306a36Sopenharmony_ci#endif 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci#ifndef GET_GATT_OFF 13562306a36Sopenharmony_ci#define GET_GATT_OFF(addr) ((addr & 0x003ff000) >> 12) 13662306a36Sopenharmony_ci#endif 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic int serverworks_create_gatt_table(struct agp_bridge_data *bridge) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci struct aper_size_info_lvl2 *value; 14162306a36Sopenharmony_ci struct serverworks_page_map page_dir; 14262306a36Sopenharmony_ci int retval; 14362306a36Sopenharmony_ci u32 temp; 14462306a36Sopenharmony_ci int i; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci value = A_SIZE_LVL2(agp_bridge->current_size); 14762306a36Sopenharmony_ci retval = serverworks_create_page_map(&page_dir); 14862306a36Sopenharmony_ci if (retval != 0) { 14962306a36Sopenharmony_ci return retval; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci retval = serverworks_create_page_map(&serverworks_private.scratch_dir); 15262306a36Sopenharmony_ci if (retval != 0) { 15362306a36Sopenharmony_ci serverworks_free_page_map(&page_dir); 15462306a36Sopenharmony_ci return retval; 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci /* Create a fake scratch directory */ 15762306a36Sopenharmony_ci for (i = 0; i < 1024; i++) { 15862306a36Sopenharmony_ci writel(agp_bridge->scratch_page, serverworks_private.scratch_dir.remapped+i); 15962306a36Sopenharmony_ci writel(virt_to_phys(serverworks_private.scratch_dir.real) | 1, page_dir.remapped+i); 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci retval = serverworks_create_gatt_pages(value->num_entries / 1024); 16362306a36Sopenharmony_ci if (retval != 0) { 16462306a36Sopenharmony_ci serverworks_free_page_map(&page_dir); 16562306a36Sopenharmony_ci serverworks_free_page_map(&serverworks_private.scratch_dir); 16662306a36Sopenharmony_ci return retval; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci agp_bridge->gatt_table_real = (u32 *)page_dir.real; 17062306a36Sopenharmony_ci agp_bridge->gatt_table = (u32 __iomem *)page_dir.remapped; 17162306a36Sopenharmony_ci agp_bridge->gatt_bus_addr = virt_to_phys(page_dir.real); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci /* Get the address for the gart region. 17462306a36Sopenharmony_ci * This is a bus address even on the alpha, b/c its 17562306a36Sopenharmony_ci * used to program the agp master not the cpu 17662306a36Sopenharmony_ci */ 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci pci_read_config_dword(agp_bridge->dev,serverworks_private.gart_addr_ofs,&temp); 17962306a36Sopenharmony_ci agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci /* Calculate the agp offset */ 18262306a36Sopenharmony_ci for (i = 0; i < value->num_entries / 1024; i++) 18362306a36Sopenharmony_ci writel(virt_to_phys(serverworks_private.gatt_pages[i]->real)|1, page_dir.remapped+i); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci return 0; 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic int serverworks_free_gatt_table(struct agp_bridge_data *bridge) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci struct serverworks_page_map page_dir; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci page_dir.real = (unsigned long *)agp_bridge->gatt_table_real; 19362306a36Sopenharmony_ci page_dir.remapped = (unsigned long __iomem *)agp_bridge->gatt_table; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci serverworks_free_gatt_pages(); 19662306a36Sopenharmony_ci serverworks_free_page_map(&page_dir); 19762306a36Sopenharmony_ci serverworks_free_page_map(&serverworks_private.scratch_dir); 19862306a36Sopenharmony_ci return 0; 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic int serverworks_fetch_size(void) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci int i; 20462306a36Sopenharmony_ci u32 temp; 20562306a36Sopenharmony_ci u32 temp2; 20662306a36Sopenharmony_ci struct aper_size_info_lvl2 *values; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci values = A_SIZE_LVL2(agp_bridge->driver->aperture_sizes); 20962306a36Sopenharmony_ci pci_read_config_dword(agp_bridge->dev,serverworks_private.gart_addr_ofs,&temp); 21062306a36Sopenharmony_ci pci_write_config_dword(agp_bridge->dev,serverworks_private.gart_addr_ofs, 21162306a36Sopenharmony_ci SVWRKS_SIZE_MASK); 21262306a36Sopenharmony_ci pci_read_config_dword(agp_bridge->dev,serverworks_private.gart_addr_ofs,&temp2); 21362306a36Sopenharmony_ci pci_write_config_dword(agp_bridge->dev,serverworks_private.gart_addr_ofs,temp); 21462306a36Sopenharmony_ci temp2 &= SVWRKS_SIZE_MASK; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++) { 21762306a36Sopenharmony_ci if (temp2 == values[i].size_value) { 21862306a36Sopenharmony_ci agp_bridge->previous_size = 21962306a36Sopenharmony_ci agp_bridge->current_size = (void *) (values + i); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci agp_bridge->aperture_size_idx = i; 22262306a36Sopenharmony_ci return values[i].size; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci return 0; 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci/* 23062306a36Sopenharmony_ci * This routine could be implemented by taking the addresses 23162306a36Sopenharmony_ci * written to the GATT, and flushing them individually. However 23262306a36Sopenharmony_ci * currently it just flushes the whole table. Which is probably 23362306a36Sopenharmony_ci * more efficient, since agp_memory blocks can be a large number of 23462306a36Sopenharmony_ci * entries. 23562306a36Sopenharmony_ci */ 23662306a36Sopenharmony_cistatic void serverworks_tlbflush(struct agp_memory *temp) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci unsigned long timeout; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci writeb(1, serverworks_private.registers+SVWRKS_POSTFLUSH); 24162306a36Sopenharmony_ci timeout = jiffies + 3*HZ; 24262306a36Sopenharmony_ci while (readb(serverworks_private.registers+SVWRKS_POSTFLUSH) == 1) { 24362306a36Sopenharmony_ci cpu_relax(); 24462306a36Sopenharmony_ci if (time_after(jiffies, timeout)) { 24562306a36Sopenharmony_ci dev_err(&serverworks_private.svrwrks_dev->dev, 24662306a36Sopenharmony_ci "TLB post flush took more than 3 seconds\n"); 24762306a36Sopenharmony_ci break; 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci writel(1, serverworks_private.registers+SVWRKS_DIRFLUSH); 25262306a36Sopenharmony_ci timeout = jiffies + 3*HZ; 25362306a36Sopenharmony_ci while (readl(serverworks_private.registers+SVWRKS_DIRFLUSH) == 1) { 25462306a36Sopenharmony_ci cpu_relax(); 25562306a36Sopenharmony_ci if (time_after(jiffies, timeout)) { 25662306a36Sopenharmony_ci dev_err(&serverworks_private.svrwrks_dev->dev, 25762306a36Sopenharmony_ci "TLB Dir flush took more than 3 seconds\n"); 25862306a36Sopenharmony_ci break; 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cistatic int serverworks_configure(void) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci u32 temp; 26662306a36Sopenharmony_ci u8 enable_reg; 26762306a36Sopenharmony_ci u16 cap_reg; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci /* Get the memory mapped registers */ 27062306a36Sopenharmony_ci pci_read_config_dword(agp_bridge->dev, serverworks_private.mm_addr_ofs, &temp); 27162306a36Sopenharmony_ci temp = (temp & PCI_BASE_ADDRESS_MEM_MASK); 27262306a36Sopenharmony_ci serverworks_private.registers = (volatile u8 __iomem *) ioremap(temp, 4096); 27362306a36Sopenharmony_ci if (!serverworks_private.registers) { 27462306a36Sopenharmony_ci dev_err(&agp_bridge->dev->dev, "can't ioremap(%#x)\n", temp); 27562306a36Sopenharmony_ci return -ENOMEM; 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci writeb(0xA, serverworks_private.registers+SVWRKS_GART_CACHE); 27962306a36Sopenharmony_ci readb(serverworks_private.registers+SVWRKS_GART_CACHE); /* PCI Posting. */ 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci writel(agp_bridge->gatt_bus_addr, serverworks_private.registers+SVWRKS_GATTBASE); 28262306a36Sopenharmony_ci readl(serverworks_private.registers+SVWRKS_GATTBASE); /* PCI Posting. */ 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci cap_reg = readw(serverworks_private.registers+SVWRKS_COMMAND); 28562306a36Sopenharmony_ci cap_reg &= ~0x0007; 28662306a36Sopenharmony_ci cap_reg |= 0x4; 28762306a36Sopenharmony_ci writew(cap_reg, serverworks_private.registers+SVWRKS_COMMAND); 28862306a36Sopenharmony_ci readw(serverworks_private.registers+SVWRKS_COMMAND); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci pci_read_config_byte(serverworks_private.svrwrks_dev,SVWRKS_AGP_ENABLE, &enable_reg); 29162306a36Sopenharmony_ci enable_reg |= 0x1; /* Agp Enable bit */ 29262306a36Sopenharmony_ci pci_write_config_byte(serverworks_private.svrwrks_dev,SVWRKS_AGP_ENABLE, enable_reg); 29362306a36Sopenharmony_ci serverworks_tlbflush(NULL); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci agp_bridge->capndx = pci_find_capability(serverworks_private.svrwrks_dev, PCI_CAP_ID_AGP); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci /* Fill in the mode register */ 29862306a36Sopenharmony_ci pci_read_config_dword(serverworks_private.svrwrks_dev, 29962306a36Sopenharmony_ci agp_bridge->capndx+PCI_AGP_STATUS, &agp_bridge->mode); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci pci_read_config_byte(agp_bridge->dev, SVWRKS_CACHING, &enable_reg); 30262306a36Sopenharmony_ci enable_reg &= ~0x3; 30362306a36Sopenharmony_ci pci_write_config_byte(agp_bridge->dev, SVWRKS_CACHING, enable_reg); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci pci_read_config_byte(agp_bridge->dev, SVWRKS_FEATURE, &enable_reg); 30662306a36Sopenharmony_ci enable_reg |= (1<<6); 30762306a36Sopenharmony_ci pci_write_config_byte(agp_bridge->dev,SVWRKS_FEATURE, enable_reg); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci return 0; 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cistatic void serverworks_cleanup(void) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci iounmap((void __iomem *) serverworks_private.registers); 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_cistatic int serverworks_insert_memory(struct agp_memory *mem, 31862306a36Sopenharmony_ci off_t pg_start, int type) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci int i, j, num_entries; 32162306a36Sopenharmony_ci unsigned long __iomem *cur_gatt; 32262306a36Sopenharmony_ci unsigned long addr; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci num_entries = A_SIZE_LVL2(agp_bridge->current_size)->num_entries; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci if (type != 0 || mem->type != 0) { 32762306a36Sopenharmony_ci return -EINVAL; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci if ((pg_start + mem->page_count) > num_entries) { 33062306a36Sopenharmony_ci return -EINVAL; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci j = pg_start; 33462306a36Sopenharmony_ci while (j < (pg_start + mem->page_count)) { 33562306a36Sopenharmony_ci addr = (j * PAGE_SIZE) + agp_bridge->gart_bus_addr; 33662306a36Sopenharmony_ci cur_gatt = SVRWRKS_GET_GATT(addr); 33762306a36Sopenharmony_ci if (!PGE_EMPTY(agp_bridge, readl(cur_gatt+GET_GATT_OFF(addr)))) 33862306a36Sopenharmony_ci return -EBUSY; 33962306a36Sopenharmony_ci j++; 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci if (!mem->is_flushed) { 34362306a36Sopenharmony_ci global_cache_flush(); 34462306a36Sopenharmony_ci mem->is_flushed = true; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { 34862306a36Sopenharmony_ci addr = (j * PAGE_SIZE) + agp_bridge->gart_bus_addr; 34962306a36Sopenharmony_ci cur_gatt = SVRWRKS_GET_GATT(addr); 35062306a36Sopenharmony_ci writel(agp_bridge->driver->mask_memory(agp_bridge, 35162306a36Sopenharmony_ci page_to_phys(mem->pages[i]), mem->type), 35262306a36Sopenharmony_ci cur_gatt+GET_GATT_OFF(addr)); 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci serverworks_tlbflush(mem); 35562306a36Sopenharmony_ci return 0; 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_cistatic int serverworks_remove_memory(struct agp_memory *mem, off_t pg_start, 35962306a36Sopenharmony_ci int type) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci int i; 36262306a36Sopenharmony_ci unsigned long __iomem *cur_gatt; 36362306a36Sopenharmony_ci unsigned long addr; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci if (type != 0 || mem->type != 0) { 36662306a36Sopenharmony_ci return -EINVAL; 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci global_cache_flush(); 37062306a36Sopenharmony_ci serverworks_tlbflush(mem); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci for (i = pg_start; i < (mem->page_count + pg_start); i++) { 37362306a36Sopenharmony_ci addr = (i * PAGE_SIZE) + agp_bridge->gart_bus_addr; 37462306a36Sopenharmony_ci cur_gatt = SVRWRKS_GET_GATT(addr); 37562306a36Sopenharmony_ci writel(agp_bridge->scratch_page, cur_gatt+GET_GATT_OFF(addr)); 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci serverworks_tlbflush(mem); 37962306a36Sopenharmony_ci return 0; 38062306a36Sopenharmony_ci} 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_cistatic const struct gatt_mask serverworks_masks[] = 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci {.mask = 1, .type = 0} 38562306a36Sopenharmony_ci}; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_cistatic const struct aper_size_info_lvl2 serverworks_sizes[7] = 38862306a36Sopenharmony_ci{ 38962306a36Sopenharmony_ci {2048, 524288, 0x80000000}, 39062306a36Sopenharmony_ci {1024, 262144, 0xc0000000}, 39162306a36Sopenharmony_ci {512, 131072, 0xe0000000}, 39262306a36Sopenharmony_ci {256, 65536, 0xf0000000}, 39362306a36Sopenharmony_ci {128, 32768, 0xf8000000}, 39462306a36Sopenharmony_ci {64, 16384, 0xfc000000}, 39562306a36Sopenharmony_ci {32, 8192, 0xfe000000} 39662306a36Sopenharmony_ci}; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_cistatic void serverworks_agp_enable(struct agp_bridge_data *bridge, u32 mode) 39962306a36Sopenharmony_ci{ 40062306a36Sopenharmony_ci u32 command; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci pci_read_config_dword(serverworks_private.svrwrks_dev, 40362306a36Sopenharmony_ci bridge->capndx + PCI_AGP_STATUS, 40462306a36Sopenharmony_ci &command); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci command = agp_collect_device_status(bridge, mode, command); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci command &= ~0x10; /* disable FW */ 40962306a36Sopenharmony_ci command &= ~0x08; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci command |= 0x100; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci pci_write_config_dword(serverworks_private.svrwrks_dev, 41462306a36Sopenharmony_ci bridge->capndx + PCI_AGP_COMMAND, 41562306a36Sopenharmony_ci command); 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci agp_device_command(command, false); 41862306a36Sopenharmony_ci} 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_cistatic const struct agp_bridge_driver sworks_driver = { 42162306a36Sopenharmony_ci .owner = THIS_MODULE, 42262306a36Sopenharmony_ci .aperture_sizes = serverworks_sizes, 42362306a36Sopenharmony_ci .size_type = LVL2_APER_SIZE, 42462306a36Sopenharmony_ci .num_aperture_sizes = 7, 42562306a36Sopenharmony_ci .configure = serverworks_configure, 42662306a36Sopenharmony_ci .fetch_size = serverworks_fetch_size, 42762306a36Sopenharmony_ci .cleanup = serverworks_cleanup, 42862306a36Sopenharmony_ci .tlb_flush = serverworks_tlbflush, 42962306a36Sopenharmony_ci .mask_memory = agp_generic_mask_memory, 43062306a36Sopenharmony_ci .masks = serverworks_masks, 43162306a36Sopenharmony_ci .agp_enable = serverworks_agp_enable, 43262306a36Sopenharmony_ci .cache_flush = global_cache_flush, 43362306a36Sopenharmony_ci .create_gatt_table = serverworks_create_gatt_table, 43462306a36Sopenharmony_ci .free_gatt_table = serverworks_free_gatt_table, 43562306a36Sopenharmony_ci .insert_memory = serverworks_insert_memory, 43662306a36Sopenharmony_ci .remove_memory = serverworks_remove_memory, 43762306a36Sopenharmony_ci .alloc_by_type = agp_generic_alloc_by_type, 43862306a36Sopenharmony_ci .free_by_type = agp_generic_free_by_type, 43962306a36Sopenharmony_ci .agp_alloc_page = agp_generic_alloc_page, 44062306a36Sopenharmony_ci .agp_alloc_pages = agp_generic_alloc_pages, 44162306a36Sopenharmony_ci .agp_destroy_page = agp_generic_destroy_page, 44262306a36Sopenharmony_ci .agp_destroy_pages = agp_generic_destroy_pages, 44362306a36Sopenharmony_ci .agp_type_to_mask_type = agp_generic_type_to_mask_type, 44462306a36Sopenharmony_ci}; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_cistatic int agp_serverworks_probe(struct pci_dev *pdev, 44762306a36Sopenharmony_ci const struct pci_device_id *ent) 44862306a36Sopenharmony_ci{ 44962306a36Sopenharmony_ci struct agp_bridge_data *bridge; 45062306a36Sopenharmony_ci struct pci_dev *bridge_dev; 45162306a36Sopenharmony_ci u32 temp, temp2; 45262306a36Sopenharmony_ci u8 cap_ptr = 0; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci switch (pdev->device) { 45762306a36Sopenharmony_ci case 0x0006: 45862306a36Sopenharmony_ci dev_err(&pdev->dev, "ServerWorks CNB20HE is unsupported due to lack of documentation\n"); 45962306a36Sopenharmony_ci return -ENODEV; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci case PCI_DEVICE_ID_SERVERWORKS_HE: 46262306a36Sopenharmony_ci case PCI_DEVICE_ID_SERVERWORKS_LE: 46362306a36Sopenharmony_ci case 0x0007: 46462306a36Sopenharmony_ci break; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci default: 46762306a36Sopenharmony_ci if (cap_ptr) 46862306a36Sopenharmony_ci dev_err(&pdev->dev, "unsupported Serverworks chipset " 46962306a36Sopenharmony_ci "[%04x/%04x]\n", pdev->vendor, pdev->device); 47062306a36Sopenharmony_ci return -ENODEV; 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci /* Everything is on func 1 here so we are hardcoding function one */ 47462306a36Sopenharmony_ci bridge_dev = pci_get_domain_bus_and_slot(pci_domain_nr(pdev->bus), 47562306a36Sopenharmony_ci (unsigned int)pdev->bus->number, 47662306a36Sopenharmony_ci PCI_DEVFN(0, 1)); 47762306a36Sopenharmony_ci if (!bridge_dev) { 47862306a36Sopenharmony_ci dev_info(&pdev->dev, "can't find secondary device\n"); 47962306a36Sopenharmony_ci return -ENODEV; 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci serverworks_private.svrwrks_dev = bridge_dev; 48362306a36Sopenharmony_ci serverworks_private.gart_addr_ofs = 0x10; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci pci_read_config_dword(pdev, SVWRKS_APSIZE, &temp); 48662306a36Sopenharmony_ci if (temp & PCI_BASE_ADDRESS_MEM_TYPE_64) { 48762306a36Sopenharmony_ci pci_read_config_dword(pdev, SVWRKS_APSIZE + 4, &temp2); 48862306a36Sopenharmony_ci if (temp2 != 0) { 48962306a36Sopenharmony_ci dev_info(&pdev->dev, "64 bit aperture address, " 49062306a36Sopenharmony_ci "but top bits are not zero; disabling AGP\n"); 49162306a36Sopenharmony_ci return -ENODEV; 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci serverworks_private.mm_addr_ofs = 0x18; 49462306a36Sopenharmony_ci } else 49562306a36Sopenharmony_ci serverworks_private.mm_addr_ofs = 0x14; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci pci_read_config_dword(pdev, serverworks_private.mm_addr_ofs, &temp); 49862306a36Sopenharmony_ci if (temp & PCI_BASE_ADDRESS_MEM_TYPE_64) { 49962306a36Sopenharmony_ci pci_read_config_dword(pdev, 50062306a36Sopenharmony_ci serverworks_private.mm_addr_ofs + 4, &temp2); 50162306a36Sopenharmony_ci if (temp2 != 0) { 50262306a36Sopenharmony_ci dev_info(&pdev->dev, "64 bit MMIO address, but top " 50362306a36Sopenharmony_ci "bits are not zero; disabling AGP\n"); 50462306a36Sopenharmony_ci return -ENODEV; 50562306a36Sopenharmony_ci } 50662306a36Sopenharmony_ci } 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci bridge = agp_alloc_bridge(); 50962306a36Sopenharmony_ci if (!bridge) 51062306a36Sopenharmony_ci return -ENOMEM; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci bridge->driver = &sworks_driver; 51362306a36Sopenharmony_ci bridge->dev_private_data = &serverworks_private; 51462306a36Sopenharmony_ci bridge->dev = pci_dev_get(pdev); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci pci_set_drvdata(pdev, bridge); 51762306a36Sopenharmony_ci return agp_add_bridge(bridge); 51862306a36Sopenharmony_ci} 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_cistatic void agp_serverworks_remove(struct pci_dev *pdev) 52162306a36Sopenharmony_ci{ 52262306a36Sopenharmony_ci struct agp_bridge_data *bridge = pci_get_drvdata(pdev); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci pci_dev_put(bridge->dev); 52562306a36Sopenharmony_ci agp_remove_bridge(bridge); 52662306a36Sopenharmony_ci agp_put_bridge(bridge); 52762306a36Sopenharmony_ci pci_dev_put(serverworks_private.svrwrks_dev); 52862306a36Sopenharmony_ci serverworks_private.svrwrks_dev = NULL; 52962306a36Sopenharmony_ci} 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_cistatic struct pci_device_id agp_serverworks_pci_table[] = { 53262306a36Sopenharmony_ci { 53362306a36Sopenharmony_ci .class = (PCI_CLASS_BRIDGE_HOST << 8), 53462306a36Sopenharmony_ci .class_mask = ~0, 53562306a36Sopenharmony_ci .vendor = PCI_VENDOR_ID_SERVERWORKS, 53662306a36Sopenharmony_ci .device = PCI_ANY_ID, 53762306a36Sopenharmony_ci .subvendor = PCI_ANY_ID, 53862306a36Sopenharmony_ci .subdevice = PCI_ANY_ID, 53962306a36Sopenharmony_ci }, 54062306a36Sopenharmony_ci { } 54162306a36Sopenharmony_ci}; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, agp_serverworks_pci_table); 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_cistatic struct pci_driver agp_serverworks_pci_driver = { 54662306a36Sopenharmony_ci .name = "agpgart-serverworks", 54762306a36Sopenharmony_ci .id_table = agp_serverworks_pci_table, 54862306a36Sopenharmony_ci .probe = agp_serverworks_probe, 54962306a36Sopenharmony_ci .remove = agp_serverworks_remove, 55062306a36Sopenharmony_ci}; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_cistatic int __init agp_serverworks_init(void) 55362306a36Sopenharmony_ci{ 55462306a36Sopenharmony_ci if (agp_off) 55562306a36Sopenharmony_ci return -EINVAL; 55662306a36Sopenharmony_ci return pci_register_driver(&agp_serverworks_pci_driver); 55762306a36Sopenharmony_ci} 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_cistatic void __exit agp_serverworks_cleanup(void) 56062306a36Sopenharmony_ci{ 56162306a36Sopenharmony_ci pci_unregister_driver(&agp_serverworks_pci_driver); 56262306a36Sopenharmony_ci} 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_cimodule_init(agp_serverworks_init); 56562306a36Sopenharmony_cimodule_exit(agp_serverworks_cleanup); 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ciMODULE_LICENSE("GPL and additional rights"); 56862306a36Sopenharmony_ci 569