head 1.1; branch 1.1.1; access; symbols libdrm-1_0_4:1.1.1.1 AGPGART_2_0:1.1.1.1 TUNGSTEN:1.1.1; locks; strict; comment @ * @; 1.1 date 2003.01.11.02.19.38; author jhartmann; state Exp; branches 1.1.1.1; next ; 1.1.1.1 date 2003.01.11.02.19.38; author jhartmann; state Exp; branches; next ; desc @@ 1.1 log @Initial revision @ text @#ifdef CONFIG_AGP_AMD typedef struct _amd_page_map { unsigned long *real; unsigned long *remapped; } amd_page_map; static struct _amd_irongate_private { volatile u8 *registers; amd_page_map **gatt_pages; int num_tables; unsigned long *real; unsigned long *remapped; } amd_irongate_private; static int amd_create_page_map(amd_page_map *page_map) { int i; int err = 0; page_map->real = (unsigned long *) __get_free_page(GFP_KERNEL); if (page_map->real == NULL) { return -ENOMEM; } SetPageReserved(virt_to_page(page_map->real)); CACHE_FLUSH(); #ifdef CONFIG_X86 err = change_page_attr(virt_to_page(page_map->real), 1, PAGE_KERNEL_NOCACHE); #endif if (!err) page_map->remapped = ioremap_nocache(virt_to_phys(page_map->real), PAGE_SIZE); if (page_map->remapped == NULL || err) { ClearPageReserved(virt_to_page(page_map->real)); free_page((unsigned long) page_map->real); page_map->real = NULL; return -ENOMEM; } CACHE_FLUSH(); for(i = 0; i < PAGE_SIZE / sizeof(unsigned long); i++) { page_map->remapped[i] = agp_bridge->scratch_page_addr; } return 0; } static void amd_free_page_map(amd_page_map *page_map) { iounmap(page_map->remapped); #ifdef CONFIG_X86 change_page_attr(virt_to_page(page_map->real), 1, PAGE_KERNEL); #endif ClearPageReserved(virt_to_page(page_map->real)); free_page((unsigned long) page_map->real); } static void amd_free_gatt_pages(void) { int i; amd_page_map **tables; amd_page_map *entry; tables = amd_irongate_private.gatt_pages; for(i = 0; i < amd_irongate_private.num_tables; i++) { entry = tables[i]; if (entry != NULL) { if (entry->real != NULL) { amd_free_page_map(entry); } kfree(entry); } } kfree(tables); } static int amd_create_gatt_pages(int nr_tables) { amd_page_map **tables; amd_page_map *entry; int retval = 0; int i; tables = kmalloc((nr_tables + 1) * sizeof(amd_page_map *), GFP_KERNEL); if (tables == NULL) { return -ENOMEM; } memset(tables, 0, sizeof(amd_page_map *) * (nr_tables + 1)); for (i = 0; i < nr_tables; i++) { entry = kmalloc(sizeof(amd_page_map), GFP_KERNEL); if (entry == NULL) { retval = -ENOMEM; break; } memset(entry, 0, sizeof(amd_page_map)); tables[i] = entry; retval = amd_create_page_map(entry); if (retval != 0) break; } amd_irongate_private.num_tables = nr_tables; amd_irongate_private.gatt_pages = tables; if (retval != 0) amd_free_gatt_pages(); return retval; } /* Since we don't need contigious memory we just try * to get the gatt table once */ #define GET_PAGE_DIR_OFF(addr) (addr >> 22) #define GET_PAGE_DIR_IDX(addr) (GET_PAGE_DIR_OFF(addr) - \ GET_PAGE_DIR_OFF(agp_bridge->gart_bus_addr)) #define GET_GATT_OFF(addr) ((addr & 0x003ff000) >> 12) #define GET_GATT(addr) (amd_irongate_private.gatt_pages[\ GET_PAGE_DIR_IDX(addr)]->remapped) static int amd_create_gatt_table(void) { aper_size_info_lvl2 *value; amd_page_map page_dir; unsigned long addr; int retval; u32 temp; int i; value = A_SIZE_LVL2(agp_bridge->current_size); retval = amd_create_page_map(&page_dir); if (retval != 0) { return retval; } retval = amd_create_gatt_pages(value->num_entries / 1024); if (retval != 0) { amd_free_page_map(&page_dir); return retval; } agp_bridge->gatt = NULL; agp_bridge->gatt_table = NULL; agp_bridge->gatt_bus_addr = virt_to_bus(page_dir.real); /* Get the address for the gart region. * This is a bus address even on the alpha, b/c its * used to program the agp master not the cpu */ pci_read_config_dword(agp_bridge->dev, AMD_APBASE, &temp); addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); agp_bridge->gart_bus_addr = addr; /* Calculate the agp offset */ for(i = 0; i < value->num_entries / 1024; i++, addr += 0x00400000) { page_dir.remapped[GET_PAGE_DIR_OFF(addr)] = virt_to_bus(amd_irongate_private.gatt_pages[i]->real); page_dir.remapped[GET_PAGE_DIR_OFF(addr)] |= 0x00000001; } return 0; } static int amd_free_gatt_table(void) { amd_page_map page_dir; page_dir.real = amd_irongate_private.real; page_dir.remapped = amd_irongate_private.remapped; amd_free_gatt_pages(); amd_free_page_map(&page_dir); return 0; } static int amd_irongate_fetch_size(void) { int i; u32 temp; aper_size_info_lvl2 *values; pci_read_config_dword(agp_bridge->dev, AMD_APSIZE, &temp); temp = (temp & 0x0000000e); values = A_SIZE_LVL2(agp_bridge->aperture_sizes); for (i = 0; i < agp_bridge->num_aperture_sizes; i++) { if (temp == values[i].size_value) { agp_bridge->previous_size = agp_bridge->current_size = (void *) (values + i); agp_bridge->aperture_size_idx = i; return values[i].size; } } return 0; } static int amd_irongate_configure(void) { aper_size_info_lvl2 *current_size; u32 temp; u16 enable_reg; current_size = A_SIZE_LVL2(agp_bridge->current_size); /* Get the memory mapped registers */ pci_read_config_dword(agp_bridge->dev, AMD_MMBASE, &temp); temp = (temp & PCI_BASE_ADDRESS_MEM_MASK); amd_irongate_private.registers = (volatile u8 *) ioremap(temp, 4096); /* Write out the address of the gatt table */ OUTREG32(amd_irongate_private.registers, AMD_ATTBASE, agp_bridge->gatt_bus_addr); /* Write the Sync register */ pci_write_config_byte(agp_bridge->dev, AMD_MODECNTL, 0x80); /* Set indexing mode */ pci_write_config_byte(agp_bridge->dev, AMD_MODECNTL2, 0x00); /* Write the enable register */ enable_reg = INREG16(amd_irongate_private.registers, AMD_GARTENABLE); enable_reg = (enable_reg | 0x0004); OUTREG16(amd_irongate_private.registers, AMD_GARTENABLE, enable_reg); /* Write out the size register */ pci_read_config_dword(agp_bridge->dev, AMD_APSIZE, &temp); temp = (((temp & ~(0x0000000e)) | current_size->size_value) | 0x00000001); pci_write_config_dword(agp_bridge->dev, AMD_APSIZE, temp); /* Flush the tlb */ OUTREG32(amd_irongate_private.registers, AMD_TLBFLUSH, 0x00000001); return 0; } static void amd_irongate_cleanup(void) { aper_size_info_lvl2 *previous_size; u32 temp; u16 enable_reg; previous_size = A_SIZE_LVL2(agp_bridge->previous_size); enable_reg = INREG16(amd_irongate_private.registers, AMD_GARTENABLE); enable_reg = (enable_reg & ~(0x0004)); OUTREG16(amd_irongate_private.registers, AMD_GARTENABLE, enable_reg); /* Write back the previous size and disable gart translation */ pci_read_config_dword(agp_bridge->dev, AMD_APSIZE, &temp); temp = ((temp & ~(0x0000000f)) | previous_size->size_value); pci_write_config_dword(agp_bridge->dev, AMD_APSIZE, temp); iounmap((void *) amd_irongate_private.registers); } /* * This routine could be implemented by taking the addresses * written to the GATT, and flushing them individually. However * currently it just flushes the whole table. Which is probably * more efficent, since agp_memory blocks can be a large number of * entries. */ static void amd_irongate_tlbflush(agp_memory * temp) { OUTREG32(amd_irongate_private.registers, AMD_TLBFLUSH, 0x00000001); } static int amd_insert_memory(agp_memory * mem, off_t pg_start, int type) { int i, j, num_entries; unsigned long *cur_gatt; unsigned long addr; num_entries = A_SIZE_LVL2(agp_bridge->current_size)->num_entries; if (type != 0 || mem->type != 0) { return -EINVAL; } if ((pg_start + mem->page_count) > num_entries) { return -EINVAL; } if(agp_test_region_for_bound_intersect(pg_start, mem->page_count)) { return -EBUSY; } if (mem->is_flushed == FALSE) { CACHE_FLUSH(); mem->is_flushed = TRUE; } for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { addr = (j * PAGE_SIZE) + agp_bridge->gart_bus_addr; cur_gatt = GET_GATT(addr); cur_gatt[GET_GATT_OFF(addr)] = mem->memory[i]; } agp_bridge->tlb_flush(mem); return 0; } static int amd_remove_memory(agp_memory * mem, off_t pg_start, int type) { int i; unsigned long *cur_gatt; unsigned long addr; if (type != 0 || mem->type != 0) { return -EINVAL; } for (i = pg_start; i < (mem->page_count + pg_start); i++) { addr = (i * PAGE_SIZE) + agp_bridge->gart_bus_addr; cur_gatt = GET_GATT(addr); cur_gatt[GET_GATT_OFF(addr)] = (unsigned long) agp_bridge->scratch_page_addr; } agp_bridge->tlb_flush(mem); return 0; } static aper_size_info_lvl2 amd_irongate_sizes[7] = { {2048, 524288, 0x0000000c}, {1024, 262144, 0x0000000a}, {512, 131072, 0x00000008}, {256, 65536, 0x00000006}, {128, 32768, 0x00000004}, {64, 16384, 0x00000002}, {32, 8192, 0x00000000} }; static int __init amd_irongate_setup (struct pci_dev *pdev) { agp_bridge->aperture_sizes = (void *) amd_irongate_sizes; agp_bridge->size_type = LVL2_APER_SIZE; agp_bridge->num_aperture_sizes = 7; agp_bridge->dev_private_data = (void *) &amd_irongate_private; agp_bridge->needs_scratch_page = FALSE; agp_bridge->configure = amd_irongate_configure; agp_bridge->fetch_size = amd_irongate_fetch_size; agp_bridge->cleanup = amd_irongate_cleanup; agp_bridge->tlb_flush = amd_irongate_tlbflush; agp_bridge->mask_memory = agp_generic_mask_memory; agp_bridge->agp_enable = agp_generic_agp_enable; agp_bridge->cache_flush = global_cache_flush; agp_bridge->create_gatt_table = amd_create_gatt_table; agp_bridge->free_gatt_table = amd_free_gatt_table; agp_bridge->insert_memory = amd_insert_memory; agp_bridge->remove_memory = amd_remove_memory; agp_bridge->alloc_by_type = agp_generic_alloc_by_type; agp_bridge->free_by_type = agp_generic_free_by_type; agp_bridge->agp_alloc_page = agp_generic_alloc_page; agp_bridge->agp_free_page = agp_generic_free_page; agp_bridge->get_reserved_map = agp_generic_get_reserved_map; agp_bridge->suspend = agp_generic_suspend; agp_bridge->resume = agp_generic_resume; agp_bridge->get_vm_config = agp_generic_get_vm_config; return 0; (void) pdev; /* unused */ } #endif /* CONFIG_AGP_AMD */ #ifdef CONFIG_AGP_AMD_8151 /* Begin AMD-8151 support */ static u_int64_t pci_read64 (struct pci_dev *dev, int reg) { union { u64 full; struct { u32 high; u32 low; } split; } tmp; pci_read_config_dword(dev, reg, &tmp.split.high); pci_read_config_dword(dev, reg+4, &tmp.split.low); return tmp.full; } static void pci_write64 (struct pci_dev *dev, int reg, u64 value) { union { u64 full; struct { u32 high; u32 low; } split; } tmp; tmp.full = value; pci_write_config_dword(dev, reg, tmp.split.high); pci_write_config_dword(dev, reg+4, tmp.split.low); } static int x86_64_insert_memory(agp_memory * mem, off_t pg_start, int type) { int i, j, num_entries; void *temp; long tmp; u32 pte; u64 addr; temp = agp_bridge->current_size; num_entries = A_SIZE_32(temp)->num_entries; num_entries -= agp_memory_reserved>>PAGE_SHIFT; if (type != 0 || mem->type != 0) return -EINVAL; /* Make sure we can fit the range in the gatt table. */ if ((pg_start + mem->page_count) > num_entries) return -EINVAL; j = pg_start; /* gatt table should be empty. */ if(agp_test_region_for_bound_intersect(pg_start, mem->page_count)) { return -EBUSY; } if (mem->is_flushed == FALSE) { CACHE_FLUSH(); mem->is_flushed = TRUE; } for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { addr = mem->memory[i]; tmp = addr; BUG_ON(tmp & 0xffffff0000000ffc); pte = (tmp & 0x000000ff00000000) >> 28; pte |=(tmp & 0x00000000fffff000); pte |= 1<<1|1<<0; agp_bridge->gatt_table[j] = pte; } agp_bridge->tlb_flush(mem); return 0; } /* * This hack alters the order element according * to the size of a long. It sucks. I totally disown this, even * though it does appear to work for the most part. */ static aper_size_info_32 x86_64_aperture_sizes[7] = { {32, 8192, 3+(sizeof(long)/8), 0 }, {64, 16384, 4+(sizeof(long)/8), 1<<1 }, {128, 32768, 5+(sizeof(long)/8), 1<<2 }, {256, 65536, 6+(sizeof(long)/8), 1<<1 | 1<<2 }, {512, 131072, 7+(sizeof(long)/8), 1<<3 }, {1024, 262144, 8+(sizeof(long)/8), 1<<1 | 1<<3}, {2048, 524288, 9+(sizeof(long)/8), 1<<2 | 1<<3} }; /* * Get the current Aperture size from the x86-64. * Note, that there may be multiple x86-64's, but we just return * the value from the first one we find. The set_size functions * keep the rest coherent anyway. Or at least should do. */ static int amd_x86_64_fetch_size(void) { struct pci_dev *dev; int i; u32 temp; aper_size_info_32 *values; pci_for_each_dev(dev) { if (dev->bus->number==0 && PCI_FUNC(dev->devfn)==3 && PCI_SLOT(dev->devfn)>=24 && PCI_SLOT(dev->devfn)<=31) { pci_read_config_dword(dev, AMD_X86_64_GARTAPERTURECTL, &temp); temp = (temp & 0xe); values = A_SIZE_32(x86_64_aperture_sizes); for (i = 0; i < agp_bridge->num_aperture_sizes; i++) { if (temp == values[i].size_value) { agp_bridge->previous_size = agp_bridge->current_size = (void *) (values + i); agp_bridge->aperture_size_idx = i; return values[i].size; } } } } /* erk, couldn't find an x86-64 ? */ return 0; } static void inline flush_x86_64_tlb(struct pci_dev *dev) { u32 tmp; pci_read_config_dword (dev, AMD_X86_64_GARTCACHECTL, &tmp); tmp |= 1<<0; pci_write_config_dword (dev, AMD_X86_64_GARTCACHECTL, tmp); } void amd_x86_64_tlbflush(agp_memory * temp) { struct pci_dev *dev; pci_for_each_dev(dev) { if (dev->bus->number==0 && PCI_FUNC(dev->devfn)==3 && PCI_SLOT(dev->devfn) >=24 && PCI_SLOT(dev->devfn) <=31) { flush_x86_64_tlb (dev); } } } /* * In a multiprocessor x86-64 system, this function gets * called once for each CPU. */ u64 amd_x86_64_configure (struct pci_dev *hammer, u64 gatt_table) { u64 aperturebase; u32 tmp; u64 addr, aper_base; /* Address to map to */ pci_read_config_dword (hammer, AMD_X86_64_GARTAPERTUREBASE, &tmp); aperturebase = tmp << 25; aper_base = (aperturebase & PCI_BASE_ADDRESS_MEM_MASK); /* address of the mappings table */ addr = (u64) gatt_table; addr >>= 12; tmp = (u32) addr<<4; tmp &= ~0xf; pci_write_config_dword (hammer, AMD_X86_64_GARTTABLEBASE, tmp); /* Enable GART translation for this hammer. */ pci_read_config_dword(hammer, AMD_X86_64_GARTAPERTURECTL, &tmp); tmp &= 0x3f; tmp |= 1<<0; pci_write_config_dword(hammer, AMD_X86_64_GARTAPERTURECTL, tmp); /* keep CPU's coherent. */ flush_x86_64_tlb (hammer); return aper_base; } static aper_size_info_32 amd_8151_sizes[7] = { {2048, 524288, 9, 0x00000000 }, /* 0 0 0 0 0 0 */ {1024, 262144, 8, 0x00000400 }, /* 1 0 0 0 0 0 */ {512, 131072, 7, 0x00000600 }, /* 1 1 0 0 0 0 */ {256, 65536, 6, 0x00000700 }, /* 1 1 1 0 0 0 */ {128, 32768, 5, 0x00000720 }, /* 1 1 1 1 0 0 */ {64, 16384, 4, 0x00000730 }, /* 1 1 1 1 1 0 */ {32, 8192, 3, 0x00000738 } /* 1 1 1 1 1 1 */ }; static int amd_8151_configure(void) { struct pci_dev *dev, *hammer=NULL; int current_size; int tmp, tmp2, i; u64 aperbar; unsigned long gatt_bus = __pa(page_address(agp_bridge->gatt)); /* Configure AGP regs in each x86-64 host bridge. */ pci_for_each_dev(dev) { if (dev->bus->number==0 && PCI_FUNC(dev->devfn)==3 && PCI_SLOT(dev->devfn)>=24 && PCI_SLOT(dev->devfn)<=31) { agp_bridge->gart_bus_addr = amd_x86_64_configure(dev,gatt_bus); hammer = dev; /* * TODO: Cache pci_dev's of x86-64's in private struct to save us * having to scan the pci list each time. */ } } if (hammer == NULL) { return -ENODEV; } /* Shadow x86-64 registers into 8151 registers. */ dev = agp_bridge->dev; if (!dev) return -ENODEV; current_size = amd_x86_64_fetch_size(); pci_read_config_dword(dev, AMD_8151_APERTURESIZE, &tmp); tmp &= ~(0xfff); /* translate x86-64 size bits to 8151 size bits*/ for (i=0 ; i<7; i++) { if (amd_8151_sizes[i].size == current_size) tmp |= (amd_8151_sizes[i].size_value) << 3; } pci_write_config_dword(dev, AMD_8151_APERTURESIZE, tmp); pci_read_config_dword (hammer, AMD_X86_64_GARTAPERTUREBASE, &tmp); aperbar = pci_read64 (dev, AMD_8151_VMAPERTURE); aperbar |= (tmp & 0x7fff) <<25; aperbar &= 0x000000ffffffffff; aperbar |= 1<<2; /* This address is a 64bit ptr FIXME: Make conditional in 32bit mode */ pci_write64 (dev, AMD_8151_VMAPERTURE, aperbar); pci_read_config_dword(dev, AMD_8151_AGP_CTL , &tmp); tmp &= ~(AMD_8151_GTLBEN | AMD_8151_APEREN); pci_read_config_dword(hammer, AMD_X86_64_GARTAPERTURECTL, &tmp2); if (tmp2 & AMD_X86_64_GARTEN) tmp |= AMD_8151_APEREN; // FIXME: bit 7 of AMD_8151_AGP_CTL (GTLBEN) must be copied if set. // But where is it set ? pci_write_config_dword(dev, AMD_8151_AGP_CTL, tmp); return 0; } static void amd_8151_cleanup(void) { struct pci_dev *dev; u32 tmp; pci_for_each_dev(dev) { /* disable gart translation */ if (dev->bus->number==0 && PCI_FUNC(dev->devfn)==3 && (PCI_SLOT(dev->devfn) >=24) && (PCI_SLOT(dev->devfn) <=31)) { pci_read_config_dword (dev, AMD_X86_64_GARTAPERTURECTL, &tmp); tmp &= ~(AMD_X86_64_GARTEN); pci_write_config_dword (dev, AMD_X86_64_GARTAPERTURECTL, tmp); } /* Now shadow the disable in the 8151 */ if (dev->vendor == PCI_VENDOR_ID_AMD && dev->device == PCI_DEVICE_ID_AMD_8151_0) { pci_read_config_dword (dev, AMD_8151_AGP_CTL, &tmp); tmp &= ~(AMD_8151_APEREN); pci_write_config_dword (dev, AMD_8151_AGP_CTL, tmp); } } } static int __init amd_8151_setup (struct pci_dev *pdev) { agp_bridge->aperture_sizes = (void *) amd_8151_sizes; agp_bridge->size_type = U32_APER_SIZE; agp_bridge->num_aperture_sizes = 7; agp_bridge->dev_private_data = NULL; agp_bridge->needs_scratch_page = FALSE; agp_bridge->configure = amd_8151_configure; agp_bridge->fetch_size = amd_x86_64_fetch_size; agp_bridge->cleanup = amd_8151_cleanup; agp_bridge->tlb_flush = amd_x86_64_tlbflush; agp_bridge->mask_memory = agp_generic_mask_memory; agp_bridge->agp_enable = agp_generic_agp_enable; agp_bridge->cache_flush = global_cache_flush; agp_bridge->create_gatt_table = agp_generic_create_gatt_table; agp_bridge->free_gatt_table = agp_generic_free_gatt_table; agp_bridge->insert_memory = x86_64_insert_memory; agp_bridge->remove_memory = agp_generic_remove_memory; agp_bridge->alloc_by_type = agp_generic_alloc_by_type; agp_bridge->free_by_type = agp_generic_free_by_type; agp_bridge->agp_alloc_page = agp_generic_alloc_page; agp_bridge->agp_free_page = agp_generic_free_page; agp_bridge->get_reserved_map = agp_generic_get_reserved_map; agp_bridge->suspend = agp_generic_suspend; agp_bridge->resume = agp_generic_resume; agp_bridge->get_vm_config = agp_generic_get_vm_config; return 0; (void) pdev; /* unused */ } #endif /* CONFIG_AGP_AMD_8151 */ @ 1.1.1.1 log @Import of Agpgart 2.0 module @ text @@