11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Serverworks AGPGART routines. 31da177e4SLinus Torvalds */ 41da177e4SLinus Torvalds 51da177e4SLinus Torvalds #include <linux/module.h> 61da177e4SLinus Torvalds #include <linux/pci.h> 71da177e4SLinus Torvalds #include <linux/init.h> 84e57b681STim Schmielau #include <linux/string.h> 94e57b681STim Schmielau #include <linux/slab.h> 10de25968cSTim Schmielau #include <linux/jiffies.h> 111da177e4SLinus Torvalds #include <linux/agp_backend.h> 12e47036b4SLaura Abbott #include <asm/set_memory.h> 131da177e4SLinus Torvalds #include "agp.h" 141da177e4SLinus Torvalds 151da177e4SLinus Torvalds #define SVWRKS_COMMAND 0x04 161da177e4SLinus Torvalds #define SVWRKS_APSIZE 0x10 171da177e4SLinus Torvalds #define SVWRKS_MMBASE 0x14 181da177e4SLinus Torvalds #define SVWRKS_CACHING 0x4b 191da177e4SLinus Torvalds #define SVWRKS_AGP_ENABLE 0x60 201da177e4SLinus Torvalds #define SVWRKS_FEATURE 0x68 211da177e4SLinus Torvalds 221da177e4SLinus Torvalds #define SVWRKS_SIZE_MASK 0xfe000000 231da177e4SLinus Torvalds 241da177e4SLinus Torvalds /* Memory mapped registers */ 251da177e4SLinus Torvalds #define SVWRKS_GART_CACHE 0x02 261da177e4SLinus Torvalds #define SVWRKS_GATTBASE 0x04 271da177e4SLinus Torvalds #define SVWRKS_TLBFLUSH 0x10 281da177e4SLinus Torvalds #define SVWRKS_POSTFLUSH 0x14 291da177e4SLinus Torvalds #define SVWRKS_DIRFLUSH 0x0c 301da177e4SLinus Torvalds 311da177e4SLinus Torvalds 321da177e4SLinus Torvalds struct serverworks_page_map { 331da177e4SLinus Torvalds unsigned long *real; 341da177e4SLinus Torvalds unsigned long __iomem *remapped; 351da177e4SLinus Torvalds }; 361da177e4SLinus Torvalds 371da177e4SLinus Torvalds static struct _serverworks_private { 381da177e4SLinus Torvalds struct pci_dev *svrwrks_dev; /* device one */ 391da177e4SLinus Torvalds volatile u8 __iomem *registers; 401da177e4SLinus Torvalds struct serverworks_page_map **gatt_pages; 411da177e4SLinus Torvalds int num_tables; 421da177e4SLinus Torvalds struct serverworks_page_map scratch_dir; 431da177e4SLinus Torvalds 441da177e4SLinus Torvalds int gart_addr_ofs; 451da177e4SLinus Torvalds int mm_addr_ofs; 461da177e4SLinus Torvalds } serverworks_private; 471da177e4SLinus Torvalds 481da177e4SLinus Torvalds static int serverworks_create_page_map(struct serverworks_page_map *page_map) 491da177e4SLinus Torvalds { 501da177e4SLinus Torvalds int i; 511da177e4SLinus Torvalds 521da177e4SLinus Torvalds page_map->real = (unsigned long *) __get_free_page(GFP_KERNEL); 531da177e4SLinus Torvalds if (page_map->real == NULL) { 541da177e4SLinus Torvalds return -ENOMEM; 551da177e4SLinus Torvalds } 56fcea424dSArjan van dev Ven 5744a207fcSDave Airlie set_memory_uc((unsigned long)page_map->real, 1); 58fcea424dSArjan van dev Ven page_map->remapped = page_map->real; 591da177e4SLinus Torvalds 601da177e4SLinus Torvalds for (i = 0; i < PAGE_SIZE / sizeof(unsigned long); i++) 611da177e4SLinus Torvalds writel(agp_bridge->scratch_page, page_map->remapped+i); 62fcea424dSArjan van dev Ven /* Red Pen: Everyone else does pci posting flush here */ 631da177e4SLinus Torvalds 641da177e4SLinus Torvalds return 0; 651da177e4SLinus Torvalds } 661da177e4SLinus Torvalds 671da177e4SLinus Torvalds static void serverworks_free_page_map(struct serverworks_page_map *page_map) 681da177e4SLinus Torvalds { 6944a207fcSDave Airlie set_memory_wb((unsigned long)page_map->real, 1); 701da177e4SLinus Torvalds free_page((unsigned long) page_map->real); 711da177e4SLinus Torvalds } 721da177e4SLinus Torvalds 731da177e4SLinus Torvalds static void serverworks_free_gatt_pages(void) 741da177e4SLinus Torvalds { 751da177e4SLinus Torvalds int i; 761da177e4SLinus Torvalds struct serverworks_page_map **tables; 771da177e4SLinus Torvalds struct serverworks_page_map *entry; 781da177e4SLinus Torvalds 791da177e4SLinus Torvalds tables = serverworks_private.gatt_pages; 801da177e4SLinus Torvalds for (i = 0; i < serverworks_private.num_tables; i++) { 811da177e4SLinus Torvalds entry = tables[i]; 821da177e4SLinus Torvalds if (entry != NULL) { 831da177e4SLinus Torvalds if (entry->real != NULL) { 841da177e4SLinus Torvalds serverworks_free_page_map(entry); 851da177e4SLinus Torvalds } 861da177e4SLinus Torvalds kfree(entry); 871da177e4SLinus Torvalds } 881da177e4SLinus Torvalds } 891da177e4SLinus Torvalds kfree(tables); 901da177e4SLinus Torvalds } 911da177e4SLinus Torvalds 921da177e4SLinus Torvalds static int serverworks_create_gatt_pages(int nr_tables) 931da177e4SLinus Torvalds { 941da177e4SLinus Torvalds struct serverworks_page_map **tables; 951da177e4SLinus Torvalds struct serverworks_page_map *entry; 961da177e4SLinus Torvalds int retval = 0; 971da177e4SLinus Torvalds int i; 981da177e4SLinus Torvalds 99*6396bb22SKees Cook tables = kcalloc(nr_tables + 1, sizeof(struct serverworks_page_map *), 1001da177e4SLinus Torvalds GFP_KERNEL); 1010ea27d9fSDave Jones if (tables == NULL) 1021da177e4SLinus Torvalds return -ENOMEM; 1030ea27d9fSDave Jones 1041da177e4SLinus Torvalds for (i = 0; i < nr_tables; i++) { 1050ea27d9fSDave Jones entry = kzalloc(sizeof(struct serverworks_page_map), GFP_KERNEL); 1061da177e4SLinus Torvalds if (entry == NULL) { 1071da177e4SLinus Torvalds retval = -ENOMEM; 1081da177e4SLinus Torvalds break; 1091da177e4SLinus Torvalds } 1101da177e4SLinus Torvalds tables[i] = entry; 1111da177e4SLinus Torvalds retval = serverworks_create_page_map(entry); 1121da177e4SLinus Torvalds if (retval != 0) break; 1131da177e4SLinus Torvalds } 1141da177e4SLinus Torvalds serverworks_private.num_tables = nr_tables; 1151da177e4SLinus Torvalds serverworks_private.gatt_pages = tables; 1161da177e4SLinus Torvalds 1171da177e4SLinus Torvalds if (retval != 0) serverworks_free_gatt_pages(); 1181da177e4SLinus Torvalds 1191da177e4SLinus Torvalds return retval; 1201da177e4SLinus Torvalds } 1211da177e4SLinus Torvalds 1221da177e4SLinus Torvalds #define SVRWRKS_GET_GATT(addr) (serverworks_private.gatt_pages[\ 1231da177e4SLinus Torvalds GET_PAGE_DIR_IDX(addr)]->remapped) 1241da177e4SLinus Torvalds 1251da177e4SLinus Torvalds #ifndef GET_PAGE_DIR_OFF 1261da177e4SLinus Torvalds #define GET_PAGE_DIR_OFF(addr) (addr >> 22) 1271da177e4SLinus Torvalds #endif 1281da177e4SLinus Torvalds 1291da177e4SLinus Torvalds #ifndef GET_PAGE_DIR_IDX 1301da177e4SLinus Torvalds #define GET_PAGE_DIR_IDX(addr) (GET_PAGE_DIR_OFF(addr) - \ 1311da177e4SLinus Torvalds GET_PAGE_DIR_OFF(agp_bridge->gart_bus_addr)) 1321da177e4SLinus Torvalds #endif 1331da177e4SLinus Torvalds 1341da177e4SLinus Torvalds #ifndef GET_GATT_OFF 1351da177e4SLinus Torvalds #define GET_GATT_OFF(addr) ((addr & 0x003ff000) >> 12) 1361da177e4SLinus Torvalds #endif 1371da177e4SLinus Torvalds 1381da177e4SLinus Torvalds static int serverworks_create_gatt_table(struct agp_bridge_data *bridge) 1391da177e4SLinus Torvalds { 1401da177e4SLinus Torvalds struct aper_size_info_lvl2 *value; 1411da177e4SLinus Torvalds struct serverworks_page_map page_dir; 1421da177e4SLinus Torvalds int retval; 1431da177e4SLinus Torvalds u32 temp; 1441da177e4SLinus Torvalds int i; 1451da177e4SLinus Torvalds 1461da177e4SLinus Torvalds value = A_SIZE_LVL2(agp_bridge->current_size); 1471da177e4SLinus Torvalds retval = serverworks_create_page_map(&page_dir); 1481da177e4SLinus Torvalds if (retval != 0) { 1491da177e4SLinus Torvalds return retval; 1501da177e4SLinus Torvalds } 1511da177e4SLinus Torvalds retval = serverworks_create_page_map(&serverworks_private.scratch_dir); 1521da177e4SLinus Torvalds if (retval != 0) { 1531da177e4SLinus Torvalds serverworks_free_page_map(&page_dir); 1541da177e4SLinus Torvalds return retval; 1551da177e4SLinus Torvalds } 1561da177e4SLinus Torvalds /* Create a fake scratch directory */ 1571da177e4SLinus Torvalds for (i = 0; i < 1024; i++) { 1581da177e4SLinus Torvalds writel(agp_bridge->scratch_page, serverworks_private.scratch_dir.remapped+i); 1596a12235cSDavid Woodhouse writel(virt_to_phys(serverworks_private.scratch_dir.real) | 1, page_dir.remapped+i); 1601da177e4SLinus Torvalds } 1611da177e4SLinus Torvalds 1621da177e4SLinus Torvalds retval = serverworks_create_gatt_pages(value->num_entries / 1024); 1631da177e4SLinus Torvalds if (retval != 0) { 1641da177e4SLinus Torvalds serverworks_free_page_map(&page_dir); 1651da177e4SLinus Torvalds serverworks_free_page_map(&serverworks_private.scratch_dir); 1661da177e4SLinus Torvalds return retval; 1671da177e4SLinus Torvalds } 1681da177e4SLinus Torvalds 1691da177e4SLinus Torvalds agp_bridge->gatt_table_real = (u32 *)page_dir.real; 1701da177e4SLinus Torvalds agp_bridge->gatt_table = (u32 __iomem *)page_dir.remapped; 1716a12235cSDavid Woodhouse agp_bridge->gatt_bus_addr = virt_to_phys(page_dir.real); 1721da177e4SLinus Torvalds 1731da177e4SLinus Torvalds /* Get the address for the gart region. 1741da177e4SLinus Torvalds * This is a bus address even on the alpha, b/c its 1751da177e4SLinus Torvalds * used to program the agp master not the cpu 1761da177e4SLinus Torvalds */ 1771da177e4SLinus Torvalds 1781da177e4SLinus Torvalds pci_read_config_dword(agp_bridge->dev,serverworks_private.gart_addr_ofs,&temp); 1791da177e4SLinus Torvalds agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); 1801da177e4SLinus Torvalds 1811da177e4SLinus Torvalds /* Calculate the agp offset */ 1821da177e4SLinus Torvalds for (i = 0; i < value->num_entries / 1024; i++) 1836a12235cSDavid Woodhouse writel(virt_to_phys(serverworks_private.gatt_pages[i]->real)|1, page_dir.remapped+i); 1841da177e4SLinus Torvalds 1851da177e4SLinus Torvalds return 0; 1861da177e4SLinus Torvalds } 1871da177e4SLinus Torvalds 1881da177e4SLinus Torvalds static int serverworks_free_gatt_table(struct agp_bridge_data *bridge) 1891da177e4SLinus Torvalds { 1901da177e4SLinus Torvalds struct serverworks_page_map page_dir; 1911da177e4SLinus Torvalds 1921da177e4SLinus Torvalds page_dir.real = (unsigned long *)agp_bridge->gatt_table_real; 1931da177e4SLinus Torvalds page_dir.remapped = (unsigned long __iomem *)agp_bridge->gatt_table; 1941da177e4SLinus Torvalds 1951da177e4SLinus Torvalds serverworks_free_gatt_pages(); 1961da177e4SLinus Torvalds serverworks_free_page_map(&page_dir); 1971da177e4SLinus Torvalds serverworks_free_page_map(&serverworks_private.scratch_dir); 1981da177e4SLinus Torvalds return 0; 1991da177e4SLinus Torvalds } 2001da177e4SLinus Torvalds 2011da177e4SLinus Torvalds static int serverworks_fetch_size(void) 2021da177e4SLinus Torvalds { 2031da177e4SLinus Torvalds int i; 2041da177e4SLinus Torvalds u32 temp; 2051da177e4SLinus Torvalds u32 temp2; 2061da177e4SLinus Torvalds struct aper_size_info_lvl2 *values; 2071da177e4SLinus Torvalds 2081da177e4SLinus Torvalds values = A_SIZE_LVL2(agp_bridge->driver->aperture_sizes); 2091da177e4SLinus Torvalds pci_read_config_dword(agp_bridge->dev,serverworks_private.gart_addr_ofs,&temp); 2101da177e4SLinus Torvalds pci_write_config_dword(agp_bridge->dev,serverworks_private.gart_addr_ofs, 2111da177e4SLinus Torvalds SVWRKS_SIZE_MASK); 2121da177e4SLinus Torvalds pci_read_config_dword(agp_bridge->dev,serverworks_private.gart_addr_ofs,&temp2); 2131da177e4SLinus Torvalds pci_write_config_dword(agp_bridge->dev,serverworks_private.gart_addr_ofs,temp); 2141da177e4SLinus Torvalds temp2 &= SVWRKS_SIZE_MASK; 2151da177e4SLinus Torvalds 2161da177e4SLinus Torvalds for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++) { 2171da177e4SLinus Torvalds if (temp2 == values[i].size_value) { 2181da177e4SLinus Torvalds agp_bridge->previous_size = 2191da177e4SLinus Torvalds agp_bridge->current_size = (void *) (values + i); 2201da177e4SLinus Torvalds 2211da177e4SLinus Torvalds agp_bridge->aperture_size_idx = i; 2221da177e4SLinus Torvalds return values[i].size; 2231da177e4SLinus Torvalds } 2241da177e4SLinus Torvalds } 2251da177e4SLinus Torvalds 2261da177e4SLinus Torvalds return 0; 2271da177e4SLinus Torvalds } 2281da177e4SLinus Torvalds 2291da177e4SLinus Torvalds /* 2301da177e4SLinus Torvalds * This routine could be implemented by taking the addresses 2311da177e4SLinus Torvalds * written to the GATT, and flushing them individually. However 2321da177e4SLinus Torvalds * currently it just flushes the whole table. Which is probably 23325985edcSLucas De Marchi * more efficient, since agp_memory blocks can be a large number of 2341da177e4SLinus Torvalds * entries. 2351da177e4SLinus Torvalds */ 2361da177e4SLinus Torvalds static void serverworks_tlbflush(struct agp_memory *temp) 2371da177e4SLinus Torvalds { 2380ff541daSDave Jones unsigned long timeout; 2390ff541daSDave Jones 2401da177e4SLinus Torvalds writeb(1, serverworks_private.registers+SVWRKS_POSTFLUSH); 2410ff541daSDave Jones timeout = jiffies + 3*HZ; 2420ff541daSDave Jones while (readb(serverworks_private.registers+SVWRKS_POSTFLUSH) == 1) { 2431da177e4SLinus Torvalds cpu_relax(); 2440ff541daSDave Jones if (time_after(jiffies, timeout)) { 245e3cf6951SBjorn Helgaas dev_err(&serverworks_private.svrwrks_dev->dev, 246e3cf6951SBjorn Helgaas "TLB post flush took more than 3 seconds\n"); 2470ff541daSDave Jones break; 2480ff541daSDave Jones } 2490ff541daSDave Jones } 2501da177e4SLinus Torvalds 2511da177e4SLinus Torvalds writel(1, serverworks_private.registers+SVWRKS_DIRFLUSH); 2520ff541daSDave Jones timeout = jiffies + 3*HZ; 2530ff541daSDave Jones while (readl(serverworks_private.registers+SVWRKS_DIRFLUSH) == 1) { 2541da177e4SLinus Torvalds cpu_relax(); 2550ff541daSDave Jones if (time_after(jiffies, timeout)) { 256e3cf6951SBjorn Helgaas dev_err(&serverworks_private.svrwrks_dev->dev, 257e3cf6951SBjorn Helgaas "TLB Dir flush took more than 3 seconds\n"); 2580ff541daSDave Jones break; 2590ff541daSDave Jones } 2600ff541daSDave Jones } 2611da177e4SLinus Torvalds } 2621da177e4SLinus Torvalds 2631da177e4SLinus Torvalds static int serverworks_configure(void) 2641da177e4SLinus Torvalds { 2651da177e4SLinus Torvalds struct aper_size_info_lvl2 *current_size; 2661da177e4SLinus Torvalds u32 temp; 2671da177e4SLinus Torvalds u8 enable_reg; 2681da177e4SLinus Torvalds u16 cap_reg; 2691da177e4SLinus Torvalds 2701da177e4SLinus Torvalds current_size = A_SIZE_LVL2(agp_bridge->current_size); 2711da177e4SLinus Torvalds 2721da177e4SLinus Torvalds /* Get the memory mapped registers */ 2731da177e4SLinus Torvalds pci_read_config_dword(agp_bridge->dev, serverworks_private.mm_addr_ofs, &temp); 2741da177e4SLinus Torvalds temp = (temp & PCI_BASE_ADDRESS_MEM_MASK); 2751da177e4SLinus Torvalds serverworks_private.registers = (volatile u8 __iomem *) ioremap(temp, 4096); 2761da177e4SLinus Torvalds if (!serverworks_private.registers) { 277e3cf6951SBjorn Helgaas dev_err(&agp_bridge->dev->dev, "can't ioremap(%#x)\n", temp); 2781da177e4SLinus Torvalds return -ENOMEM; 2791da177e4SLinus Torvalds } 2801da177e4SLinus Torvalds 2811da177e4SLinus Torvalds writeb(0xA, serverworks_private.registers+SVWRKS_GART_CACHE); 2821da177e4SLinus Torvalds readb(serverworks_private.registers+SVWRKS_GART_CACHE); /* PCI Posting. */ 2831da177e4SLinus Torvalds 2841da177e4SLinus Torvalds writel(agp_bridge->gatt_bus_addr, serverworks_private.registers+SVWRKS_GATTBASE); 2851da177e4SLinus Torvalds readl(serverworks_private.registers+SVWRKS_GATTBASE); /* PCI Posting. */ 2861da177e4SLinus Torvalds 2871da177e4SLinus Torvalds cap_reg = readw(serverworks_private.registers+SVWRKS_COMMAND); 2881da177e4SLinus Torvalds cap_reg &= ~0x0007; 2891da177e4SLinus Torvalds cap_reg |= 0x4; 2901da177e4SLinus Torvalds writew(cap_reg, serverworks_private.registers+SVWRKS_COMMAND); 2911da177e4SLinus Torvalds readw(serverworks_private.registers+SVWRKS_COMMAND); 2921da177e4SLinus Torvalds 2931da177e4SLinus Torvalds pci_read_config_byte(serverworks_private.svrwrks_dev,SVWRKS_AGP_ENABLE, &enable_reg); 2941da177e4SLinus Torvalds enable_reg |= 0x1; /* Agp Enable bit */ 2951da177e4SLinus Torvalds pci_write_config_byte(serverworks_private.svrwrks_dev,SVWRKS_AGP_ENABLE, enable_reg); 2961da177e4SLinus Torvalds serverworks_tlbflush(NULL); 2971da177e4SLinus Torvalds 2981da177e4SLinus Torvalds agp_bridge->capndx = pci_find_capability(serverworks_private.svrwrks_dev, PCI_CAP_ID_AGP); 2991da177e4SLinus Torvalds 3001da177e4SLinus Torvalds /* Fill in the mode register */ 3011da177e4SLinus Torvalds pci_read_config_dword(serverworks_private.svrwrks_dev, 3021da177e4SLinus Torvalds agp_bridge->capndx+PCI_AGP_STATUS, &agp_bridge->mode); 3031da177e4SLinus Torvalds 3041da177e4SLinus Torvalds pci_read_config_byte(agp_bridge->dev, SVWRKS_CACHING, &enable_reg); 3051da177e4SLinus Torvalds enable_reg &= ~0x3; 3061da177e4SLinus Torvalds pci_write_config_byte(agp_bridge->dev, SVWRKS_CACHING, enable_reg); 3071da177e4SLinus Torvalds 3081da177e4SLinus Torvalds pci_read_config_byte(agp_bridge->dev, SVWRKS_FEATURE, &enable_reg); 3091da177e4SLinus Torvalds enable_reg |= (1<<6); 3101da177e4SLinus Torvalds pci_write_config_byte(agp_bridge->dev,SVWRKS_FEATURE, enable_reg); 3111da177e4SLinus Torvalds 3121da177e4SLinus Torvalds return 0; 3131da177e4SLinus Torvalds } 3141da177e4SLinus Torvalds 3151da177e4SLinus Torvalds static void serverworks_cleanup(void) 3161da177e4SLinus Torvalds { 3171da177e4SLinus Torvalds iounmap((void __iomem *) serverworks_private.registers); 3181da177e4SLinus Torvalds } 3191da177e4SLinus Torvalds 3201da177e4SLinus Torvalds static int serverworks_insert_memory(struct agp_memory *mem, 3211da177e4SLinus Torvalds off_t pg_start, int type) 3221da177e4SLinus Torvalds { 3231da177e4SLinus Torvalds int i, j, num_entries; 3241da177e4SLinus Torvalds unsigned long __iomem *cur_gatt; 3251da177e4SLinus Torvalds unsigned long addr; 3261da177e4SLinus Torvalds 3271da177e4SLinus Torvalds num_entries = A_SIZE_LVL2(agp_bridge->current_size)->num_entries; 3281da177e4SLinus Torvalds 3291da177e4SLinus Torvalds if (type != 0 || mem->type != 0) { 3301da177e4SLinus Torvalds return -EINVAL; 3311da177e4SLinus Torvalds } 3321da177e4SLinus Torvalds if ((pg_start + mem->page_count) > num_entries) { 3331da177e4SLinus Torvalds return -EINVAL; 3341da177e4SLinus Torvalds } 3351da177e4SLinus Torvalds 3361da177e4SLinus Torvalds j = pg_start; 3371da177e4SLinus Torvalds while (j < (pg_start + mem->page_count)) { 3381da177e4SLinus Torvalds addr = (j * PAGE_SIZE) + agp_bridge->gart_bus_addr; 3391da177e4SLinus Torvalds cur_gatt = SVRWRKS_GET_GATT(addr); 3401da177e4SLinus Torvalds if (!PGE_EMPTY(agp_bridge, readl(cur_gatt+GET_GATT_OFF(addr)))) 3411da177e4SLinus Torvalds return -EBUSY; 3421da177e4SLinus Torvalds j++; 3431da177e4SLinus Torvalds } 3441da177e4SLinus Torvalds 345c7258012SJoe Perches if (!mem->is_flushed) { 3461da177e4SLinus Torvalds global_cache_flush(); 347c7258012SJoe Perches mem->is_flushed = true; 3481da177e4SLinus Torvalds } 3491da177e4SLinus Torvalds 3501da177e4SLinus Torvalds for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { 3511da177e4SLinus Torvalds addr = (j * PAGE_SIZE) + agp_bridge->gart_bus_addr; 3521da177e4SLinus Torvalds cur_gatt = SVRWRKS_GET_GATT(addr); 3532a4ceb6dSDavid Woodhouse writel(agp_bridge->driver->mask_memory(agp_bridge, 3546a12235cSDavid Woodhouse page_to_phys(mem->pages[i]), mem->type), 3552a4ceb6dSDavid Woodhouse cur_gatt+GET_GATT_OFF(addr)); 3561da177e4SLinus Torvalds } 3571da177e4SLinus Torvalds serverworks_tlbflush(mem); 3581da177e4SLinus Torvalds return 0; 3591da177e4SLinus Torvalds } 3601da177e4SLinus Torvalds 3611da177e4SLinus Torvalds static int serverworks_remove_memory(struct agp_memory *mem, off_t pg_start, 3621da177e4SLinus Torvalds int type) 3631da177e4SLinus Torvalds { 3641da177e4SLinus Torvalds int i; 3651da177e4SLinus Torvalds unsigned long __iomem *cur_gatt; 3661da177e4SLinus Torvalds unsigned long addr; 3671da177e4SLinus Torvalds 3681da177e4SLinus Torvalds if (type != 0 || mem->type != 0) { 3691da177e4SLinus Torvalds return -EINVAL; 3701da177e4SLinus Torvalds } 3711da177e4SLinus Torvalds 3721da177e4SLinus Torvalds global_cache_flush(); 3731da177e4SLinus Torvalds serverworks_tlbflush(mem); 3741da177e4SLinus Torvalds 3751da177e4SLinus Torvalds for (i = pg_start; i < (mem->page_count + pg_start); i++) { 3761da177e4SLinus Torvalds addr = (i * PAGE_SIZE) + agp_bridge->gart_bus_addr; 3771da177e4SLinus Torvalds cur_gatt = SVRWRKS_GET_GATT(addr); 3781da177e4SLinus Torvalds writel(agp_bridge->scratch_page, cur_gatt+GET_GATT_OFF(addr)); 3791da177e4SLinus Torvalds } 3801da177e4SLinus Torvalds 3811da177e4SLinus Torvalds serverworks_tlbflush(mem); 3821da177e4SLinus Torvalds return 0; 3831da177e4SLinus Torvalds } 3841da177e4SLinus Torvalds 385e5524f35SDave Jones static const struct gatt_mask serverworks_masks[] = 3861da177e4SLinus Torvalds { 3871da177e4SLinus Torvalds {.mask = 1, .type = 0} 3881da177e4SLinus Torvalds }; 3891da177e4SLinus Torvalds 390e5524f35SDave Jones static const struct aper_size_info_lvl2 serverworks_sizes[7] = 3911da177e4SLinus Torvalds { 3921da177e4SLinus Torvalds {2048, 524288, 0x80000000}, 3931da177e4SLinus Torvalds {1024, 262144, 0xc0000000}, 3941da177e4SLinus Torvalds {512, 131072, 0xe0000000}, 3951da177e4SLinus Torvalds {256, 65536, 0xf0000000}, 3961da177e4SLinus Torvalds {128, 32768, 0xf8000000}, 3971da177e4SLinus Torvalds {64, 16384, 0xfc000000}, 3981da177e4SLinus Torvalds {32, 8192, 0xfe000000} 3991da177e4SLinus Torvalds }; 4001da177e4SLinus Torvalds 4011da177e4SLinus Torvalds static void serverworks_agp_enable(struct agp_bridge_data *bridge, u32 mode) 4021da177e4SLinus Torvalds { 4031da177e4SLinus Torvalds u32 command; 4041da177e4SLinus Torvalds 4051da177e4SLinus Torvalds pci_read_config_dword(serverworks_private.svrwrks_dev, 4061da177e4SLinus Torvalds bridge->capndx + PCI_AGP_STATUS, 4071da177e4SLinus Torvalds &command); 4081da177e4SLinus Torvalds 4091da177e4SLinus Torvalds command = agp_collect_device_status(bridge, mode, command); 4101da177e4SLinus Torvalds 4111da177e4SLinus Torvalds command &= ~0x10; /* disable FW */ 4121da177e4SLinus Torvalds command &= ~0x08; 4131da177e4SLinus Torvalds 4141da177e4SLinus Torvalds command |= 0x100; 4151da177e4SLinus Torvalds 4161da177e4SLinus Torvalds pci_write_config_dword(serverworks_private.svrwrks_dev, 4171da177e4SLinus Torvalds bridge->capndx + PCI_AGP_COMMAND, 4181da177e4SLinus Torvalds command); 4191da177e4SLinus Torvalds 420c7258012SJoe Perches agp_device_command(command, false); 4211da177e4SLinus Torvalds } 4221da177e4SLinus Torvalds 423e5524f35SDave Jones static const struct agp_bridge_driver sworks_driver = { 4241da177e4SLinus Torvalds .owner = THIS_MODULE, 4251da177e4SLinus Torvalds .aperture_sizes = serverworks_sizes, 4261da177e4SLinus Torvalds .size_type = LVL2_APER_SIZE, 4271da177e4SLinus Torvalds .num_aperture_sizes = 7, 4281da177e4SLinus Torvalds .configure = serverworks_configure, 4291da177e4SLinus Torvalds .fetch_size = serverworks_fetch_size, 4301da177e4SLinus Torvalds .cleanup = serverworks_cleanup, 4311da177e4SLinus Torvalds .tlb_flush = serverworks_tlbflush, 4321da177e4SLinus Torvalds .mask_memory = agp_generic_mask_memory, 4331da177e4SLinus Torvalds .masks = serverworks_masks, 4341da177e4SLinus Torvalds .agp_enable = serverworks_agp_enable, 4351da177e4SLinus Torvalds .cache_flush = global_cache_flush, 4361da177e4SLinus Torvalds .create_gatt_table = serverworks_create_gatt_table, 4371da177e4SLinus Torvalds .free_gatt_table = serverworks_free_gatt_table, 4381da177e4SLinus Torvalds .insert_memory = serverworks_insert_memory, 4391da177e4SLinus Torvalds .remove_memory = serverworks_remove_memory, 4401da177e4SLinus Torvalds .alloc_by_type = agp_generic_alloc_by_type, 4411da177e4SLinus Torvalds .free_by_type = agp_generic_free_by_type, 4421da177e4SLinus Torvalds .agp_alloc_page = agp_generic_alloc_page, 4435f310b63SRene Herman .agp_alloc_pages = agp_generic_alloc_pages, 4441da177e4SLinus Torvalds .agp_destroy_page = agp_generic_destroy_page, 4455f310b63SRene Herman .agp_destroy_pages = agp_generic_destroy_pages, 446a030ce44SThomas Hellstrom .agp_type_to_mask_type = agp_generic_type_to_mask_type, 4471da177e4SLinus Torvalds }; 4481da177e4SLinus Torvalds 449bcd2982aSGreg Kroah-Hartman static int agp_serverworks_probe(struct pci_dev *pdev, 4501da177e4SLinus Torvalds const struct pci_device_id *ent) 4511da177e4SLinus Torvalds { 4521da177e4SLinus Torvalds struct agp_bridge_data *bridge; 4531da177e4SLinus Torvalds struct pci_dev *bridge_dev; 4541da177e4SLinus Torvalds u32 temp, temp2; 4551da177e4SLinus Torvalds u8 cap_ptr = 0; 4561da177e4SLinus Torvalds 4571da177e4SLinus Torvalds cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP); 4581da177e4SLinus Torvalds 4591da177e4SLinus Torvalds switch (pdev->device) { 4601da177e4SLinus Torvalds case 0x0006: 461e3cf6951SBjorn Helgaas dev_err(&pdev->dev, "ServerWorks CNB20HE is unsupported due to lack of documentation\n"); 4621da177e4SLinus Torvalds return -ENODEV; 4631da177e4SLinus Torvalds 4641da177e4SLinus Torvalds case PCI_DEVICE_ID_SERVERWORKS_HE: 4651da177e4SLinus Torvalds case PCI_DEVICE_ID_SERVERWORKS_LE: 4661da177e4SLinus Torvalds case 0x0007: 4671da177e4SLinus Torvalds break; 4681da177e4SLinus Torvalds 4691da177e4SLinus Torvalds default: 4701da177e4SLinus Torvalds if (cap_ptr) 471e3cf6951SBjorn Helgaas dev_err(&pdev->dev, "unsupported Serverworks chipset " 472e3cf6951SBjorn Helgaas "[%04x/%04x]\n", pdev->vendor, pdev->device); 4731da177e4SLinus Torvalds return -ENODEV; 4741da177e4SLinus Torvalds } 4751da177e4SLinus Torvalds 476881ba59dSAlan Cox /* Everything is on func 1 here so we are hardcoding function one */ 477067ddab5SSinan Kaya bridge_dev = pci_get_domain_bus_and_slot(pci_domain_nr(pdev->bus), 478067ddab5SSinan Kaya (unsigned int)pdev->bus->number, 479881ba59dSAlan Cox PCI_DEVFN(0, 1)); 480881ba59dSAlan Cox if (!bridge_dev) { 481e3cf6951SBjorn Helgaas dev_info(&pdev->dev, "can't find secondary device\n"); 482881ba59dSAlan Cox return -ENODEV; 483881ba59dSAlan Cox } 484881ba59dSAlan Cox 4851da177e4SLinus Torvalds serverworks_private.svrwrks_dev = bridge_dev; 4861da177e4SLinus Torvalds serverworks_private.gart_addr_ofs = 0x10; 4871da177e4SLinus Torvalds 4881da177e4SLinus Torvalds pci_read_config_dword(pdev, SVWRKS_APSIZE, &temp); 4891da177e4SLinus Torvalds if (temp & PCI_BASE_ADDRESS_MEM_TYPE_64) { 4901da177e4SLinus Torvalds pci_read_config_dword(pdev, SVWRKS_APSIZE + 4, &temp2); 4911da177e4SLinus Torvalds if (temp2 != 0) { 492e3cf6951SBjorn Helgaas dev_info(&pdev->dev, "64 bit aperture address, " 493e3cf6951SBjorn Helgaas "but top bits are not zero; disabling AGP\n"); 4941da177e4SLinus Torvalds return -ENODEV; 4951da177e4SLinus Torvalds } 4961da177e4SLinus Torvalds serverworks_private.mm_addr_ofs = 0x18; 4971da177e4SLinus Torvalds } else 4981da177e4SLinus Torvalds serverworks_private.mm_addr_ofs = 0x14; 4991da177e4SLinus Torvalds 5001da177e4SLinus Torvalds pci_read_config_dword(pdev, serverworks_private.mm_addr_ofs, &temp); 5011da177e4SLinus Torvalds if (temp & PCI_BASE_ADDRESS_MEM_TYPE_64) { 5021da177e4SLinus Torvalds pci_read_config_dword(pdev, 5031da177e4SLinus Torvalds serverworks_private.mm_addr_ofs + 4, &temp2); 5041da177e4SLinus Torvalds if (temp2 != 0) { 505e3cf6951SBjorn Helgaas dev_info(&pdev->dev, "64 bit MMIO address, but top " 506e3cf6951SBjorn Helgaas "bits are not zero; disabling AGP\n"); 5071da177e4SLinus Torvalds return -ENODEV; 5081da177e4SLinus Torvalds } 5091da177e4SLinus Torvalds } 5101da177e4SLinus Torvalds 5111da177e4SLinus Torvalds bridge = agp_alloc_bridge(); 5121da177e4SLinus Torvalds if (!bridge) 5131da177e4SLinus Torvalds return -ENOMEM; 5141da177e4SLinus Torvalds 5151da177e4SLinus Torvalds bridge->driver = &sworks_driver; 5161da177e4SLinus Torvalds bridge->dev_private_data = &serverworks_private, 517881ba59dSAlan Cox bridge->dev = pci_dev_get(pdev); 5181da177e4SLinus Torvalds 5191da177e4SLinus Torvalds pci_set_drvdata(pdev, bridge); 5201da177e4SLinus Torvalds return agp_add_bridge(bridge); 5211da177e4SLinus Torvalds } 5221da177e4SLinus Torvalds 52339af33fcSBill Pemberton static void agp_serverworks_remove(struct pci_dev *pdev) 5241da177e4SLinus Torvalds { 5251da177e4SLinus Torvalds struct agp_bridge_data *bridge = pci_get_drvdata(pdev); 5261da177e4SLinus Torvalds 527881ba59dSAlan Cox pci_dev_put(bridge->dev); 5281da177e4SLinus Torvalds agp_remove_bridge(bridge); 5291da177e4SLinus Torvalds agp_put_bridge(bridge); 530881ba59dSAlan Cox pci_dev_put(serverworks_private.svrwrks_dev); 531881ba59dSAlan Cox serverworks_private.svrwrks_dev = NULL; 5321da177e4SLinus Torvalds } 5331da177e4SLinus Torvalds 5341da177e4SLinus Torvalds static struct pci_device_id agp_serverworks_pci_table[] = { 5351da177e4SLinus Torvalds { 5361da177e4SLinus Torvalds .class = (PCI_CLASS_BRIDGE_HOST << 8), 5371da177e4SLinus Torvalds .class_mask = ~0, 5381da177e4SLinus Torvalds .vendor = PCI_VENDOR_ID_SERVERWORKS, 5391da177e4SLinus Torvalds .device = PCI_ANY_ID, 5401da177e4SLinus Torvalds .subvendor = PCI_ANY_ID, 5411da177e4SLinus Torvalds .subdevice = PCI_ANY_ID, 5421da177e4SLinus Torvalds }, 5431da177e4SLinus Torvalds { } 5441da177e4SLinus Torvalds }; 5451da177e4SLinus Torvalds 5461da177e4SLinus Torvalds MODULE_DEVICE_TABLE(pci, agp_serverworks_pci_table); 5471da177e4SLinus Torvalds 5481da177e4SLinus Torvalds static struct pci_driver agp_serverworks_pci_driver = { 5491da177e4SLinus Torvalds .name = "agpgart-serverworks", 5501da177e4SLinus Torvalds .id_table = agp_serverworks_pci_table, 5511da177e4SLinus Torvalds .probe = agp_serverworks_probe, 5521da177e4SLinus Torvalds .remove = agp_serverworks_remove, 5531da177e4SLinus Torvalds }; 5541da177e4SLinus Torvalds 5551da177e4SLinus Torvalds static int __init agp_serverworks_init(void) 5561da177e4SLinus Torvalds { 5571da177e4SLinus Torvalds if (agp_off) 5581da177e4SLinus Torvalds return -EINVAL; 5591da177e4SLinus Torvalds return pci_register_driver(&agp_serverworks_pci_driver); 5601da177e4SLinus Torvalds } 5611da177e4SLinus Torvalds 5621da177e4SLinus Torvalds static void __exit agp_serverworks_cleanup(void) 5631da177e4SLinus Torvalds { 5641da177e4SLinus Torvalds pci_unregister_driver(&agp_serverworks_pci_driver); 5651da177e4SLinus Torvalds } 5661da177e4SLinus Torvalds 5671da177e4SLinus Torvalds module_init(agp_serverworks_init); 5681da177e4SLinus Torvalds module_exit(agp_serverworks_cleanup); 5691da177e4SLinus Torvalds 5701da177e4SLinus Torvalds MODULE_LICENSE("GPL and additional rights"); 5711da177e4SLinus Torvalds 572