18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Transmeta's Efficeon AGPGART driver.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Based upon a diff by Linus around November '02.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Ported to the 2.6 kernel by Carlos Puchol <cpglinux@puchol.com>
78c2ecf20Sopenharmony_ci * and H. Peter Anvin <hpa@transmeta.com>.
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci/*
118c2ecf20Sopenharmony_ci * NOTE-cpg-040217:
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci *   - when compiled as a module, after loading the module,
148c2ecf20Sopenharmony_ci *     it will refuse to unload, indicating it is in use,
158c2ecf20Sopenharmony_ci *     when it is not.
168c2ecf20Sopenharmony_ci *   - no s3 (suspend to ram) testing.
178c2ecf20Sopenharmony_ci *   - tested on the efficeon integrated nothbridge for tens
188c2ecf20Sopenharmony_ci *     of iterations of starting x and glxgears.
198c2ecf20Sopenharmony_ci *   - tested with radeon 9000 and radeon mobility m9 cards
208c2ecf20Sopenharmony_ci *   - tested with c3/c4 enabled (with the mobility m9 card)
218c2ecf20Sopenharmony_ci */
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include <linux/module.h>
248c2ecf20Sopenharmony_ci#include <linux/pci.h>
258c2ecf20Sopenharmony_ci#include <linux/init.h>
268c2ecf20Sopenharmony_ci#include <linux/agp_backend.h>
278c2ecf20Sopenharmony_ci#include <linux/gfp.h>
288c2ecf20Sopenharmony_ci#include <linux/page-flags.h>
298c2ecf20Sopenharmony_ci#include <linux/mm.h>
308c2ecf20Sopenharmony_ci#include "agp.h"
318c2ecf20Sopenharmony_ci#include "intel-agp.h"
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci/*
348c2ecf20Sopenharmony_ci * The real differences to the generic AGP code is
358c2ecf20Sopenharmony_ci * in the GART mappings - a two-level setup with the
368c2ecf20Sopenharmony_ci * first level being an on-chip 64-entry table.
378c2ecf20Sopenharmony_ci *
388c2ecf20Sopenharmony_ci * The page array is filled through the ATTPAGE register
398c2ecf20Sopenharmony_ci * (Aperture Translation Table Page Register) at 0xB8. Bits:
408c2ecf20Sopenharmony_ci *  31:20: physical page address
418c2ecf20Sopenharmony_ci *   11:9: Page Attribute Table Index (PATI)
428c2ecf20Sopenharmony_ci *	   must match the PAT index for the
438c2ecf20Sopenharmony_ci *	   mapped pages (the 2nd level page table pages
448c2ecf20Sopenharmony_ci *	   themselves should be just regular WB-cacheable,
458c2ecf20Sopenharmony_ci *	   so this is normally zero.)
468c2ecf20Sopenharmony_ci *      8: Present
478c2ecf20Sopenharmony_ci *    7:6: reserved, write as zero
488c2ecf20Sopenharmony_ci *    5:0: GATT directory index: which 1st-level entry
498c2ecf20Sopenharmony_ci *
508c2ecf20Sopenharmony_ci * The Efficeon AGP spec requires pages to be WB-cacheable
518c2ecf20Sopenharmony_ci * but to be explicitly CLFLUSH'd after any changes.
528c2ecf20Sopenharmony_ci */
538c2ecf20Sopenharmony_ci#define EFFICEON_ATTPAGE	0xb8
548c2ecf20Sopenharmony_ci#define EFFICEON_L1_SIZE	64	/* Number of PDE pages */
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci#define EFFICEON_PATI		(0 << 9)
578c2ecf20Sopenharmony_ci#define EFFICEON_PRESENT	(1 << 8)
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic struct _efficeon_private {
608c2ecf20Sopenharmony_ci	unsigned long l1_table[EFFICEON_L1_SIZE];
618c2ecf20Sopenharmony_ci} efficeon_private;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cistatic const struct gatt_mask efficeon_generic_masks[] =
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	{.mask = 0x00000001, .type = 0}
668c2ecf20Sopenharmony_ci};
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci/* This function does the same thing as mask_memory() for this chipset... */
698c2ecf20Sopenharmony_cistatic inline unsigned long efficeon_mask_memory(struct page *page)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	unsigned long addr = page_to_phys(page);
728c2ecf20Sopenharmony_ci	return addr | 0x00000001;
738c2ecf20Sopenharmony_ci}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistatic const struct aper_size_info_lvl2 efficeon_generic_sizes[4] =
768c2ecf20Sopenharmony_ci{
778c2ecf20Sopenharmony_ci	{256, 65536, 0},
788c2ecf20Sopenharmony_ci	{128, 32768, 32},
798c2ecf20Sopenharmony_ci	{64, 16384, 48},
808c2ecf20Sopenharmony_ci	{32, 8192, 56}
818c2ecf20Sopenharmony_ci};
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci/*
848c2ecf20Sopenharmony_ci * Control interfaces are largely identical to
858c2ecf20Sopenharmony_ci * the legacy Intel 440BX..
868c2ecf20Sopenharmony_ci */
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic int efficeon_fetch_size(void)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	int i;
918c2ecf20Sopenharmony_ci	u16 temp;
928c2ecf20Sopenharmony_ci	struct aper_size_info_lvl2 *values;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	pci_read_config_word(agp_bridge->dev, INTEL_APSIZE, &temp);
958c2ecf20Sopenharmony_ci	values = A_SIZE_LVL2(agp_bridge->driver->aperture_sizes);
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++) {
988c2ecf20Sopenharmony_ci		if (temp == values[i].size_value) {
998c2ecf20Sopenharmony_ci			agp_bridge->previous_size =
1008c2ecf20Sopenharmony_ci			    agp_bridge->current_size = (void *) (values + i);
1018c2ecf20Sopenharmony_ci			agp_bridge->aperture_size_idx = i;
1028c2ecf20Sopenharmony_ci			return values[i].size;
1038c2ecf20Sopenharmony_ci		}
1048c2ecf20Sopenharmony_ci	}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	return 0;
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistatic void efficeon_tlbflush(struct agp_memory * mem)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	printk(KERN_DEBUG PFX "efficeon_tlbflush()\n");
1128c2ecf20Sopenharmony_ci	pci_write_config_dword(agp_bridge->dev, INTEL_AGPCTRL, 0x2200);
1138c2ecf20Sopenharmony_ci	pci_write_config_dword(agp_bridge->dev, INTEL_AGPCTRL, 0x2280);
1148c2ecf20Sopenharmony_ci}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistatic void efficeon_cleanup(void)
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci	u16 temp;
1198c2ecf20Sopenharmony_ci	struct aper_size_info_lvl2 *previous_size;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	printk(KERN_DEBUG PFX "efficeon_cleanup()\n");
1228c2ecf20Sopenharmony_ci	previous_size = A_SIZE_LVL2(agp_bridge->previous_size);
1238c2ecf20Sopenharmony_ci	pci_read_config_word(agp_bridge->dev, INTEL_NBXCFG, &temp);
1248c2ecf20Sopenharmony_ci	pci_write_config_word(agp_bridge->dev, INTEL_NBXCFG, temp & ~(1 << 9));
1258c2ecf20Sopenharmony_ci	pci_write_config_word(agp_bridge->dev, INTEL_APSIZE,
1268c2ecf20Sopenharmony_ci			      previous_size->size_value);
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_cistatic int efficeon_configure(void)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	u16 temp2;
1328c2ecf20Sopenharmony_ci	struct aper_size_info_lvl2 *current_size;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	printk(KERN_DEBUG PFX "efficeon_configure()\n");
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	current_size = A_SIZE_LVL2(agp_bridge->current_size);
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	/* aperture size */
1398c2ecf20Sopenharmony_ci	pci_write_config_word(agp_bridge->dev, INTEL_APSIZE,
1408c2ecf20Sopenharmony_ci			      current_size->size_value);
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	/* address to map to */
1438c2ecf20Sopenharmony_ci	agp_bridge->gart_bus_addr = pci_bus_address(agp_bridge->dev,
1448c2ecf20Sopenharmony_ci						    AGP_APERTURE_BAR);
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	/* agpctrl */
1478c2ecf20Sopenharmony_ci	pci_write_config_dword(agp_bridge->dev, INTEL_AGPCTRL, 0x2280);
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	/* paccfg/nbxcfg */
1508c2ecf20Sopenharmony_ci	pci_read_config_word(agp_bridge->dev, INTEL_NBXCFG, &temp2);
1518c2ecf20Sopenharmony_ci	pci_write_config_word(agp_bridge->dev, INTEL_NBXCFG,
1528c2ecf20Sopenharmony_ci			      (temp2 & ~(1 << 10)) | (1 << 9) | (1 << 11));
1538c2ecf20Sopenharmony_ci	/* clear any possible error conditions */
1548c2ecf20Sopenharmony_ci	pci_write_config_byte(agp_bridge->dev, INTEL_ERRSTS + 1, 7);
1558c2ecf20Sopenharmony_ci	return 0;
1568c2ecf20Sopenharmony_ci}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_cistatic int efficeon_free_gatt_table(struct agp_bridge_data *bridge)
1598c2ecf20Sopenharmony_ci{
1608c2ecf20Sopenharmony_ci	int index, freed = 0;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	for (index = 0; index < EFFICEON_L1_SIZE; index++) {
1638c2ecf20Sopenharmony_ci		unsigned long page = efficeon_private.l1_table[index];
1648c2ecf20Sopenharmony_ci		if (page) {
1658c2ecf20Sopenharmony_ci			efficeon_private.l1_table[index] = 0;
1668c2ecf20Sopenharmony_ci			free_page(page);
1678c2ecf20Sopenharmony_ci			freed++;
1688c2ecf20Sopenharmony_ci		}
1698c2ecf20Sopenharmony_ci		printk(KERN_DEBUG PFX "efficeon_free_gatt_table(%p, %02x, %08x)\n",
1708c2ecf20Sopenharmony_ci			agp_bridge->dev, EFFICEON_ATTPAGE, index);
1718c2ecf20Sopenharmony_ci		pci_write_config_dword(agp_bridge->dev,
1728c2ecf20Sopenharmony_ci			EFFICEON_ATTPAGE, index);
1738c2ecf20Sopenharmony_ci	}
1748c2ecf20Sopenharmony_ci	printk(KERN_DEBUG PFX "efficeon_free_gatt_table() freed %d pages\n", freed);
1758c2ecf20Sopenharmony_ci	return 0;
1768c2ecf20Sopenharmony_ci}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci/*
1808c2ecf20Sopenharmony_ci * Since we don't need contiguous memory we just try
1818c2ecf20Sopenharmony_ci * to get the gatt table once
1828c2ecf20Sopenharmony_ci */
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci#define GET_PAGE_DIR_OFF(addr) (addr >> 22)
1858c2ecf20Sopenharmony_ci#define GET_PAGE_DIR_IDX(addr) (GET_PAGE_DIR_OFF(addr) - \
1868c2ecf20Sopenharmony_ci	GET_PAGE_DIR_OFF(agp_bridge->gart_bus_addr))
1878c2ecf20Sopenharmony_ci#define GET_GATT_OFF(addr) ((addr & 0x003ff000) >> 12)
1888c2ecf20Sopenharmony_ci#undef  GET_GATT
1898c2ecf20Sopenharmony_ci#define GET_GATT(addr) (efficeon_private.gatt_pages[\
1908c2ecf20Sopenharmony_ci	GET_PAGE_DIR_IDX(addr)]->remapped)
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_cistatic int efficeon_create_gatt_table(struct agp_bridge_data *bridge)
1938c2ecf20Sopenharmony_ci{
1948c2ecf20Sopenharmony_ci	int index;
1958c2ecf20Sopenharmony_ci	const int pati    = EFFICEON_PATI;
1968c2ecf20Sopenharmony_ci	const int present = EFFICEON_PRESENT;
1978c2ecf20Sopenharmony_ci	const int clflush_chunk = ((cpuid_ebx(1) >> 8) & 0xff) << 3;
1988c2ecf20Sopenharmony_ci	int num_entries, l1_pages;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	num_entries = A_SIZE_LVL2(agp_bridge->current_size)->num_entries;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	printk(KERN_DEBUG PFX "efficeon_create_gatt_table(%d)\n", num_entries);
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	/* There are 2^10 PTE pages per PDE page */
2058c2ecf20Sopenharmony_ci	BUG_ON(num_entries & 0x3ff);
2068c2ecf20Sopenharmony_ci	l1_pages = num_entries >> 10;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	for (index = 0 ; index < l1_pages ; index++) {
2098c2ecf20Sopenharmony_ci		int offset;
2108c2ecf20Sopenharmony_ci		unsigned long page;
2118c2ecf20Sopenharmony_ci		unsigned long value;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci		page = efficeon_private.l1_table[index];
2148c2ecf20Sopenharmony_ci		BUG_ON(page);
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci		page = get_zeroed_page(GFP_KERNEL);
2178c2ecf20Sopenharmony_ci		if (!page) {
2188c2ecf20Sopenharmony_ci			efficeon_free_gatt_table(agp_bridge);
2198c2ecf20Sopenharmony_ci			return -ENOMEM;
2208c2ecf20Sopenharmony_ci		}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci		for (offset = 0; offset < PAGE_SIZE; offset += clflush_chunk)
2238c2ecf20Sopenharmony_ci			clflush((char *)page+offset);
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci		efficeon_private.l1_table[index] = page;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci		value = virt_to_phys((unsigned long *)page) | pati | present | index;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci		pci_write_config_dword(agp_bridge->dev,
2308c2ecf20Sopenharmony_ci			EFFICEON_ATTPAGE, value);
2318c2ecf20Sopenharmony_ci	}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	return 0;
2348c2ecf20Sopenharmony_ci}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_cistatic int efficeon_insert_memory(struct agp_memory * mem, off_t pg_start, int type)
2378c2ecf20Sopenharmony_ci{
2388c2ecf20Sopenharmony_ci	int i, count = mem->page_count, num_entries;
2398c2ecf20Sopenharmony_ci	unsigned int *page, *last_page;
2408c2ecf20Sopenharmony_ci	const int clflush_chunk = ((cpuid_ebx(1) >> 8) & 0xff) << 3;
2418c2ecf20Sopenharmony_ci	const unsigned long clflush_mask = ~(clflush_chunk-1);
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	printk(KERN_DEBUG PFX "efficeon_insert_memory(%lx, %d)\n", pg_start, count);
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	num_entries = A_SIZE_LVL2(agp_bridge->current_size)->num_entries;
2468c2ecf20Sopenharmony_ci	if ((pg_start + mem->page_count) > num_entries)
2478c2ecf20Sopenharmony_ci		return -EINVAL;
2488c2ecf20Sopenharmony_ci	if (type != 0 || mem->type != 0)
2498c2ecf20Sopenharmony_ci		return -EINVAL;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	if (!mem->is_flushed) {
2528c2ecf20Sopenharmony_ci		global_cache_flush();
2538c2ecf20Sopenharmony_ci		mem->is_flushed = true;
2548c2ecf20Sopenharmony_ci	}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	last_page = NULL;
2578c2ecf20Sopenharmony_ci	for (i = 0; i < count; i++) {
2588c2ecf20Sopenharmony_ci		int index = pg_start + i;
2598c2ecf20Sopenharmony_ci		unsigned long insert = efficeon_mask_memory(mem->pages[i]);
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci		page = (unsigned int *) efficeon_private.l1_table[index >> 10];
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci		if (!page)
2648c2ecf20Sopenharmony_ci			continue;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci		page += (index & 0x3ff);
2678c2ecf20Sopenharmony_ci		*page = insert;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci		/* clflush is slow, so don't clflush until we have to */
2708c2ecf20Sopenharmony_ci		if (last_page &&
2718c2ecf20Sopenharmony_ci		    (((unsigned long)page^(unsigned long)last_page) &
2728c2ecf20Sopenharmony_ci		     clflush_mask))
2738c2ecf20Sopenharmony_ci			clflush(last_page);
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci		last_page = page;
2768c2ecf20Sopenharmony_ci	}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	if ( last_page )
2798c2ecf20Sopenharmony_ci		clflush(last_page);
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	agp_bridge->driver->tlb_flush(mem);
2828c2ecf20Sopenharmony_ci	return 0;
2838c2ecf20Sopenharmony_ci}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_cistatic int efficeon_remove_memory(struct agp_memory * mem, off_t pg_start, int type)
2868c2ecf20Sopenharmony_ci{
2878c2ecf20Sopenharmony_ci	int i, count = mem->page_count, num_entries;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	printk(KERN_DEBUG PFX "efficeon_remove_memory(%lx, %d)\n", pg_start, count);
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	num_entries = A_SIZE_LVL2(agp_bridge->current_size)->num_entries;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	if ((pg_start + mem->page_count) > num_entries)
2948c2ecf20Sopenharmony_ci		return -EINVAL;
2958c2ecf20Sopenharmony_ci	if (type != 0 || mem->type != 0)
2968c2ecf20Sopenharmony_ci		return -EINVAL;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	for (i = 0; i < count; i++) {
2998c2ecf20Sopenharmony_ci		int index = pg_start + i;
3008c2ecf20Sopenharmony_ci		unsigned int *page = (unsigned int *) efficeon_private.l1_table[index >> 10];
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci		if (!page)
3038c2ecf20Sopenharmony_ci			continue;
3048c2ecf20Sopenharmony_ci		page += (index & 0x3ff);
3058c2ecf20Sopenharmony_ci		*page = 0;
3068c2ecf20Sopenharmony_ci	}
3078c2ecf20Sopenharmony_ci	agp_bridge->driver->tlb_flush(mem);
3088c2ecf20Sopenharmony_ci	return 0;
3098c2ecf20Sopenharmony_ci}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_cistatic const struct agp_bridge_driver efficeon_driver = {
3138c2ecf20Sopenharmony_ci	.owner			= THIS_MODULE,
3148c2ecf20Sopenharmony_ci	.aperture_sizes		= efficeon_generic_sizes,
3158c2ecf20Sopenharmony_ci	.size_type		= LVL2_APER_SIZE,
3168c2ecf20Sopenharmony_ci	.num_aperture_sizes	= 4,
3178c2ecf20Sopenharmony_ci	.configure		= efficeon_configure,
3188c2ecf20Sopenharmony_ci	.fetch_size		= efficeon_fetch_size,
3198c2ecf20Sopenharmony_ci	.cleanup		= efficeon_cleanup,
3208c2ecf20Sopenharmony_ci	.tlb_flush		= efficeon_tlbflush,
3218c2ecf20Sopenharmony_ci	.mask_memory		= agp_generic_mask_memory,
3228c2ecf20Sopenharmony_ci	.masks			= efficeon_generic_masks,
3238c2ecf20Sopenharmony_ci	.agp_enable		= agp_generic_enable,
3248c2ecf20Sopenharmony_ci	.cache_flush		= global_cache_flush,
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	// Efficeon-specific GATT table setup / populate / teardown
3278c2ecf20Sopenharmony_ci	.create_gatt_table	= efficeon_create_gatt_table,
3288c2ecf20Sopenharmony_ci	.free_gatt_table	= efficeon_free_gatt_table,
3298c2ecf20Sopenharmony_ci	.insert_memory		= efficeon_insert_memory,
3308c2ecf20Sopenharmony_ci	.remove_memory		= efficeon_remove_memory,
3318c2ecf20Sopenharmony_ci	.cant_use_aperture	= false,	// true might be faster?
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	// Generic
3348c2ecf20Sopenharmony_ci	.alloc_by_type		= agp_generic_alloc_by_type,
3358c2ecf20Sopenharmony_ci	.free_by_type		= agp_generic_free_by_type,
3368c2ecf20Sopenharmony_ci	.agp_alloc_page		= agp_generic_alloc_page,
3378c2ecf20Sopenharmony_ci	.agp_alloc_pages	= agp_generic_alloc_pages,
3388c2ecf20Sopenharmony_ci	.agp_destroy_page	= agp_generic_destroy_page,
3398c2ecf20Sopenharmony_ci	.agp_destroy_pages	= agp_generic_destroy_pages,
3408c2ecf20Sopenharmony_ci	.agp_type_to_mask_type  = agp_generic_type_to_mask_type,
3418c2ecf20Sopenharmony_ci};
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_cistatic int agp_efficeon_probe(struct pci_dev *pdev,
3448c2ecf20Sopenharmony_ci			      const struct pci_device_id *ent)
3458c2ecf20Sopenharmony_ci{
3468c2ecf20Sopenharmony_ci	struct agp_bridge_data *bridge;
3478c2ecf20Sopenharmony_ci	u8 cap_ptr;
3488c2ecf20Sopenharmony_ci	struct resource *r;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP);
3518c2ecf20Sopenharmony_ci	if (!cap_ptr)
3528c2ecf20Sopenharmony_ci		return -ENODEV;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	/* Probe for Efficeon controller */
3558c2ecf20Sopenharmony_ci	if (pdev->device != PCI_DEVICE_ID_EFFICEON) {
3568c2ecf20Sopenharmony_ci		printk(KERN_ERR PFX "Unsupported Efficeon chipset (device id: %04x)\n",
3578c2ecf20Sopenharmony_ci		    pdev->device);
3588c2ecf20Sopenharmony_ci		return -ENODEV;
3598c2ecf20Sopenharmony_ci	}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	printk(KERN_INFO PFX "Detected Transmeta Efficeon TM8000 series chipset\n");
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	bridge = agp_alloc_bridge();
3648c2ecf20Sopenharmony_ci	if (!bridge)
3658c2ecf20Sopenharmony_ci		return -ENOMEM;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	bridge->driver = &efficeon_driver;
3688c2ecf20Sopenharmony_ci	bridge->dev = pdev;
3698c2ecf20Sopenharmony_ci	bridge->capndx = cap_ptr;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	/*
3728c2ecf20Sopenharmony_ci	* If the device has not been properly setup, the following will catch
3738c2ecf20Sopenharmony_ci	* the problem and should stop the system from crashing.
3748c2ecf20Sopenharmony_ci	* 20030610 - hamish@zot.org
3758c2ecf20Sopenharmony_ci	*/
3768c2ecf20Sopenharmony_ci	if (pci_enable_device(pdev)) {
3778c2ecf20Sopenharmony_ci		printk(KERN_ERR PFX "Unable to Enable PCI device\n");
3788c2ecf20Sopenharmony_ci		agp_put_bridge(bridge);
3798c2ecf20Sopenharmony_ci		return -ENODEV;
3808c2ecf20Sopenharmony_ci	}
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	/*
3838c2ecf20Sopenharmony_ci	* The following fixes the case where the BIOS has "forgotten" to
3848c2ecf20Sopenharmony_ci	* provide an address range for the GART.
3858c2ecf20Sopenharmony_ci	* 20030610 - hamish@zot.org
3868c2ecf20Sopenharmony_ci	*/
3878c2ecf20Sopenharmony_ci	r = &pdev->resource[0];
3888c2ecf20Sopenharmony_ci	if (!r->start && r->end) {
3898c2ecf20Sopenharmony_ci		if (pci_assign_resource(pdev, 0)) {
3908c2ecf20Sopenharmony_ci			printk(KERN_ERR PFX "could not assign resource 0\n");
3918c2ecf20Sopenharmony_ci			agp_put_bridge(bridge);
3928c2ecf20Sopenharmony_ci			return -ENODEV;
3938c2ecf20Sopenharmony_ci		}
3948c2ecf20Sopenharmony_ci	}
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	/* Fill in the mode register */
3978c2ecf20Sopenharmony_ci	if (cap_ptr) {
3988c2ecf20Sopenharmony_ci		pci_read_config_dword(pdev,
3998c2ecf20Sopenharmony_ci				bridge->capndx+PCI_AGP_STATUS,
4008c2ecf20Sopenharmony_ci				&bridge->mode);
4018c2ecf20Sopenharmony_ci	}
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	pci_set_drvdata(pdev, bridge);
4048c2ecf20Sopenharmony_ci	return agp_add_bridge(bridge);
4058c2ecf20Sopenharmony_ci}
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_cistatic void agp_efficeon_remove(struct pci_dev *pdev)
4088c2ecf20Sopenharmony_ci{
4098c2ecf20Sopenharmony_ci	struct agp_bridge_data *bridge = pci_get_drvdata(pdev);
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	agp_remove_bridge(bridge);
4128c2ecf20Sopenharmony_ci	agp_put_bridge(bridge);
4138c2ecf20Sopenharmony_ci}
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
4168c2ecf20Sopenharmony_cistatic int agp_efficeon_suspend(struct pci_dev *dev, pm_message_t state)
4178c2ecf20Sopenharmony_ci{
4188c2ecf20Sopenharmony_ci	return 0;
4198c2ecf20Sopenharmony_ci}
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_cistatic int agp_efficeon_resume(struct pci_dev *pdev)
4228c2ecf20Sopenharmony_ci{
4238c2ecf20Sopenharmony_ci	printk(KERN_DEBUG PFX "agp_efficeon_resume()\n");
4248c2ecf20Sopenharmony_ci	return efficeon_configure();
4258c2ecf20Sopenharmony_ci}
4268c2ecf20Sopenharmony_ci#endif
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_cistatic const struct pci_device_id agp_efficeon_pci_table[] = {
4298c2ecf20Sopenharmony_ci	{
4308c2ecf20Sopenharmony_ci	.class		= (PCI_CLASS_BRIDGE_HOST << 8),
4318c2ecf20Sopenharmony_ci	.class_mask	= ~0,
4328c2ecf20Sopenharmony_ci	.vendor		= PCI_VENDOR_ID_TRANSMETA,
4338c2ecf20Sopenharmony_ci	.device		= PCI_ANY_ID,
4348c2ecf20Sopenharmony_ci	.subvendor	= PCI_ANY_ID,
4358c2ecf20Sopenharmony_ci	.subdevice	= PCI_ANY_ID,
4368c2ecf20Sopenharmony_ci	},
4378c2ecf20Sopenharmony_ci	{ }
4388c2ecf20Sopenharmony_ci};
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, agp_efficeon_pci_table);
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_cistatic struct pci_driver agp_efficeon_pci_driver = {
4438c2ecf20Sopenharmony_ci	.name		= "agpgart-efficeon",
4448c2ecf20Sopenharmony_ci	.id_table	= agp_efficeon_pci_table,
4458c2ecf20Sopenharmony_ci	.probe		= agp_efficeon_probe,
4468c2ecf20Sopenharmony_ci	.remove		= agp_efficeon_remove,
4478c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
4488c2ecf20Sopenharmony_ci	.suspend	= agp_efficeon_suspend,
4498c2ecf20Sopenharmony_ci	.resume		= agp_efficeon_resume,
4508c2ecf20Sopenharmony_ci#endif
4518c2ecf20Sopenharmony_ci};
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_cistatic int __init agp_efficeon_init(void)
4548c2ecf20Sopenharmony_ci{
4558c2ecf20Sopenharmony_ci	static int agp_initialised=0;
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	if (agp_off)
4588c2ecf20Sopenharmony_ci		return -EINVAL;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	if (agp_initialised == 1)
4618c2ecf20Sopenharmony_ci		return 0;
4628c2ecf20Sopenharmony_ci	agp_initialised=1;
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	return pci_register_driver(&agp_efficeon_pci_driver);
4658c2ecf20Sopenharmony_ci}
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_cistatic void __exit agp_efficeon_cleanup(void)
4688c2ecf20Sopenharmony_ci{
4698c2ecf20Sopenharmony_ci	pci_unregister_driver(&agp_efficeon_pci_driver);
4708c2ecf20Sopenharmony_ci}
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_cimodule_init(agp_efficeon_init);
4738c2ecf20Sopenharmony_cimodule_exit(agp_efficeon_cleanup);
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ciMODULE_AUTHOR("Carlos Puchol <cpglinux@puchol.com>");
4768c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL and additional rights");
477