18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Nvidia AGPGART routines.
38c2ecf20Sopenharmony_ci * Based upon a 2.4 agpgart diff by the folks from NVIDIA, and hacked up
48c2ecf20Sopenharmony_ci * to work in 2.5 by Dave Jones.
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/module.h>
88c2ecf20Sopenharmony_ci#include <linux/pci.h>
98c2ecf20Sopenharmony_ci#include <linux/init.h>
108c2ecf20Sopenharmony_ci#include <linux/agp_backend.h>
118c2ecf20Sopenharmony_ci#include <linux/page-flags.h>
128c2ecf20Sopenharmony_ci#include <linux/mm.h>
138c2ecf20Sopenharmony_ci#include <linux/jiffies.h>
148c2ecf20Sopenharmony_ci#include "agp.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci/* NVIDIA registers */
178c2ecf20Sopenharmony_ci#define NVIDIA_0_APSIZE		0x80
188c2ecf20Sopenharmony_ci#define NVIDIA_1_WBC		0xf0
198c2ecf20Sopenharmony_ci#define NVIDIA_2_GARTCTRL	0xd0
208c2ecf20Sopenharmony_ci#define NVIDIA_2_APBASE		0xd8
218c2ecf20Sopenharmony_ci#define NVIDIA_2_APLIMIT	0xdc
228c2ecf20Sopenharmony_ci#define NVIDIA_2_ATTBASE(i)	(0xe0 + (i) * 4)
238c2ecf20Sopenharmony_ci#define NVIDIA_3_APBASE		0x50
248c2ecf20Sopenharmony_ci#define NVIDIA_3_APLIMIT	0x54
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic struct _nvidia_private {
288c2ecf20Sopenharmony_ci	struct pci_dev *dev_1;
298c2ecf20Sopenharmony_ci	struct pci_dev *dev_2;
308c2ecf20Sopenharmony_ci	struct pci_dev *dev_3;
318c2ecf20Sopenharmony_ci	volatile u32 __iomem *aperture;
328c2ecf20Sopenharmony_ci	int num_active_entries;
338c2ecf20Sopenharmony_ci	off_t pg_offset;
348c2ecf20Sopenharmony_ci	u32 wbc_mask;
358c2ecf20Sopenharmony_ci} nvidia_private;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic int nvidia_fetch_size(void)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	int i;
418c2ecf20Sopenharmony_ci	u8 size_value;
428c2ecf20Sopenharmony_ci	struct aper_size_info_8 *values;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	pci_read_config_byte(agp_bridge->dev, NVIDIA_0_APSIZE, &size_value);
458c2ecf20Sopenharmony_ci	size_value &= 0x0f;
468c2ecf20Sopenharmony_ci	values = A_SIZE_8(agp_bridge->driver->aperture_sizes);
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++) {
498c2ecf20Sopenharmony_ci		if (size_value == values[i].size_value) {
508c2ecf20Sopenharmony_ci			agp_bridge->previous_size =
518c2ecf20Sopenharmony_ci				agp_bridge->current_size = (void *) (values + i);
528c2ecf20Sopenharmony_ci			agp_bridge->aperture_size_idx = i;
538c2ecf20Sopenharmony_ci			return values[i].size;
548c2ecf20Sopenharmony_ci		}
558c2ecf20Sopenharmony_ci	}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	return 0;
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci#define SYSCFG          0xC0010010
618c2ecf20Sopenharmony_ci#define IORR_BASE0      0xC0010016
628c2ecf20Sopenharmony_ci#define IORR_MASK0      0xC0010017
638c2ecf20Sopenharmony_ci#define AMD_K7_NUM_IORR 2
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic int nvidia_init_iorr(u32 base, u32 size)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	u32 base_hi, base_lo;
688c2ecf20Sopenharmony_ci	u32 mask_hi, mask_lo;
698c2ecf20Sopenharmony_ci	u32 sys_hi, sys_lo;
708c2ecf20Sopenharmony_ci	u32 iorr_addr, free_iorr_addr;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	/* Find the iorr that is already used for the base */
738c2ecf20Sopenharmony_ci	/* If not found, determine the uppermost available iorr */
748c2ecf20Sopenharmony_ci	free_iorr_addr = AMD_K7_NUM_IORR;
758c2ecf20Sopenharmony_ci	for (iorr_addr = 0; iorr_addr < AMD_K7_NUM_IORR; iorr_addr++) {
768c2ecf20Sopenharmony_ci		rdmsr(IORR_BASE0 + 2 * iorr_addr, base_lo, base_hi);
778c2ecf20Sopenharmony_ci		rdmsr(IORR_MASK0 + 2 * iorr_addr, mask_lo, mask_hi);
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci		if ((base_lo & 0xfffff000) == (base & 0xfffff000))
808c2ecf20Sopenharmony_ci			break;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci		if ((mask_lo & 0x00000800) == 0)
838c2ecf20Sopenharmony_ci			free_iorr_addr = iorr_addr;
848c2ecf20Sopenharmony_ci	}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	if (iorr_addr >= AMD_K7_NUM_IORR) {
878c2ecf20Sopenharmony_ci		iorr_addr = free_iorr_addr;
888c2ecf20Sopenharmony_ci		if (iorr_addr >= AMD_K7_NUM_IORR)
898c2ecf20Sopenharmony_ci			return -EINVAL;
908c2ecf20Sopenharmony_ci	}
918c2ecf20Sopenharmony_ci    base_hi = 0x0;
928c2ecf20Sopenharmony_ci    base_lo = (base & ~0xfff) | 0x18;
938c2ecf20Sopenharmony_ci    mask_hi = 0xf;
948c2ecf20Sopenharmony_ci    mask_lo = ((~(size - 1)) & 0xfffff000) | 0x800;
958c2ecf20Sopenharmony_ci    wrmsr(IORR_BASE0 + 2 * iorr_addr, base_lo, base_hi);
968c2ecf20Sopenharmony_ci    wrmsr(IORR_MASK0 + 2 * iorr_addr, mask_lo, mask_hi);
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci    rdmsr(SYSCFG, sys_lo, sys_hi);
998c2ecf20Sopenharmony_ci    sys_lo |= 0x00100000;
1008c2ecf20Sopenharmony_ci    wrmsr(SYSCFG, sys_lo, sys_hi);
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	return 0;
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic int nvidia_configure(void)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	int i, rc, num_dirs;
1088c2ecf20Sopenharmony_ci	u32 apbase, aplimit;
1098c2ecf20Sopenharmony_ci	phys_addr_t apbase_phys;
1108c2ecf20Sopenharmony_ci	struct aper_size_info_8 *current_size;
1118c2ecf20Sopenharmony_ci	u32 temp;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	current_size = A_SIZE_8(agp_bridge->current_size);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	/* aperture size */
1168c2ecf20Sopenharmony_ci	pci_write_config_byte(agp_bridge->dev, NVIDIA_0_APSIZE,
1178c2ecf20Sopenharmony_ci		current_size->size_value);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	/* address to map to */
1208c2ecf20Sopenharmony_ci	apbase = pci_bus_address(agp_bridge->dev, AGP_APERTURE_BAR);
1218c2ecf20Sopenharmony_ci	agp_bridge->gart_bus_addr = apbase;
1228c2ecf20Sopenharmony_ci	aplimit = apbase + (current_size->size * 1024 * 1024) - 1;
1238c2ecf20Sopenharmony_ci	pci_write_config_dword(nvidia_private.dev_2, NVIDIA_2_APBASE, apbase);
1248c2ecf20Sopenharmony_ci	pci_write_config_dword(nvidia_private.dev_2, NVIDIA_2_APLIMIT, aplimit);
1258c2ecf20Sopenharmony_ci	pci_write_config_dword(nvidia_private.dev_3, NVIDIA_3_APBASE, apbase);
1268c2ecf20Sopenharmony_ci	pci_write_config_dword(nvidia_private.dev_3, NVIDIA_3_APLIMIT, aplimit);
1278c2ecf20Sopenharmony_ci	if (0 != (rc = nvidia_init_iorr(apbase, current_size->size * 1024 * 1024)))
1288c2ecf20Sopenharmony_ci		return rc;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	/* directory size is 64k */
1318c2ecf20Sopenharmony_ci	num_dirs = current_size->size / 64;
1328c2ecf20Sopenharmony_ci	nvidia_private.num_active_entries = current_size->num_entries;
1338c2ecf20Sopenharmony_ci	nvidia_private.pg_offset = 0;
1348c2ecf20Sopenharmony_ci	if (num_dirs == 0) {
1358c2ecf20Sopenharmony_ci		num_dirs = 1;
1368c2ecf20Sopenharmony_ci		nvidia_private.num_active_entries /= (64 / current_size->size);
1378c2ecf20Sopenharmony_ci		nvidia_private.pg_offset = (apbase & (64 * 1024 * 1024 - 1) &
1388c2ecf20Sopenharmony_ci			~(current_size->size * 1024 * 1024 - 1)) / PAGE_SIZE;
1398c2ecf20Sopenharmony_ci	}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	/* attbase */
1428c2ecf20Sopenharmony_ci	for (i = 0; i < 8; i++) {
1438c2ecf20Sopenharmony_ci		pci_write_config_dword(nvidia_private.dev_2, NVIDIA_2_ATTBASE(i),
1448c2ecf20Sopenharmony_ci			(agp_bridge->gatt_bus_addr + (i % num_dirs) * 64 * 1024) | 1);
1458c2ecf20Sopenharmony_ci	}
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	/* gtlb control */
1488c2ecf20Sopenharmony_ci	pci_read_config_dword(nvidia_private.dev_2, NVIDIA_2_GARTCTRL, &temp);
1498c2ecf20Sopenharmony_ci	pci_write_config_dword(nvidia_private.dev_2, NVIDIA_2_GARTCTRL, temp | 0x11);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	/* gart control */
1528c2ecf20Sopenharmony_ci	pci_read_config_dword(agp_bridge->dev, NVIDIA_0_APSIZE, &temp);
1538c2ecf20Sopenharmony_ci	pci_write_config_dword(agp_bridge->dev, NVIDIA_0_APSIZE, temp | 0x100);
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	/* map aperture */
1568c2ecf20Sopenharmony_ci	apbase_phys = pci_resource_start(agp_bridge->dev, AGP_APERTURE_BAR);
1578c2ecf20Sopenharmony_ci	nvidia_private.aperture =
1588c2ecf20Sopenharmony_ci		(volatile u32 __iomem *) ioremap(apbase_phys, 33 * PAGE_SIZE);
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	if (!nvidia_private.aperture)
1618c2ecf20Sopenharmony_ci		return -ENOMEM;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	return 0;
1648c2ecf20Sopenharmony_ci}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_cistatic void nvidia_cleanup(void)
1678c2ecf20Sopenharmony_ci{
1688c2ecf20Sopenharmony_ci	struct aper_size_info_8 *previous_size;
1698c2ecf20Sopenharmony_ci	u32 temp;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	/* gart control */
1728c2ecf20Sopenharmony_ci	pci_read_config_dword(agp_bridge->dev, NVIDIA_0_APSIZE, &temp);
1738c2ecf20Sopenharmony_ci	pci_write_config_dword(agp_bridge->dev, NVIDIA_0_APSIZE, temp & ~(0x100));
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	/* gtlb control */
1768c2ecf20Sopenharmony_ci	pci_read_config_dword(nvidia_private.dev_2, NVIDIA_2_GARTCTRL, &temp);
1778c2ecf20Sopenharmony_ci	pci_write_config_dword(nvidia_private.dev_2, NVIDIA_2_GARTCTRL, temp & ~(0x11));
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	/* unmap aperture */
1808c2ecf20Sopenharmony_ci	iounmap((void __iomem *) nvidia_private.aperture);
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	/* restore previous aperture size */
1838c2ecf20Sopenharmony_ci	previous_size = A_SIZE_8(agp_bridge->previous_size);
1848c2ecf20Sopenharmony_ci	pci_write_config_byte(agp_bridge->dev, NVIDIA_0_APSIZE,
1858c2ecf20Sopenharmony_ci		previous_size->size_value);
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	/* restore iorr for previous aperture size */
1888c2ecf20Sopenharmony_ci	nvidia_init_iorr(agp_bridge->gart_bus_addr,
1898c2ecf20Sopenharmony_ci		previous_size->size * 1024 * 1024);
1908c2ecf20Sopenharmony_ci}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci/*
1948c2ecf20Sopenharmony_ci * Note we can't use the generic routines, even though they are 99% the same.
1958c2ecf20Sopenharmony_ci * Aperture sizes <64M still requires a full 64k GART directory, but
1968c2ecf20Sopenharmony_ci * only use the portion of the TLB entries that correspond to the apertures
1978c2ecf20Sopenharmony_ci * alignment inside the surrounding 64M block.
1988c2ecf20Sopenharmony_ci */
1998c2ecf20Sopenharmony_ciextern int agp_memory_reserved;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_cistatic int nvidia_insert_memory(struct agp_memory *mem, off_t pg_start, int type)
2028c2ecf20Sopenharmony_ci{
2038c2ecf20Sopenharmony_ci	int i, j;
2048c2ecf20Sopenharmony_ci	int mask_type;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	mask_type = agp_generic_type_to_mask_type(mem->bridge, type);
2078c2ecf20Sopenharmony_ci	if (mask_type != 0 || type != mem->type)
2088c2ecf20Sopenharmony_ci		return -EINVAL;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	if (mem->page_count == 0)
2118c2ecf20Sopenharmony_ci		return 0;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	if ((pg_start + mem->page_count) >
2148c2ecf20Sopenharmony_ci		(nvidia_private.num_active_entries - agp_memory_reserved/PAGE_SIZE))
2158c2ecf20Sopenharmony_ci		return -EINVAL;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	for (j = pg_start; j < (pg_start + mem->page_count); j++) {
2188c2ecf20Sopenharmony_ci		if (!PGE_EMPTY(agp_bridge, readl(agp_bridge->gatt_table+nvidia_private.pg_offset+j)))
2198c2ecf20Sopenharmony_ci			return -EBUSY;
2208c2ecf20Sopenharmony_ci	}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	if (!mem->is_flushed) {
2238c2ecf20Sopenharmony_ci		global_cache_flush();
2248c2ecf20Sopenharmony_ci		mem->is_flushed = true;
2258c2ecf20Sopenharmony_ci	}
2268c2ecf20Sopenharmony_ci	for (i = 0, j = pg_start; i < mem->page_count; i++, j++) {
2278c2ecf20Sopenharmony_ci		writel(agp_bridge->driver->mask_memory(agp_bridge,
2288c2ecf20Sopenharmony_ci			       page_to_phys(mem->pages[i]), mask_type),
2298c2ecf20Sopenharmony_ci			agp_bridge->gatt_table+nvidia_private.pg_offset+j);
2308c2ecf20Sopenharmony_ci	}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	/* PCI Posting. */
2338c2ecf20Sopenharmony_ci	readl(agp_bridge->gatt_table+nvidia_private.pg_offset+j - 1);
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	agp_bridge->driver->tlb_flush(mem);
2368c2ecf20Sopenharmony_ci	return 0;
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_cistatic int nvidia_remove_memory(struct agp_memory *mem, off_t pg_start, int type)
2418c2ecf20Sopenharmony_ci{
2428c2ecf20Sopenharmony_ci	int i;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	int mask_type;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	mask_type = agp_generic_type_to_mask_type(mem->bridge, type);
2478c2ecf20Sopenharmony_ci	if (mask_type != 0 || type != mem->type)
2488c2ecf20Sopenharmony_ci		return -EINVAL;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	if (mem->page_count == 0)
2518c2ecf20Sopenharmony_ci		return 0;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	for (i = pg_start; i < (mem->page_count + pg_start); i++)
2548c2ecf20Sopenharmony_ci		writel(agp_bridge->scratch_page, agp_bridge->gatt_table+nvidia_private.pg_offset+i);
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	agp_bridge->driver->tlb_flush(mem);
2578c2ecf20Sopenharmony_ci	return 0;
2588c2ecf20Sopenharmony_ci}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_cistatic void nvidia_tlbflush(struct agp_memory *mem)
2628c2ecf20Sopenharmony_ci{
2638c2ecf20Sopenharmony_ci	unsigned long end;
2648c2ecf20Sopenharmony_ci	u32 wbc_reg, temp;
2658c2ecf20Sopenharmony_ci	int i;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	/* flush chipset */
2688c2ecf20Sopenharmony_ci	if (nvidia_private.wbc_mask) {
2698c2ecf20Sopenharmony_ci		pci_read_config_dword(nvidia_private.dev_1, NVIDIA_1_WBC, &wbc_reg);
2708c2ecf20Sopenharmony_ci		wbc_reg |= nvidia_private.wbc_mask;
2718c2ecf20Sopenharmony_ci		pci_write_config_dword(nvidia_private.dev_1, NVIDIA_1_WBC, wbc_reg);
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci		end = jiffies + 3*HZ;
2748c2ecf20Sopenharmony_ci		do {
2758c2ecf20Sopenharmony_ci			pci_read_config_dword(nvidia_private.dev_1,
2768c2ecf20Sopenharmony_ci					NVIDIA_1_WBC, &wbc_reg);
2778c2ecf20Sopenharmony_ci			if (time_before_eq(end, jiffies)) {
2788c2ecf20Sopenharmony_ci				printk(KERN_ERR PFX
2798c2ecf20Sopenharmony_ci				    "TLB flush took more than 3 seconds.\n");
2808c2ecf20Sopenharmony_ci			}
2818c2ecf20Sopenharmony_ci		} while (wbc_reg & nvidia_private.wbc_mask);
2828c2ecf20Sopenharmony_ci	}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	/* flush TLB entries */
2858c2ecf20Sopenharmony_ci	for (i = 0; i < 32 + 1; i++)
2868c2ecf20Sopenharmony_ci		temp = readl(nvidia_private.aperture+(i * PAGE_SIZE / sizeof(u32)));
2878c2ecf20Sopenharmony_ci	for (i = 0; i < 32 + 1; i++)
2888c2ecf20Sopenharmony_ci		temp = readl(nvidia_private.aperture+(i * PAGE_SIZE / sizeof(u32)));
2898c2ecf20Sopenharmony_ci}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_cistatic const struct aper_size_info_8 nvidia_generic_sizes[5] =
2938c2ecf20Sopenharmony_ci{
2948c2ecf20Sopenharmony_ci	{512, 131072, 7, 0},
2958c2ecf20Sopenharmony_ci	{256, 65536, 6, 8},
2968c2ecf20Sopenharmony_ci	{128, 32768, 5, 12},
2978c2ecf20Sopenharmony_ci	{64, 16384, 4, 14},
2988c2ecf20Sopenharmony_ci	/* The 32M mode still requires a 64k gatt */
2998c2ecf20Sopenharmony_ci	{32, 16384, 4, 15}
3008c2ecf20Sopenharmony_ci};
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_cistatic const struct gatt_mask nvidia_generic_masks[] =
3048c2ecf20Sopenharmony_ci{
3058c2ecf20Sopenharmony_ci	{ .mask = 1, .type = 0}
3068c2ecf20Sopenharmony_ci};
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_cistatic const struct agp_bridge_driver nvidia_driver = {
3108c2ecf20Sopenharmony_ci	.owner			= THIS_MODULE,
3118c2ecf20Sopenharmony_ci	.aperture_sizes		= nvidia_generic_sizes,
3128c2ecf20Sopenharmony_ci	.size_type		= U8_APER_SIZE,
3138c2ecf20Sopenharmony_ci	.num_aperture_sizes	= 5,
3148c2ecf20Sopenharmony_ci	.needs_scratch_page	= true,
3158c2ecf20Sopenharmony_ci	.configure		= nvidia_configure,
3168c2ecf20Sopenharmony_ci	.fetch_size		= nvidia_fetch_size,
3178c2ecf20Sopenharmony_ci	.cleanup		= nvidia_cleanup,
3188c2ecf20Sopenharmony_ci	.tlb_flush		= nvidia_tlbflush,
3198c2ecf20Sopenharmony_ci	.mask_memory		= agp_generic_mask_memory,
3208c2ecf20Sopenharmony_ci	.masks			= nvidia_generic_masks,
3218c2ecf20Sopenharmony_ci	.agp_enable		= agp_generic_enable,
3228c2ecf20Sopenharmony_ci	.cache_flush		= global_cache_flush,
3238c2ecf20Sopenharmony_ci	.create_gatt_table	= agp_generic_create_gatt_table,
3248c2ecf20Sopenharmony_ci	.free_gatt_table	= agp_generic_free_gatt_table,
3258c2ecf20Sopenharmony_ci	.insert_memory		= nvidia_insert_memory,
3268c2ecf20Sopenharmony_ci	.remove_memory		= nvidia_remove_memory,
3278c2ecf20Sopenharmony_ci	.alloc_by_type		= agp_generic_alloc_by_type,
3288c2ecf20Sopenharmony_ci	.free_by_type		= agp_generic_free_by_type,
3298c2ecf20Sopenharmony_ci	.agp_alloc_page		= agp_generic_alloc_page,
3308c2ecf20Sopenharmony_ci	.agp_alloc_pages	= agp_generic_alloc_pages,
3318c2ecf20Sopenharmony_ci	.agp_destroy_page	= agp_generic_destroy_page,
3328c2ecf20Sopenharmony_ci	.agp_destroy_pages	= agp_generic_destroy_pages,
3338c2ecf20Sopenharmony_ci	.agp_type_to_mask_type  = agp_generic_type_to_mask_type,
3348c2ecf20Sopenharmony_ci};
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_cistatic int agp_nvidia_probe(struct pci_dev *pdev,
3378c2ecf20Sopenharmony_ci			    const struct pci_device_id *ent)
3388c2ecf20Sopenharmony_ci{
3398c2ecf20Sopenharmony_ci	struct agp_bridge_data *bridge;
3408c2ecf20Sopenharmony_ci	u8 cap_ptr;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	nvidia_private.dev_1 =
3438c2ecf20Sopenharmony_ci		pci_get_domain_bus_and_slot(pci_domain_nr(pdev->bus),
3448c2ecf20Sopenharmony_ci					    (unsigned int)pdev->bus->number,
3458c2ecf20Sopenharmony_ci					    PCI_DEVFN(0, 1));
3468c2ecf20Sopenharmony_ci	nvidia_private.dev_2 =
3478c2ecf20Sopenharmony_ci		pci_get_domain_bus_and_slot(pci_domain_nr(pdev->bus),
3488c2ecf20Sopenharmony_ci					    (unsigned int)pdev->bus->number,
3498c2ecf20Sopenharmony_ci					    PCI_DEVFN(0, 2));
3508c2ecf20Sopenharmony_ci	nvidia_private.dev_3 =
3518c2ecf20Sopenharmony_ci		pci_get_domain_bus_and_slot(pci_domain_nr(pdev->bus),
3528c2ecf20Sopenharmony_ci					    (unsigned int)pdev->bus->number,
3538c2ecf20Sopenharmony_ci					    PCI_DEVFN(30, 0));
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	if (!nvidia_private.dev_1 || !nvidia_private.dev_2 || !nvidia_private.dev_3) {
3568c2ecf20Sopenharmony_ci		printk(KERN_INFO PFX "Detected an NVIDIA nForce/nForce2 "
3578c2ecf20Sopenharmony_ci			"chipset, but could not find the secondary devices.\n");
3588c2ecf20Sopenharmony_ci		return -ENODEV;
3598c2ecf20Sopenharmony_ci	}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP);
3628c2ecf20Sopenharmony_ci	if (!cap_ptr)
3638c2ecf20Sopenharmony_ci		return -ENODEV;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	switch (pdev->device) {
3668c2ecf20Sopenharmony_ci	case PCI_DEVICE_ID_NVIDIA_NFORCE:
3678c2ecf20Sopenharmony_ci		printk(KERN_INFO PFX "Detected NVIDIA nForce chipset\n");
3688c2ecf20Sopenharmony_ci		nvidia_private.wbc_mask = 0x00010000;
3698c2ecf20Sopenharmony_ci		break;
3708c2ecf20Sopenharmony_ci	case PCI_DEVICE_ID_NVIDIA_NFORCE2:
3718c2ecf20Sopenharmony_ci		printk(KERN_INFO PFX "Detected NVIDIA nForce2 chipset\n");
3728c2ecf20Sopenharmony_ci		nvidia_private.wbc_mask = 0x80000000;
3738c2ecf20Sopenharmony_ci		break;
3748c2ecf20Sopenharmony_ci	default:
3758c2ecf20Sopenharmony_ci		printk(KERN_ERR PFX "Unsupported NVIDIA chipset (device id: %04x)\n",
3768c2ecf20Sopenharmony_ci			    pdev->device);
3778c2ecf20Sopenharmony_ci		return -ENODEV;
3788c2ecf20Sopenharmony_ci	}
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	bridge = agp_alloc_bridge();
3818c2ecf20Sopenharmony_ci	if (!bridge)
3828c2ecf20Sopenharmony_ci		return -ENOMEM;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	bridge->driver = &nvidia_driver;
3858c2ecf20Sopenharmony_ci	bridge->dev_private_data = &nvidia_private;
3868c2ecf20Sopenharmony_ci	bridge->dev = pdev;
3878c2ecf20Sopenharmony_ci	bridge->capndx = cap_ptr;
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	/* Fill in the mode register */
3908c2ecf20Sopenharmony_ci	pci_read_config_dword(pdev,
3918c2ecf20Sopenharmony_ci			bridge->capndx+PCI_AGP_STATUS,
3928c2ecf20Sopenharmony_ci			&bridge->mode);
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	pci_set_drvdata(pdev, bridge);
3958c2ecf20Sopenharmony_ci	return agp_add_bridge(bridge);
3968c2ecf20Sopenharmony_ci}
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_cistatic void agp_nvidia_remove(struct pci_dev *pdev)
3998c2ecf20Sopenharmony_ci{
4008c2ecf20Sopenharmony_ci	struct agp_bridge_data *bridge = pci_get_drvdata(pdev);
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	agp_remove_bridge(bridge);
4038c2ecf20Sopenharmony_ci	agp_put_bridge(bridge);
4048c2ecf20Sopenharmony_ci}
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
4078c2ecf20Sopenharmony_cistatic int agp_nvidia_suspend(struct pci_dev *pdev, pm_message_t state)
4088c2ecf20Sopenharmony_ci{
4098c2ecf20Sopenharmony_ci	pci_save_state(pdev);
4108c2ecf20Sopenharmony_ci	pci_set_power_state(pdev, PCI_D3hot);
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	return 0;
4138c2ecf20Sopenharmony_ci}
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_cistatic int agp_nvidia_resume(struct pci_dev *pdev)
4168c2ecf20Sopenharmony_ci{
4178c2ecf20Sopenharmony_ci	/* set power state 0 and restore PCI space */
4188c2ecf20Sopenharmony_ci	pci_set_power_state(pdev, PCI_D0);
4198c2ecf20Sopenharmony_ci	pci_restore_state(pdev);
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	/* reconfigure AGP hardware again */
4228c2ecf20Sopenharmony_ci	nvidia_configure();
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	return 0;
4258c2ecf20Sopenharmony_ci}
4268c2ecf20Sopenharmony_ci#endif
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_cistatic const struct pci_device_id agp_nvidia_pci_table[] = {
4308c2ecf20Sopenharmony_ci	{
4318c2ecf20Sopenharmony_ci	.class		= (PCI_CLASS_BRIDGE_HOST << 8),
4328c2ecf20Sopenharmony_ci	.class_mask	= ~0,
4338c2ecf20Sopenharmony_ci	.vendor		= PCI_VENDOR_ID_NVIDIA,
4348c2ecf20Sopenharmony_ci	.device		= PCI_DEVICE_ID_NVIDIA_NFORCE,
4358c2ecf20Sopenharmony_ci	.subvendor	= PCI_ANY_ID,
4368c2ecf20Sopenharmony_ci	.subdevice	= PCI_ANY_ID,
4378c2ecf20Sopenharmony_ci	},
4388c2ecf20Sopenharmony_ci	{
4398c2ecf20Sopenharmony_ci	.class		= (PCI_CLASS_BRIDGE_HOST << 8),
4408c2ecf20Sopenharmony_ci	.class_mask	= ~0,
4418c2ecf20Sopenharmony_ci	.vendor		= PCI_VENDOR_ID_NVIDIA,
4428c2ecf20Sopenharmony_ci	.device		= PCI_DEVICE_ID_NVIDIA_NFORCE2,
4438c2ecf20Sopenharmony_ci	.subvendor	= PCI_ANY_ID,
4448c2ecf20Sopenharmony_ci	.subdevice	= PCI_ANY_ID,
4458c2ecf20Sopenharmony_ci	},
4468c2ecf20Sopenharmony_ci	{ }
4478c2ecf20Sopenharmony_ci};
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, agp_nvidia_pci_table);
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_cistatic struct pci_driver agp_nvidia_pci_driver = {
4528c2ecf20Sopenharmony_ci	.name		= "agpgart-nvidia",
4538c2ecf20Sopenharmony_ci	.id_table	= agp_nvidia_pci_table,
4548c2ecf20Sopenharmony_ci	.probe		= agp_nvidia_probe,
4558c2ecf20Sopenharmony_ci	.remove		= agp_nvidia_remove,
4568c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
4578c2ecf20Sopenharmony_ci	.suspend	= agp_nvidia_suspend,
4588c2ecf20Sopenharmony_ci	.resume		= agp_nvidia_resume,
4598c2ecf20Sopenharmony_ci#endif
4608c2ecf20Sopenharmony_ci};
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_cistatic int __init agp_nvidia_init(void)
4638c2ecf20Sopenharmony_ci{
4648c2ecf20Sopenharmony_ci	if (agp_off)
4658c2ecf20Sopenharmony_ci		return -EINVAL;
4668c2ecf20Sopenharmony_ci	return pci_register_driver(&agp_nvidia_pci_driver);
4678c2ecf20Sopenharmony_ci}
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_cistatic void __exit agp_nvidia_cleanup(void)
4708c2ecf20Sopenharmony_ci{
4718c2ecf20Sopenharmony_ci	pci_unregister_driver(&agp_nvidia_pci_driver);
4728c2ecf20Sopenharmony_ci	pci_dev_put(nvidia_private.dev_1);
4738c2ecf20Sopenharmony_ci	pci_dev_put(nvidia_private.dev_2);
4748c2ecf20Sopenharmony_ci	pci_dev_put(nvidia_private.dev_3);
4758c2ecf20Sopenharmony_ci}
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_cimodule_init(agp_nvidia_init);
4788c2ecf20Sopenharmony_cimodule_exit(agp_nvidia_cleanup);
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL and additional rights");
4818c2ecf20Sopenharmony_ciMODULE_AUTHOR("NVIDIA Corporation");
4828c2ecf20Sopenharmony_ci
483