brintos

brintos / linux-shallow public Read only

0
0
Text · 15.0 KiB · 795c8c9 Raw
554 lines · c
1/*2 * AMD K7 AGPGART routines.3 */4 5#include <linux/module.h>6#include <linux/pci.h>7#include <linux/init.h>8#include <linux/agp_backend.h>9#include <linux/page-flags.h>10#include <linux/mm.h>11#include <linux/slab.h>12#include <asm/set_memory.h>13#include "agp.h"14 15#define AMD_MMBASE_BAR	116#define AMD_APSIZE	0xac17#define AMD_MODECNTL	0xb018#define AMD_MODECNTL2	0xb219#define AMD_GARTENABLE	0x02	/* In mmio region (16-bit register) */20#define AMD_ATTBASE	0x04	/* In mmio region (32-bit register) */21#define AMD_TLBFLUSH	0x0c	/* In mmio region (32-bit register) */22#define AMD_CACHEENTRY	0x10	/* In mmio region (32-bit register) */23 24static const struct pci_device_id agp_amdk7_pci_table[];25 26struct amd_page_map {27	unsigned long *real;28	unsigned long __iomem *remapped;29};30 31static struct _amd_irongate_private {32	volatile u8 __iomem *registers;33	struct amd_page_map **gatt_pages;34	int num_tables;35} amd_irongate_private;36 37static int amd_create_page_map(struct amd_page_map *page_map)38{39	int i;40 41	page_map->real = (unsigned long *) __get_free_page(GFP_KERNEL);42	if (page_map->real == NULL)43		return -ENOMEM;44 45	set_memory_uc((unsigned long)page_map->real, 1);46	page_map->remapped = page_map->real;47 48	for (i = 0; i < PAGE_SIZE / sizeof(unsigned long); i++) {49		writel(agp_bridge->scratch_page, page_map->remapped+i);50		readl(page_map->remapped+i);	/* PCI Posting. */51	}52 53	return 0;54}55 56static void amd_free_page_map(struct amd_page_map *page_map)57{58	set_memory_wb((unsigned long)page_map->real, 1);59	free_page((unsigned long) page_map->real);60}61 62static void amd_free_gatt_pages(void)63{64	int i;65	struct amd_page_map **tables;66	struct amd_page_map *entry;67 68	tables = amd_irongate_private.gatt_pages;69	for (i = 0; i < amd_irongate_private.num_tables; i++) {70		entry = tables[i];71		if (entry != NULL) {72			if (entry->real != NULL)73				amd_free_page_map(entry);74			kfree(entry);75		}76	}77	kfree(tables);78	amd_irongate_private.gatt_pages = NULL;79}80 81static int amd_create_gatt_pages(int nr_tables)82{83	struct amd_page_map **tables;84	struct amd_page_map *entry;85	int retval = 0;86	int i;87 88	tables = kcalloc(nr_tables + 1, sizeof(struct amd_page_map *),89			 GFP_KERNEL);90	if (tables == NULL)91		return -ENOMEM;92 93	for (i = 0; i < nr_tables; i++) {94		entry = kzalloc(sizeof(struct amd_page_map), GFP_KERNEL);95		tables[i] = entry;96		if (entry == NULL) {97			retval = -ENOMEM;98			break;99		}100		retval = amd_create_page_map(entry);101		if (retval != 0)102			break;103	}104	amd_irongate_private.num_tables = i;105	amd_irongate_private.gatt_pages = tables;106 107	if (retval != 0)108		amd_free_gatt_pages();109 110	return retval;111}112 113/* Since we don't need contiguous memory we just try114 * to get the gatt table once115 */116 117#define GET_PAGE_DIR_OFF(addr) (addr >> 22)118#define GET_PAGE_DIR_IDX(addr) (GET_PAGE_DIR_OFF(addr) - \119	GET_PAGE_DIR_OFF(agp_bridge->gart_bus_addr))120#define GET_GATT_OFF(addr) ((addr & 0x003ff000) >> 12)121#define GET_GATT(addr) (amd_irongate_private.gatt_pages[\122	GET_PAGE_DIR_IDX(addr)]->remapped)123 124static int amd_create_gatt_table(struct agp_bridge_data *bridge)125{126	struct aper_size_info_lvl2 *value;127	struct amd_page_map page_dir;128	unsigned long __iomem *cur_gatt;129	unsigned long addr;130	int retval;131	int i;132 133	value = A_SIZE_LVL2(agp_bridge->current_size);134	retval = amd_create_page_map(&page_dir);135	if (retval != 0)136		return retval;137 138	retval = amd_create_gatt_pages(value->num_entries / 1024);139	if (retval != 0) {140		amd_free_page_map(&page_dir);141		return retval;142	}143 144	agp_bridge->gatt_table_real = (u32 *)page_dir.real;145	agp_bridge->gatt_table = (u32 __iomem *)page_dir.remapped;146	agp_bridge->gatt_bus_addr = virt_to_phys(page_dir.real);147 148	/* Get the address for the gart region.149	 * This is a bus address even on the alpha, b/c its150	 * used to program the agp master not the cpu151	 */152 153	addr = pci_bus_address(agp_bridge->dev, AGP_APERTURE_BAR);154	agp_bridge->gart_bus_addr = addr;155 156	/* Calculate the agp offset */157	for (i = 0; i < value->num_entries / 1024; i++, addr += 0x00400000) {158		writel(virt_to_phys(amd_irongate_private.gatt_pages[i]->real) | 1,159			page_dir.remapped+GET_PAGE_DIR_OFF(addr));160		readl(page_dir.remapped+GET_PAGE_DIR_OFF(addr));	/* PCI Posting. */161	}162 163	for (i = 0; i < value->num_entries; i++) {164		addr = (i * PAGE_SIZE) + agp_bridge->gart_bus_addr;165		cur_gatt = GET_GATT(addr);166		writel(agp_bridge->scratch_page, cur_gatt+GET_GATT_OFF(addr));167		readl(cur_gatt+GET_GATT_OFF(addr));	/* PCI Posting. */168	}169 170	return 0;171}172 173static int amd_free_gatt_table(struct agp_bridge_data *bridge)174{175	struct amd_page_map page_dir;176 177	page_dir.real = (unsigned long *)agp_bridge->gatt_table_real;178	page_dir.remapped = (unsigned long __iomem *)agp_bridge->gatt_table;179 180	amd_free_gatt_pages();181	amd_free_page_map(&page_dir);182	return 0;183}184 185static int amd_irongate_fetch_size(void)186{187	int i;188	u32 temp;189	struct aper_size_info_lvl2 *values;190 191	pci_read_config_dword(agp_bridge->dev, AMD_APSIZE, &temp);192	temp = (temp & 0x0000000e);193	values = A_SIZE_LVL2(agp_bridge->driver->aperture_sizes);194	for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++) {195		if (temp == values[i].size_value) {196			agp_bridge->previous_size =197			    agp_bridge->current_size = (void *) (values + i);198 199			agp_bridge->aperture_size_idx = i;200			return values[i].size;201		}202	}203 204	return 0;205}206 207static int amd_irongate_configure(void)208{209	struct aper_size_info_lvl2 *current_size;210	phys_addr_t reg;211	u32 temp;212	u16 enable_reg;213 214	current_size = A_SIZE_LVL2(agp_bridge->current_size);215 216	if (!amd_irongate_private.registers) {217		/* Get the memory mapped registers */218		reg = pci_resource_start(agp_bridge->dev, AMD_MMBASE_BAR);219		amd_irongate_private.registers = (volatile u8 __iomem *) ioremap(reg, 4096);220		if (!amd_irongate_private.registers)221			return -ENOMEM;222	}223 224	/* Write out the address of the gatt table */225	writel(agp_bridge->gatt_bus_addr, amd_irongate_private.registers+AMD_ATTBASE);226	readl(amd_irongate_private.registers+AMD_ATTBASE);	/* PCI Posting. */227 228	/* Write the Sync register */229	pci_write_config_byte(agp_bridge->dev, AMD_MODECNTL, 0x80);230 231	/* Set indexing mode */232	pci_write_config_byte(agp_bridge->dev, AMD_MODECNTL2, 0x00);233 234	/* Write the enable register */235	enable_reg = readw(amd_irongate_private.registers+AMD_GARTENABLE);236	enable_reg = (enable_reg | 0x0004);237	writew(enable_reg, amd_irongate_private.registers+AMD_GARTENABLE);238	readw(amd_irongate_private.registers+AMD_GARTENABLE);	/* PCI Posting. */239 240	/* Write out the size register */241	pci_read_config_dword(agp_bridge->dev, AMD_APSIZE, &temp);242	temp = (((temp & ~(0x0000000e)) | current_size->size_value) | 1);243	pci_write_config_dword(agp_bridge->dev, AMD_APSIZE, temp);244 245	/* Flush the tlb */246	writel(1, amd_irongate_private.registers+AMD_TLBFLUSH);247	readl(amd_irongate_private.registers+AMD_TLBFLUSH);	/* PCI Posting.*/248	return 0;249}250 251static void amd_irongate_cleanup(void)252{253	struct aper_size_info_lvl2 *previous_size;254	u32 temp;255	u16 enable_reg;256 257	previous_size = A_SIZE_LVL2(agp_bridge->previous_size);258 259	enable_reg = readw(amd_irongate_private.registers+AMD_GARTENABLE);260	enable_reg = (enable_reg & ~(0x0004));261	writew(enable_reg, amd_irongate_private.registers+AMD_GARTENABLE);262	readw(amd_irongate_private.registers+AMD_GARTENABLE);	/* PCI Posting. */263 264	/* Write back the previous size and disable gart translation */265	pci_read_config_dword(agp_bridge->dev, AMD_APSIZE, &temp);266	temp = ((temp & ~(0x0000000f)) | previous_size->size_value);267	pci_write_config_dword(agp_bridge->dev, AMD_APSIZE, temp);268	iounmap((void __iomem *) amd_irongate_private.registers);269}270 271/*272 * This routine could be implemented by taking the addresses273 * written to the GATT, and flushing them individually.  However274 * currently it just flushes the whole table.  Which is probably275 * more efficient, since agp_memory blocks can be a large number of276 * entries.277 */278 279static void amd_irongate_tlbflush(struct agp_memory *temp)280{281	writel(1, amd_irongate_private.registers+AMD_TLBFLUSH);282	readl(amd_irongate_private.registers+AMD_TLBFLUSH);	/* PCI Posting. */283}284 285static int amd_insert_memory(struct agp_memory *mem, off_t pg_start, int type)286{287	int i, j, num_entries;288	unsigned long __iomem *cur_gatt;289	unsigned long addr;290 291	num_entries = A_SIZE_LVL2(agp_bridge->current_size)->num_entries;292 293	if (type != mem->type ||294	    agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type))295		return -EINVAL;296 297	if ((pg_start + mem->page_count) > num_entries)298		return -EINVAL;299 300	j = pg_start;301	while (j < (pg_start + mem->page_count)) {302		addr = (j * PAGE_SIZE) + agp_bridge->gart_bus_addr;303		cur_gatt = GET_GATT(addr);304		if (!PGE_EMPTY(agp_bridge, readl(cur_gatt+GET_GATT_OFF(addr))))305			return -EBUSY;306		j++;307	}308 309	if (!mem->is_flushed) {310		global_cache_flush();311		mem->is_flushed = true;312	}313 314	for (i = 0, j = pg_start; i < mem->page_count; i++, j++) {315		addr = (j * PAGE_SIZE) + agp_bridge->gart_bus_addr;316		cur_gatt = GET_GATT(addr);317		writel(agp_generic_mask_memory(agp_bridge,318					       page_to_phys(mem->pages[i]),319					       mem->type),320		       cur_gatt+GET_GATT_OFF(addr));321		readl(cur_gatt+GET_GATT_OFF(addr));	/* PCI Posting. */322	}323	amd_irongate_tlbflush(mem);324	return 0;325}326 327static int amd_remove_memory(struct agp_memory *mem, off_t pg_start, int type)328{329	int i;330	unsigned long __iomem *cur_gatt;331	unsigned long addr;332 333	if (type != mem->type ||334	    agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type))335		return -EINVAL;336 337	for (i = pg_start; i < (mem->page_count + pg_start); i++) {338		addr = (i * PAGE_SIZE) + agp_bridge->gart_bus_addr;339		cur_gatt = GET_GATT(addr);340		writel(agp_bridge->scratch_page, cur_gatt+GET_GATT_OFF(addr));341		readl(cur_gatt+GET_GATT_OFF(addr));	/* PCI Posting. */342	}343 344	amd_irongate_tlbflush(mem);345	return 0;346}347 348static const struct aper_size_info_lvl2 amd_irongate_sizes[7] =349{350	{2048, 524288, 0x0000000c},351	{1024, 262144, 0x0000000a},352	{512, 131072, 0x00000008},353	{256, 65536, 0x00000006},354	{128, 32768, 0x00000004},355	{64, 16384, 0x00000002},356	{32, 8192, 0x00000000}357};358 359static const struct gatt_mask amd_irongate_masks[] =360{361	{.mask = 1, .type = 0}362};363 364static const struct agp_bridge_driver amd_irongate_driver = {365	.owner			= THIS_MODULE,366	.aperture_sizes		= amd_irongate_sizes,367	.size_type		= LVL2_APER_SIZE,368	.num_aperture_sizes	= 7,369	.needs_scratch_page	= true,370	.configure		= amd_irongate_configure,371	.fetch_size		= amd_irongate_fetch_size,372	.cleanup		= amd_irongate_cleanup,373	.tlb_flush		= amd_irongate_tlbflush,374	.mask_memory		= agp_generic_mask_memory,375	.masks			= amd_irongate_masks,376	.agp_enable		= agp_generic_enable,377	.cache_flush		= global_cache_flush,378	.create_gatt_table	= amd_create_gatt_table,379	.free_gatt_table	= amd_free_gatt_table,380	.insert_memory		= amd_insert_memory,381	.remove_memory		= amd_remove_memory,382	.alloc_by_type		= agp_generic_alloc_by_type,383	.free_by_type		= agp_generic_free_by_type,384	.agp_alloc_page		= agp_generic_alloc_page,385	.agp_alloc_pages	= agp_generic_alloc_pages,386	.agp_destroy_page	= agp_generic_destroy_page,387	.agp_destroy_pages	= agp_generic_destroy_pages,388	.agp_type_to_mask_type  = agp_generic_type_to_mask_type,389};390 391static struct agp_device_ids amd_agp_device_ids[] =392{393	{394		.device_id	= PCI_DEVICE_ID_AMD_FE_GATE_7006,395		.chipset_name	= "Irongate",396	},397	{398		.device_id	= PCI_DEVICE_ID_AMD_FE_GATE_700E,399		.chipset_name	= "761",400	},401	{402		.device_id	= PCI_DEVICE_ID_AMD_FE_GATE_700C,403		.chipset_name	= "760MP",404	},405	{ }, /* dummy final entry, always present */406};407 408static int agp_amdk7_probe(struct pci_dev *pdev,409			   const struct pci_device_id *ent)410{411	struct agp_bridge_data *bridge;412	u8 cap_ptr;413	int j;414 415	cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP);416	if (!cap_ptr)417		return -ENODEV;418 419	j = ent - agp_amdk7_pci_table;420	dev_info(&pdev->dev, "AMD %s chipset\n",421		 amd_agp_device_ids[j].chipset_name);422 423	bridge = agp_alloc_bridge();424	if (!bridge)425		return -ENOMEM;426 427	bridge->driver = &amd_irongate_driver;428	bridge->dev_private_data = &amd_irongate_private;429	bridge->dev = pdev;430	bridge->capndx = cap_ptr;431 432	/* 751 Errata (22564_B-1.PDF)433	   erratum 20: strobe glitch with Nvidia NV10 GeForce cards.434	   system controller may experience noise due to strong drive strengths435	 */436	if (agp_bridge->dev->device == PCI_DEVICE_ID_AMD_FE_GATE_7006) {437		struct pci_dev *gfxcard=NULL;438 439		cap_ptr = 0;440		while (!cap_ptr) {441			gfxcard = pci_get_class(PCI_CLASS_DISPLAY_VGA<<8, gfxcard);442			if (!gfxcard) {443				dev_info(&pdev->dev, "no AGP VGA controller\n");444				return -ENODEV;445			}446			cap_ptr = pci_find_capability(gfxcard, PCI_CAP_ID_AGP);447		}448 449		/* With so many variants of NVidia cards, it's simpler just450		   to blacklist them all, and then whitelist them as needed451		   (if necessary at all). */452		if (gfxcard->vendor == PCI_VENDOR_ID_NVIDIA) {453			agp_bridge->flags |= AGP_ERRATA_1X;454			dev_info(&pdev->dev, "AMD 751 chipset with NVidia GeForce; forcing 1X due to errata\n");455		}456		pci_dev_put(gfxcard);457	}458 459	/* 761 Errata (23613_F.pdf)460	 * Revisions B0/B1 were a disaster.461	 * erratum 44: SYSCLK/AGPCLK skew causes 2X failures -- Force mode to 1X462	 * erratum 45: Timing problem prevents fast writes -- Disable fast write.463	 * erratum 46: Setup violation on AGP SBA pins - Disable side band addressing.464	 * With this lot disabled, we should prevent lockups. */465	if (agp_bridge->dev->device == PCI_DEVICE_ID_AMD_FE_GATE_700E) {466		if (pdev->revision == 0x10 || pdev->revision == 0x11) {467			agp_bridge->flags = AGP_ERRATA_FASTWRITES;468			agp_bridge->flags |= AGP_ERRATA_SBA;469			agp_bridge->flags |= AGP_ERRATA_1X;470			dev_info(&pdev->dev, "AMD 761 chipset with errata; disabling AGP fast writes & SBA and forcing to 1X\n");471		}472	}473 474	/* Fill in the mode register */475	pci_read_config_dword(pdev,476			bridge->capndx+PCI_AGP_STATUS,477			&bridge->mode);478 479	pci_set_drvdata(pdev, bridge);480	return agp_add_bridge(bridge);481}482 483static void agp_amdk7_remove(struct pci_dev *pdev)484{485	struct agp_bridge_data *bridge = pci_get_drvdata(pdev);486 487	agp_remove_bridge(bridge);488	agp_put_bridge(bridge);489}490 491static int agp_amdk7_resume(struct device *dev)492{493	return amd_irongate_driver.configure();494}495 496/* must be the same order as name table above */497static const struct pci_device_id agp_amdk7_pci_table[] = {498	{499	.class		= (PCI_CLASS_BRIDGE_HOST << 8),500	.class_mask	= ~0,501	.vendor		= PCI_VENDOR_ID_AMD,502	.device		= PCI_DEVICE_ID_AMD_FE_GATE_7006,503	.subvendor	= PCI_ANY_ID,504	.subdevice	= PCI_ANY_ID,505	},506	{507	.class		= (PCI_CLASS_BRIDGE_HOST << 8),508	.class_mask	= ~0,509	.vendor		= PCI_VENDOR_ID_AMD,510	.device		= PCI_DEVICE_ID_AMD_FE_GATE_700E,511	.subvendor	= PCI_ANY_ID,512	.subdevice	= PCI_ANY_ID,513	},514	{515	.class		= (PCI_CLASS_BRIDGE_HOST << 8),516	.class_mask	= ~0,517	.vendor		= PCI_VENDOR_ID_AMD,518	.device		= PCI_DEVICE_ID_AMD_FE_GATE_700C,519	.subvendor	= PCI_ANY_ID,520	.subdevice	= PCI_ANY_ID,521	},522	{ }523};524 525MODULE_DEVICE_TABLE(pci, agp_amdk7_pci_table);526 527static DEFINE_SIMPLE_DEV_PM_OPS(agp_amdk7_pm_ops, NULL, agp_amdk7_resume);528 529static struct pci_driver agp_amdk7_pci_driver = {530	.name		= "agpgart-amdk7",531	.id_table	= agp_amdk7_pci_table,532	.probe		= agp_amdk7_probe,533	.remove		= agp_amdk7_remove,534	.driver.pm	= &agp_amdk7_pm_ops,535};536 537static int __init agp_amdk7_init(void)538{539	if (agp_off)540		return -EINVAL;541	return pci_register_driver(&agp_amdk7_pci_driver);542}543 544static void __exit agp_amdk7_cleanup(void)545{546	pci_unregister_driver(&agp_amdk7_pci_driver);547}548 549module_init(agp_amdk7_init);550module_exit(agp_amdk7_cleanup);551 552MODULE_DESCRIPTION("AMD K7 AGPGART routines");553MODULE_LICENSE("GPL and additional rights");554