xref: /linux/drivers/char/agp/uninorth-agp.c (revision 89f6fc9c)
109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * UniNorth AGPGART routines.
41da177e4SLinus Torvalds  */
51da177e4SLinus Torvalds #include <linux/module.h>
6*89f6fc9cSRob Herring #include <linux/of.h>
71da177e4SLinus Torvalds #include <linux/pci.h>
85a0e3ad6STejun Heo #include <linux/slab.h>
91da177e4SLinus Torvalds #include <linux/init.h>
101da177e4SLinus Torvalds #include <linux/pagemap.h>
111da177e4SLinus Torvalds #include <linux/agp_backend.h>
121da177e4SLinus Torvalds #include <linux/delay.h>
13e8a5f900SMichel Dänzer #include <linux/vmalloc.h>
141da177e4SLinus Torvalds #include <asm/uninorth.h>
151da177e4SLinus Torvalds #include <asm/prom.h>
160c541b44SBenjamin Herrenschmidt #include <asm/pmac_feature.h>
171da177e4SLinus Torvalds #include "agp.h"
181da177e4SLinus Torvalds 
191da177e4SLinus Torvalds /*
201da177e4SLinus Torvalds  * NOTES for uninorth3 (G5 AGP) supports :
211da177e4SLinus Torvalds  *
221da177e4SLinus Torvalds  * There maybe also possibility to have bigger cache line size for
231da177e4SLinus Torvalds  * agp (see pmac_pci.c and look for cache line). Need to be investigated
241da177e4SLinus Torvalds  * by someone.
251da177e4SLinus Torvalds  *
261da177e4SLinus Torvalds  * PAGE size are hardcoded but this may change, see asm/page.h.
271da177e4SLinus Torvalds  *
281da177e4SLinus Torvalds  * Jerome Glisse <j.glisse@gmail.com>
291da177e4SLinus Torvalds  */
301da177e4SLinus Torvalds static int uninorth_rev;
311da177e4SLinus Torvalds static int is_u3;
3261cf0593SJerome Glisse static u32 scratch_value;
331da177e4SLinus Torvalds 
3452f072cbSMichel Dänzer #define DEFAULT_APERTURE_SIZE 256
3552f072cbSMichel Dänzer #define DEFAULT_APERTURE_STRING "256"
36b0385146SAl Viro static char *aperture = NULL;
370c541b44SBenjamin Herrenschmidt 
uninorth_fetch_size(void)381da177e4SLinus Torvalds static int uninorth_fetch_size(void)
391da177e4SLinus Torvalds {
4018088748SMichel Dänzer 	int i, size = 0;
4118088748SMichel Dänzer 	struct aper_size_info_32 *values =
4218088748SMichel Dänzer 	    A_SIZE_32(agp_bridge->driver->aperture_sizes);
431da177e4SLinus Torvalds 
4418088748SMichel Dänzer 	if (aperture) {
4518088748SMichel Dänzer 		char *save = aperture;
461da177e4SLinus Torvalds 
4718088748SMichel Dänzer 		size = memparse(aperture, &aperture) >> 20;
4818088748SMichel Dänzer 		aperture = save;
4918088748SMichel Dänzer 
5018088748SMichel Dänzer 		for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++)
5118088748SMichel Dänzer 			if (size == values[i].size)
5218088748SMichel Dänzer 				break;
5318088748SMichel Dänzer 
5418088748SMichel Dänzer 		if (i == agp_bridge->driver->num_aperture_sizes) {
55e3cf6951SBjorn Helgaas 			dev_err(&agp_bridge->dev->dev, "invalid aperture size, "
56e3cf6951SBjorn Helgaas 				"using default\n");
5718088748SMichel Dänzer 			size = 0;
5818088748SMichel Dänzer 			aperture = NULL;
5918088748SMichel Dänzer 		}
6018088748SMichel Dänzer 	}
6118088748SMichel Dänzer 
6218088748SMichel Dänzer 	if (!size) {
6318088748SMichel Dänzer 		for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++)
6452f072cbSMichel Dänzer 			if (values[i].size == DEFAULT_APERTURE_SIZE)
6518088748SMichel Dänzer 				break;
6618088748SMichel Dänzer 	}
6718088748SMichel Dänzer 
681da177e4SLinus Torvalds 	agp_bridge->previous_size =
691da177e4SLinus Torvalds 	    agp_bridge->current_size = (void *)(values + i);
701da177e4SLinus Torvalds 	agp_bridge->aperture_size_idx = i;
711da177e4SLinus Torvalds 	return values[i].size;
721da177e4SLinus Torvalds }
731da177e4SLinus Torvalds 
uninorth_tlbflush(struct agp_memory * mem)741da177e4SLinus Torvalds static void uninorth_tlbflush(struct agp_memory *mem)
751da177e4SLinus Torvalds {
761da177e4SLinus Torvalds 	u32 ctrl = UNI_N_CFG_GART_ENABLE;
771da177e4SLinus Torvalds 
781da177e4SLinus Torvalds 	if (is_u3)
791da177e4SLinus Torvalds 		ctrl |= U3_N_CFG_GART_PERFRD;
801da177e4SLinus Torvalds 	pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL,
811da177e4SLinus Torvalds 			       ctrl | UNI_N_CFG_GART_INVAL);
821da177e4SLinus Torvalds 	pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL, ctrl);
831da177e4SLinus Torvalds 
845613beb4SMichel Dänzer 	if (!mem && uninorth_rev <= 0x30) {
851da177e4SLinus Torvalds 		pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL,
861da177e4SLinus Torvalds 				       ctrl | UNI_N_CFG_GART_2xRESET);
871da177e4SLinus Torvalds 		pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL,
881da177e4SLinus Torvalds 				       ctrl);
891da177e4SLinus Torvalds 	}
901da177e4SLinus Torvalds }
911da177e4SLinus Torvalds 
uninorth_cleanup(void)921da177e4SLinus Torvalds static void uninorth_cleanup(void)
931da177e4SLinus Torvalds {
941da177e4SLinus Torvalds 	u32 tmp;
951da177e4SLinus Torvalds 
961da177e4SLinus Torvalds 	pci_read_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL, &tmp);
971da177e4SLinus Torvalds 	if (!(tmp & UNI_N_CFG_GART_ENABLE))
981da177e4SLinus Torvalds 		return;
991da177e4SLinus Torvalds 	tmp |= UNI_N_CFG_GART_INVAL;
1001da177e4SLinus Torvalds 	pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL, tmp);
1011da177e4SLinus Torvalds 	pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL, 0);
1021da177e4SLinus Torvalds 
1031da177e4SLinus Torvalds 	if (uninorth_rev <= 0x30) {
1041da177e4SLinus Torvalds 		pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL,
1051da177e4SLinus Torvalds 				       UNI_N_CFG_GART_2xRESET);
1061da177e4SLinus Torvalds 		pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL,
1071da177e4SLinus Torvalds 				       0);
1081da177e4SLinus Torvalds 	}
1091da177e4SLinus Torvalds }
1101da177e4SLinus Torvalds 
uninorth_configure(void)1111da177e4SLinus Torvalds static int uninorth_configure(void)
1121da177e4SLinus Torvalds {
1131da177e4SLinus Torvalds 	struct aper_size_info_32 *current_size;
1141da177e4SLinus Torvalds 
1151da177e4SLinus Torvalds 	current_size = A_SIZE_32(agp_bridge->current_size);
1161da177e4SLinus Torvalds 
117e3cf6951SBjorn Helgaas 	dev_info(&agp_bridge->dev->dev, "configuring for size idx: %d\n",
1181da177e4SLinus Torvalds 		 current_size->size_value);
1191da177e4SLinus Torvalds 
1201da177e4SLinus Torvalds 	/* aperture size and gatt addr */
1211da177e4SLinus Torvalds 	pci_write_config_dword(agp_bridge->dev,
1221da177e4SLinus Torvalds 		UNI_N_CFG_GART_BASE,
1231da177e4SLinus Torvalds 		(agp_bridge->gatt_bus_addr & 0xfffff000)
1241da177e4SLinus Torvalds 			| current_size->size_value);
1251da177e4SLinus Torvalds 
1261da177e4SLinus Torvalds 	/* HACK ALERT
1271da177e4SLinus Torvalds 	 * UniNorth seem to be buggy enough not to handle properly when
1281da177e4SLinus Torvalds 	 * the AGP aperture isn't mapped at bus physical address 0
1291da177e4SLinus Torvalds 	 */
1301da177e4SLinus Torvalds 	agp_bridge->gart_bus_addr = 0;
1311da177e4SLinus Torvalds #ifdef CONFIG_PPC64
1321da177e4SLinus Torvalds 	/* Assume U3 or later on PPC64 systems */
1331da177e4SLinus Torvalds 	/* high 4 bits of GART physical address go in UNI_N_CFG_AGP_BASE */
1341da177e4SLinus Torvalds 	pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_AGP_BASE,
1351da177e4SLinus Torvalds 			       (agp_bridge->gatt_bus_addr >> 32) & 0xf);
1361da177e4SLinus Torvalds #else
1371da177e4SLinus Torvalds 	pci_write_config_dword(agp_bridge->dev,
1381da177e4SLinus Torvalds 		UNI_N_CFG_AGP_BASE, agp_bridge->gart_bus_addr);
1391da177e4SLinus Torvalds #endif
1401da177e4SLinus Torvalds 
1411da177e4SLinus Torvalds 	if (is_u3) {
1421da177e4SLinus Torvalds 		pci_write_config_dword(agp_bridge->dev,
1431da177e4SLinus Torvalds 				       UNI_N_CFG_GART_DUMMY_PAGE,
1445e8d6b8bSDavid Woodhouse 				       page_to_phys(agp_bridge->scratch_page_page) >> 12);
1451da177e4SLinus Torvalds 	}
1461da177e4SLinus Torvalds 
1471da177e4SLinus Torvalds 	return 0;
1481da177e4SLinus Torvalds }
1491da177e4SLinus Torvalds 
uninorth_insert_memory(struct agp_memory * mem,off_t pg_start,int type)15037580f3fSMichel Dänzer static int uninorth_insert_memory(struct agp_memory *mem, off_t pg_start, int type)
1511da177e4SLinus Torvalds {
1521da177e4SLinus Torvalds 	int i, num_entries;
1531da177e4SLinus Torvalds 	void *temp;
1541da177e4SLinus Torvalds 	u32 *gp;
15562369028SMichel Dänzer 	int mask_type;
1561da177e4SLinus Torvalds 
15762369028SMichel Dänzer 	if (type != mem->type)
15862369028SMichel Dänzer 		return -EINVAL;
15962369028SMichel Dänzer 
16062369028SMichel Dänzer 	mask_type = agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type);
16162369028SMichel Dänzer 	if (mask_type != 0) {
1621da177e4SLinus Torvalds 		/* We know nothing of memory types */
1631da177e4SLinus Torvalds 		return -EINVAL;
16462369028SMichel Dänzer 	}
16562369028SMichel Dänzer 
1663fc3a6b4SMichel Dänzer 	if (mem->page_count == 0)
1673fc3a6b4SMichel Dänzer 		return 0;
1683fc3a6b4SMichel Dänzer 
1693fc3a6b4SMichel Dänzer 	temp = agp_bridge->current_size;
1703fc3a6b4SMichel Dänzer 	num_entries = A_SIZE_32(temp)->num_entries;
1713fc3a6b4SMichel Dänzer 
1721da177e4SLinus Torvalds 	if ((pg_start + mem->page_count) > num_entries)
1731da177e4SLinus Torvalds 		return -EINVAL;
1741da177e4SLinus Torvalds 
1751da177e4SLinus Torvalds 	gp = (u32 *) &agp_bridge->gatt_table[pg_start];
1761da177e4SLinus Torvalds 	for (i = 0; i < mem->page_count; ++i) {
17761cf0593SJerome Glisse 		if (gp[i] != scratch_value) {
178e3cf6951SBjorn Helgaas 			dev_info(&agp_bridge->dev->dev,
17937580f3fSMichel Dänzer 				 "uninorth_insert_memory: entry 0x%x occupied (%x)\n",
1801da177e4SLinus Torvalds 				 i, gp[i]);
1811da177e4SLinus Torvalds 			return -EBUSY;
1821da177e4SLinus Torvalds 		}
1831da177e4SLinus Torvalds 	}
1841da177e4SLinus Torvalds 
1851da177e4SLinus Torvalds 	for (i = 0; i < mem->page_count; i++) {
18637580f3fSMichel Dänzer 		if (is_u3)
18707613ba2SDave Airlie 			gp[i] = (page_to_phys(mem->pages[i]) >> PAGE_SHIFT) | 0x80000000UL;
18837580f3fSMichel Dänzer 		else
18937580f3fSMichel Dänzer 			gp[i] =	cpu_to_le32((page_to_phys(mem->pages[i]) & 0xFFFFF000UL) |
19037580f3fSMichel Dänzer 					    0x1UL);
19107613ba2SDave Airlie 		flush_dcache_range((unsigned long)__va(page_to_phys(mem->pages[i])),
19207613ba2SDave Airlie 				   (unsigned long)__va(page_to_phys(mem->pages[i]))+0x1000);
1931da177e4SLinus Torvalds 	}
1941da177e4SLinus Torvalds 	mb();
1951da177e4SLinus Torvalds 	uninorth_tlbflush(mem);
1961da177e4SLinus Torvalds 
1971da177e4SLinus Torvalds 	return 0;
1981da177e4SLinus Torvalds }
1991da177e4SLinus Torvalds 
uninorth_remove_memory(struct agp_memory * mem,off_t pg_start,int type)200dec60f3aSMathieu Malaterre static int uninorth_remove_memory(struct agp_memory *mem, off_t pg_start, int type)
2011da177e4SLinus Torvalds {
2021da177e4SLinus Torvalds 	size_t i;
2031da177e4SLinus Torvalds 	u32 *gp;
2043fc3a6b4SMichel Dänzer 	int mask_type;
2051da177e4SLinus Torvalds 
2063fc3a6b4SMichel Dänzer 	if (type != mem->type)
2073fc3a6b4SMichel Dänzer 		return -EINVAL;
2083fc3a6b4SMichel Dänzer 
2093fc3a6b4SMichel Dänzer 	mask_type = agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type);
2103fc3a6b4SMichel Dänzer 	if (mask_type != 0) {
2111da177e4SLinus Torvalds 		/* We know nothing of memory types */
2121da177e4SLinus Torvalds 		return -EINVAL;
2133fc3a6b4SMichel Dänzer 	}
2143fc3a6b4SMichel Dänzer 
2153fc3a6b4SMichel Dänzer 	if (mem->page_count == 0)
2163fc3a6b4SMichel Dänzer 		return 0;
2171da177e4SLinus Torvalds 
2181da177e4SLinus Torvalds 	gp = (u32 *) &agp_bridge->gatt_table[pg_start];
21961cf0593SJerome Glisse 	for (i = 0; i < mem->page_count; ++i) {
22061cf0593SJerome Glisse 		gp[i] = scratch_value;
22161cf0593SJerome Glisse 	}
2221da177e4SLinus Torvalds 	mb();
2231da177e4SLinus Torvalds 	uninorth_tlbflush(mem);
2241da177e4SLinus Torvalds 
2251da177e4SLinus Torvalds 	return 0;
2261da177e4SLinus Torvalds }
2271da177e4SLinus Torvalds 
uninorth_agp_enable(struct agp_bridge_data * bridge,u32 mode)2281da177e4SLinus Torvalds static void uninorth_agp_enable(struct agp_bridge_data *bridge, u32 mode)
2291da177e4SLinus Torvalds {
2301da177e4SLinus Torvalds 	u32 command, scratch, status;
2311da177e4SLinus Torvalds 	int timeout;
2321da177e4SLinus Torvalds 
2331da177e4SLinus Torvalds 	pci_read_config_dword(bridge->dev,
2341da177e4SLinus Torvalds 			      bridge->capndx + PCI_AGP_STATUS,
2351da177e4SLinus Torvalds 			      &status);
2361da177e4SLinus Torvalds 
2371da177e4SLinus Torvalds 	command = agp_collect_device_status(bridge, mode, status);
2381da177e4SLinus Torvalds 	command |= PCI_AGP_COMMAND_AGP;
2391da177e4SLinus Torvalds 
2401da177e4SLinus Torvalds 	if (uninorth_rev == 0x21) {
2411da177e4SLinus Torvalds 		/*
2421da177e4SLinus Torvalds 		 * Darwin disable AGP 4x on this revision, thus we
2431da177e4SLinus Torvalds 		 * may assume it's broken. This is an AGP2 controller.
2441da177e4SLinus Torvalds 		 */
2451da177e4SLinus Torvalds 		command &= ~AGPSTAT2_4X;
2461da177e4SLinus Torvalds 	}
2471da177e4SLinus Torvalds 
2481da177e4SLinus Torvalds 	if ((uninorth_rev >= 0x30) && (uninorth_rev <= 0x33)) {
2491da177e4SLinus Torvalds 		/*
250fd589a8fSAnand Gadiyar 		 * We need to set REQ_DEPTH to 7 for U3 versions 1.0, 2.1,
2511da177e4SLinus Torvalds 		 * 2.2 and 2.3, Darwin do so.
2521da177e4SLinus Torvalds 		 */
2531da177e4SLinus Torvalds 		if ((command >> AGPSTAT_RQ_DEPTH_SHIFT) > 7)
2541da177e4SLinus Torvalds 			command = (command & ~AGPSTAT_RQ_DEPTH)
2551da177e4SLinus Torvalds 				| (7 << AGPSTAT_RQ_DEPTH_SHIFT);
2561da177e4SLinus Torvalds 	}
2571da177e4SLinus Torvalds 
2581da177e4SLinus Torvalds 	uninorth_tlbflush(NULL);
2591da177e4SLinus Torvalds 
2601da177e4SLinus Torvalds 	timeout = 0;
2611da177e4SLinus Torvalds 	do {
2621da177e4SLinus Torvalds 		pci_write_config_dword(bridge->dev,
2631da177e4SLinus Torvalds 				       bridge->capndx + PCI_AGP_COMMAND,
2641da177e4SLinus Torvalds 				       command);
2651da177e4SLinus Torvalds 		pci_read_config_dword(bridge->dev,
2661da177e4SLinus Torvalds 				      bridge->capndx + PCI_AGP_COMMAND,
2671da177e4SLinus Torvalds 				       &scratch);
2681da177e4SLinus Torvalds 	} while ((scratch & PCI_AGP_COMMAND_AGP) == 0 && ++timeout < 1000);
2691da177e4SLinus Torvalds 	if ((scratch & PCI_AGP_COMMAND_AGP) == 0)
270e3cf6951SBjorn Helgaas 		dev_err(&bridge->dev->dev, "can't write UniNorth AGP "
2710c541b44SBenjamin Herrenschmidt 			"command register\n");
2721da177e4SLinus Torvalds 
2731da177e4SLinus Torvalds 	if (uninorth_rev >= 0x30) {
2741da177e4SLinus Torvalds 		/* This is an AGP V3 */
275c7258012SJoe Perches 		agp_device_command(command, (status & AGPSTAT_MODE_3_0) != 0);
2761da177e4SLinus Torvalds 	} else {
2771da177e4SLinus Torvalds 		/* AGP V2 */
278c7258012SJoe Perches 		agp_device_command(command, false);
2791da177e4SLinus Torvalds 	}
2801da177e4SLinus Torvalds 
2811da177e4SLinus Torvalds 	uninorth_tlbflush(NULL);
2821da177e4SLinus Torvalds }
2831da177e4SLinus Torvalds 
2841da177e4SLinus Torvalds #ifdef CONFIG_PM
2850c541b44SBenjamin Herrenschmidt /*
2860c541b44SBenjamin Herrenschmidt  * These Power Management routines are _not_ called by the normal PCI PM layer,
2870c541b44SBenjamin Herrenschmidt  * but directly by the video driver through function pointers in the device
2880c541b44SBenjamin Herrenschmidt  * tree.
2890c541b44SBenjamin Herrenschmidt  */
agp_uninorth_suspend(struct pci_dev * pdev)2900c541b44SBenjamin Herrenschmidt static int agp_uninorth_suspend(struct pci_dev *pdev)
2911da177e4SLinus Torvalds {
2920c541b44SBenjamin Herrenschmidt 	struct agp_bridge_data *bridge;
2931da177e4SLinus Torvalds 	u32 cmd;
2941da177e4SLinus Torvalds 	u8 agp;
2951da177e4SLinus Torvalds 	struct pci_dev *device = NULL;
2961da177e4SLinus Torvalds 
2970c541b44SBenjamin Herrenschmidt 	bridge = agp_find_bridge(pdev);
2980c541b44SBenjamin Herrenschmidt 	if (bridge == NULL)
2990c541b44SBenjamin Herrenschmidt 		return -ENODEV;
3000c541b44SBenjamin Herrenschmidt 
3010c541b44SBenjamin Herrenschmidt 	/* Only one suspend supported */
3020c541b44SBenjamin Herrenschmidt 	if (bridge->dev_private_data)
3031da177e4SLinus Torvalds 		return 0;
3041da177e4SLinus Torvalds 
3051da177e4SLinus Torvalds 	/* turn off AGP on the video chip, if it was enabled */
3061da177e4SLinus Torvalds 	for_each_pci_dev(device) {
3071da177e4SLinus Torvalds 		/* Don't touch the bridge yet, device first */
3081da177e4SLinus Torvalds 		if (device == pdev)
3091da177e4SLinus Torvalds 			continue;
3101da177e4SLinus Torvalds 		/* Only deal with devices on the same bus here, no Mac has a P2P
3111da177e4SLinus Torvalds 		 * bridge on the AGP port, and mucking around the entire PCI
3121da177e4SLinus Torvalds 		 * tree is source of problems on some machines because of a bug
3131da177e4SLinus Torvalds 		 * in some versions of pci_find_capability() when hitting a dead
3141da177e4SLinus Torvalds 		 * device
3151da177e4SLinus Torvalds 		 */
3161da177e4SLinus Torvalds 		if (device->bus != pdev->bus)
3171da177e4SLinus Torvalds 			continue;
3181da177e4SLinus Torvalds 		agp = pci_find_capability(device, PCI_CAP_ID_AGP);
3191da177e4SLinus Torvalds 		if (!agp)
3201da177e4SLinus Torvalds 			continue;
3211da177e4SLinus Torvalds 		pci_read_config_dword(device, agp + PCI_AGP_COMMAND, &cmd);
3221da177e4SLinus Torvalds 		if (!(cmd & PCI_AGP_COMMAND_AGP))
3231da177e4SLinus Torvalds 			continue;
324e3cf6951SBjorn Helgaas 		dev_info(&pdev->dev, "disabling AGP on device %s\n",
3251da177e4SLinus Torvalds 			 pci_name(device));
3261da177e4SLinus Torvalds 		cmd &= ~PCI_AGP_COMMAND_AGP;
3271da177e4SLinus Torvalds 		pci_write_config_dword(device, agp + PCI_AGP_COMMAND, cmd);
3281da177e4SLinus Torvalds 	}
3291da177e4SLinus Torvalds 
3301da177e4SLinus Torvalds 	/* turn off AGP on the bridge */
3311da177e4SLinus Torvalds 	agp = pci_find_capability(pdev, PCI_CAP_ID_AGP);
3321da177e4SLinus Torvalds 	pci_read_config_dword(pdev, agp + PCI_AGP_COMMAND, &cmd);
333b07cd518SAndrew Morton 	bridge->dev_private_data = (void *)(long)cmd;
3341da177e4SLinus Torvalds 	if (cmd & PCI_AGP_COMMAND_AGP) {
335e3cf6951SBjorn Helgaas 		dev_info(&pdev->dev, "disabling AGP on bridge\n");
3361da177e4SLinus Torvalds 		cmd &= ~PCI_AGP_COMMAND_AGP;
3371da177e4SLinus Torvalds 		pci_write_config_dword(pdev, agp + PCI_AGP_COMMAND, cmd);
3381da177e4SLinus Torvalds 	}
3391da177e4SLinus Torvalds 	/* turn off the GART */
3401da177e4SLinus Torvalds 	uninorth_cleanup();
3411da177e4SLinus Torvalds 
3421da177e4SLinus Torvalds 	return 0;
3431da177e4SLinus Torvalds }
3441da177e4SLinus Torvalds 
agp_uninorth_resume(struct pci_dev * pdev)3451da177e4SLinus Torvalds static int agp_uninorth_resume(struct pci_dev *pdev)
3461da177e4SLinus Torvalds {
3470c541b44SBenjamin Herrenschmidt 	struct agp_bridge_data *bridge;
3480c541b44SBenjamin Herrenschmidt 	u32 command;
3490c541b44SBenjamin Herrenschmidt 
3500c541b44SBenjamin Herrenschmidt 	bridge = agp_find_bridge(pdev);
3510c541b44SBenjamin Herrenschmidt 	if (bridge == NULL)
3520c541b44SBenjamin Herrenschmidt 		return -ENODEV;
3530c541b44SBenjamin Herrenschmidt 
354b07cd518SAndrew Morton 	command = (long)bridge->dev_private_data;
3550c541b44SBenjamin Herrenschmidt 	bridge->dev_private_data = NULL;
3560c541b44SBenjamin Herrenschmidt 	if (!(command & PCI_AGP_COMMAND_AGP))
3570c541b44SBenjamin Herrenschmidt 		return 0;
3580c541b44SBenjamin Herrenschmidt 
3590c541b44SBenjamin Herrenschmidt 	uninorth_agp_enable(bridge, command);
3600c541b44SBenjamin Herrenschmidt 
3611da177e4SLinus Torvalds 	return 0;
3621da177e4SLinus Torvalds }
3630c541b44SBenjamin Herrenschmidt #endif /* CONFIG_PM */
3641da177e4SLinus Torvalds 
3655ada62b1SDenis Kirjanov static struct {
3665ada62b1SDenis Kirjanov 	struct page **pages_arr;
3675ada62b1SDenis Kirjanov } uninorth_priv;
3685ada62b1SDenis Kirjanov 
uninorth_create_gatt_table(struct agp_bridge_data * bridge)3691da177e4SLinus Torvalds static int uninorth_create_gatt_table(struct agp_bridge_data *bridge)
3701da177e4SLinus Torvalds {
3711da177e4SLinus Torvalds 	char *table;
3721da177e4SLinus Torvalds 	char *table_end;
3731da177e4SLinus Torvalds 	int size;
3741da177e4SLinus Torvalds 	int page_order;
3751da177e4SLinus Torvalds 	int num_entries;
3761da177e4SLinus Torvalds 	int i;
3771da177e4SLinus Torvalds 	void *temp;
3781da177e4SLinus Torvalds 	struct page *page;
3791da177e4SLinus Torvalds 
3801da177e4SLinus Torvalds 	/* We can't handle 2 level gatt's */
3811da177e4SLinus Torvalds 	if (bridge->driver->size_type == LVL2_APER_SIZE)
3821da177e4SLinus Torvalds 		return -EINVAL;
3831da177e4SLinus Torvalds 
3841da177e4SLinus Torvalds 	table = NULL;
3851da177e4SLinus Torvalds 	i = bridge->aperture_size_idx;
3861da177e4SLinus Torvalds 	temp = bridge->current_size;
3871da177e4SLinus Torvalds 	size = page_order = num_entries = 0;
3881da177e4SLinus Torvalds 
3891da177e4SLinus Torvalds 	do {
3901da177e4SLinus Torvalds 		size = A_SIZE_32(temp)->size;
3911da177e4SLinus Torvalds 		page_order = A_SIZE_32(temp)->page_order;
3921da177e4SLinus Torvalds 		num_entries = A_SIZE_32(temp)->num_entries;
3931da177e4SLinus Torvalds 
3941da177e4SLinus Torvalds 		table = (char *) __get_free_pages(GFP_KERNEL, page_order);
3951da177e4SLinus Torvalds 
3961da177e4SLinus Torvalds 		if (table == NULL) {
3971da177e4SLinus Torvalds 			i++;
3981da177e4SLinus Torvalds 			bridge->current_size = A_IDX32(bridge);
3991da177e4SLinus Torvalds 		} else {
4001da177e4SLinus Torvalds 			bridge->aperture_size_idx = i;
4011da177e4SLinus Torvalds 		}
4021da177e4SLinus Torvalds 	} while (!table && (i < bridge->driver->num_aperture_sizes));
4031da177e4SLinus Torvalds 
4041da177e4SLinus Torvalds 	if (table == NULL)
4051da177e4SLinus Torvalds 		return -ENOMEM;
4061da177e4SLinus Torvalds 
4076da2ec56SKees Cook 	uninorth_priv.pages_arr = kmalloc_array(1 << page_order,
4086da2ec56SKees Cook 						sizeof(struct page *),
4096da2ec56SKees Cook 						GFP_KERNEL);
4105ada62b1SDenis Kirjanov 	if (uninorth_priv.pages_arr == NULL)
411e8a5f900SMichel Dänzer 		goto enomem;
412e8a5f900SMichel Dänzer 
4131da177e4SLinus Torvalds 	table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1);
4141da177e4SLinus Torvalds 
415e8a5f900SMichel Dänzer 	for (page = virt_to_page(table), i = 0; page <= virt_to_page(table_end);
416e8a5f900SMichel Dänzer 	     page++, i++) {
4171da177e4SLinus Torvalds 		SetPageReserved(page);
4185ada62b1SDenis Kirjanov 		uninorth_priv.pages_arr[i] = page;
419e8a5f900SMichel Dänzer 	}
4201da177e4SLinus Torvalds 
4211da177e4SLinus Torvalds 	bridge->gatt_table_real = (u32 *) table;
422e8a5f900SMichel Dänzer 	/* Need to clear out any dirty data still sitting in caches */
423e8a5f900SMichel Dänzer 	flush_dcache_range((unsigned long)table,
42479905ad5SPaul Mackerras 			   (unsigned long)table_end + 1);
4255ada62b1SDenis Kirjanov 	bridge->gatt_table = vmap(uninorth_priv.pages_arr, (1 << page_order), 0, PAGE_KERNEL_NCG);
426e8a5f900SMichel Dänzer 
427e8a5f900SMichel Dänzer 	if (bridge->gatt_table == NULL)
428e8a5f900SMichel Dänzer 		goto enomem;
429e8a5f900SMichel Dänzer 
4306a12235cSDavid Woodhouse 	bridge->gatt_bus_addr = virt_to_phys(table);
4311da177e4SLinus Torvalds 
43261cf0593SJerome Glisse 	if (is_u3)
43361cf0593SJerome Glisse 		scratch_value = (page_to_phys(agp_bridge->scratch_page_page) >> PAGE_SHIFT) | 0x80000000UL;
43461cf0593SJerome Glisse 	else
43561cf0593SJerome Glisse 		scratch_value =	cpu_to_le32((page_to_phys(agp_bridge->scratch_page_page) & 0xFFFFF000UL) |
43661cf0593SJerome Glisse 				0x1UL);
4371da177e4SLinus Torvalds 	for (i = 0; i < num_entries; i++)
43861cf0593SJerome Glisse 		bridge->gatt_table[i] = scratch_value;
4391da177e4SLinus Torvalds 
4401da177e4SLinus Torvalds 	return 0;
441e8a5f900SMichel Dänzer 
442e8a5f900SMichel Dänzer enomem:
4435ada62b1SDenis Kirjanov 	kfree(uninorth_priv.pages_arr);
444e8a5f900SMichel Dänzer 	if (table)
445e8a5f900SMichel Dänzer 		free_pages((unsigned long)table, page_order);
446e8a5f900SMichel Dänzer 	return -ENOMEM;
4471da177e4SLinus Torvalds }
4481da177e4SLinus Torvalds 
uninorth_free_gatt_table(struct agp_bridge_data * bridge)4491da177e4SLinus Torvalds static int uninorth_free_gatt_table(struct agp_bridge_data *bridge)
4501da177e4SLinus Torvalds {
4511da177e4SLinus Torvalds 	int page_order;
4521da177e4SLinus Torvalds 	char *table, *table_end;
4531da177e4SLinus Torvalds 	void *temp;
4541da177e4SLinus Torvalds 	struct page *page;
4551da177e4SLinus Torvalds 
4561da177e4SLinus Torvalds 	temp = bridge->current_size;
4571da177e4SLinus Torvalds 	page_order = A_SIZE_32(temp)->page_order;
4581da177e4SLinus Torvalds 
4591da177e4SLinus Torvalds 	/* Do not worry about freeing memory, because if this is
4601da177e4SLinus Torvalds 	 * called, then all agp memory is deallocated and removed
4611da177e4SLinus Torvalds 	 * from the table.
4621da177e4SLinus Torvalds 	 */
4631da177e4SLinus Torvalds 
464e8a5f900SMichel Dänzer 	vunmap(bridge->gatt_table);
4655ada62b1SDenis Kirjanov 	kfree(uninorth_priv.pages_arr);
4661da177e4SLinus Torvalds 	table = (char *) bridge->gatt_table_real;
4671da177e4SLinus Torvalds 	table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1);
4681da177e4SLinus Torvalds 
4691da177e4SLinus Torvalds 	for (page = virt_to_page(table); page <= virt_to_page(table_end); page++)
4701da177e4SLinus Torvalds 		ClearPageReserved(page);
4711da177e4SLinus Torvalds 
4721da177e4SLinus Torvalds 	free_pages((unsigned long) bridge->gatt_table_real, page_order);
4731da177e4SLinus Torvalds 
4741da177e4SLinus Torvalds 	return 0;
4751da177e4SLinus Torvalds }
4761da177e4SLinus Torvalds 
null_cache_flush(void)477dec60f3aSMathieu Malaterre static void null_cache_flush(void)
4781da177e4SLinus Torvalds {
4791da177e4SLinus Torvalds 	mb();
4801da177e4SLinus Torvalds }
4811da177e4SLinus Torvalds 
4821da177e4SLinus Torvalds /* Setup function */
4831da177e4SLinus Torvalds 
48452f072cbSMichel Dänzer static const struct aper_size_info_32 uninorth_sizes[] =
4851da177e4SLinus Torvalds {
4861da177e4SLinus Torvalds 	{256, 65536, 6, 64},
4871da177e4SLinus Torvalds 	{128, 32768, 5, 32},
4881da177e4SLinus Torvalds 	{64, 16384, 4, 16},
4891da177e4SLinus Torvalds 	{32, 8192, 3, 8},
4901da177e4SLinus Torvalds 	{16, 4096, 2, 4},
4911da177e4SLinus Torvalds 	{8, 2048, 1, 2},
4921da177e4SLinus Torvalds 	{4, 1024, 0, 1}
4931da177e4SLinus Torvalds };
4941da177e4SLinus Torvalds 
4951da177e4SLinus Torvalds /*
4961da177e4SLinus Torvalds  * Not sure that u3 supports that high aperture sizes but it
4971da177e4SLinus Torvalds  * would strange if it did not :)
4981da177e4SLinus Torvalds  */
49952f072cbSMichel Dänzer static const struct aper_size_info_32 u3_sizes[] =
5001da177e4SLinus Torvalds {
5011da177e4SLinus Torvalds 	{512, 131072, 7, 128},
5021da177e4SLinus Torvalds 	{256, 65536, 6, 64},
5031da177e4SLinus Torvalds 	{128, 32768, 5, 32},
5041da177e4SLinus Torvalds 	{64, 16384, 4, 16},
5051da177e4SLinus Torvalds 	{32, 8192, 3, 8},
5061da177e4SLinus Torvalds 	{16, 4096, 2, 4},
5071da177e4SLinus Torvalds 	{8, 2048, 1, 2},
5081da177e4SLinus Torvalds 	{4, 1024, 0, 1}
5091da177e4SLinus Torvalds };
5101da177e4SLinus Torvalds 
511e047d1cfSRyusuke Konishi const struct agp_bridge_driver uninorth_agp_driver = {
5121da177e4SLinus Torvalds 	.owner			= THIS_MODULE,
5131da177e4SLinus Torvalds 	.aperture_sizes		= (void *)uninorth_sizes,
5141da177e4SLinus Torvalds 	.size_type		= U32_APER_SIZE,
51552f072cbSMichel Dänzer 	.num_aperture_sizes	= ARRAY_SIZE(uninorth_sizes),
5161da177e4SLinus Torvalds 	.configure		= uninorth_configure,
5171da177e4SLinus Torvalds 	.fetch_size		= uninorth_fetch_size,
5181da177e4SLinus Torvalds 	.cleanup		= uninorth_cleanup,
5191da177e4SLinus Torvalds 	.tlb_flush		= uninorth_tlbflush,
5201da177e4SLinus Torvalds 	.mask_memory		= agp_generic_mask_memory,
5211da177e4SLinus Torvalds 	.masks			= NULL,
5221da177e4SLinus Torvalds 	.cache_flush		= null_cache_flush,
5231da177e4SLinus Torvalds 	.agp_enable		= uninorth_agp_enable,
5241da177e4SLinus Torvalds 	.create_gatt_table	= uninorth_create_gatt_table,
5251da177e4SLinus Torvalds 	.free_gatt_table	= uninorth_free_gatt_table,
5261da177e4SLinus Torvalds 	.insert_memory		= uninorth_insert_memory,
52737580f3fSMichel Dänzer 	.remove_memory		= uninorth_remove_memory,
5281da177e4SLinus Torvalds 	.alloc_by_type		= agp_generic_alloc_by_type,
5291da177e4SLinus Torvalds 	.free_by_type		= agp_generic_free_by_type,
5301da177e4SLinus Torvalds 	.agp_alloc_page		= agp_generic_alloc_page,
5315f310b63SRene Herman 	.agp_alloc_pages	= agp_generic_alloc_pages,
5321da177e4SLinus Torvalds 	.agp_destroy_page	= agp_generic_destroy_page,
5335f310b63SRene Herman 	.agp_destroy_pages	= agp_generic_destroy_pages,
534a030ce44SThomas Hellstrom 	.agp_type_to_mask_type  = agp_generic_type_to_mask_type,
535c7258012SJoe Perches 	.cant_use_aperture	= true,
53661cf0593SJerome Glisse 	.needs_scratch_page	= true,
5371da177e4SLinus Torvalds };
5381da177e4SLinus Torvalds 
539e047d1cfSRyusuke Konishi const struct agp_bridge_driver u3_agp_driver = {
5401da177e4SLinus Torvalds 	.owner			= THIS_MODULE,
5411da177e4SLinus Torvalds 	.aperture_sizes		= (void *)u3_sizes,
5421da177e4SLinus Torvalds 	.size_type		= U32_APER_SIZE,
54352f072cbSMichel Dänzer 	.num_aperture_sizes	= ARRAY_SIZE(u3_sizes),
5441da177e4SLinus Torvalds 	.configure		= uninorth_configure,
5451da177e4SLinus Torvalds 	.fetch_size		= uninorth_fetch_size,
5461da177e4SLinus Torvalds 	.cleanup		= uninorth_cleanup,
5471da177e4SLinus Torvalds 	.tlb_flush		= uninorth_tlbflush,
5481da177e4SLinus Torvalds 	.mask_memory		= agp_generic_mask_memory,
5491da177e4SLinus Torvalds 	.masks			= NULL,
5501da177e4SLinus Torvalds 	.cache_flush		= null_cache_flush,
5511da177e4SLinus Torvalds 	.agp_enable		= uninorth_agp_enable,
5521da177e4SLinus Torvalds 	.create_gatt_table	= uninorth_create_gatt_table,
5531da177e4SLinus Torvalds 	.free_gatt_table	= uninorth_free_gatt_table,
55437580f3fSMichel Dänzer 	.insert_memory		= uninorth_insert_memory,
55537580f3fSMichel Dänzer 	.remove_memory		= uninorth_remove_memory,
5561da177e4SLinus Torvalds 	.alloc_by_type		= agp_generic_alloc_by_type,
5571da177e4SLinus Torvalds 	.free_by_type		= agp_generic_free_by_type,
5581da177e4SLinus Torvalds 	.agp_alloc_page		= agp_generic_alloc_page,
5595f310b63SRene Herman 	.agp_alloc_pages	= agp_generic_alloc_pages,
5601da177e4SLinus Torvalds 	.agp_destroy_page	= agp_generic_destroy_page,
561c09ff7e1SStephen Rothwell 	.agp_destroy_pages	= agp_generic_destroy_pages,
562a030ce44SThomas Hellstrom 	.agp_type_to_mask_type  = agp_generic_type_to_mask_type,
563c7258012SJoe Perches 	.cant_use_aperture	= true,
564c7258012SJoe Perches 	.needs_scratch_page	= true,
5651da177e4SLinus Torvalds };
5661da177e4SLinus Torvalds 
5670bbed20eSBill Pemberton static struct agp_device_ids uninorth_agp_device_ids[] = {
5681da177e4SLinus Torvalds 	{
5691da177e4SLinus Torvalds 		.device_id	= PCI_DEVICE_ID_APPLE_UNI_N_AGP,
5701da177e4SLinus Torvalds 		.chipset_name	= "UniNorth",
5711da177e4SLinus Torvalds 	},
5721da177e4SLinus Torvalds 	{
5731da177e4SLinus Torvalds 		.device_id	= PCI_DEVICE_ID_APPLE_UNI_N_AGP_P,
5741da177e4SLinus Torvalds 		.chipset_name	= "UniNorth/Pangea",
5751da177e4SLinus Torvalds 	},
5761da177e4SLinus Torvalds 	{
5771da177e4SLinus Torvalds 		.device_id	= PCI_DEVICE_ID_APPLE_UNI_N_AGP15,
5781da177e4SLinus Torvalds 		.chipset_name	= "UniNorth 1.5",
5791da177e4SLinus Torvalds 	},
5801da177e4SLinus Torvalds 	{
5811da177e4SLinus Torvalds 		.device_id	= PCI_DEVICE_ID_APPLE_UNI_N_AGP2,
5821da177e4SLinus Torvalds 		.chipset_name	= "UniNorth 2",
5831da177e4SLinus Torvalds 	},
5841da177e4SLinus Torvalds 	{
5851da177e4SLinus Torvalds 		.device_id	= PCI_DEVICE_ID_APPLE_U3_AGP,
5861da177e4SLinus Torvalds 		.chipset_name	= "U3",
5871da177e4SLinus Torvalds 	},
5881da177e4SLinus Torvalds 	{
5891da177e4SLinus Torvalds 		.device_id	= PCI_DEVICE_ID_APPLE_U3L_AGP,
5901da177e4SLinus Torvalds 		.chipset_name	= "U3L",
5911da177e4SLinus Torvalds 	},
5921da177e4SLinus Torvalds 	{
5931da177e4SLinus Torvalds 		.device_id	= PCI_DEVICE_ID_APPLE_U3H_AGP,
5941da177e4SLinus Torvalds 		.chipset_name	= "U3H",
5951da177e4SLinus Torvalds 	},
5967fce260aSOlof Johansson 	{
5977fce260aSOlof Johansson 		.device_id	= PCI_DEVICE_ID_APPLE_IPID2_AGP,
5987fce260aSOlof Johansson 		.chipset_name	= "UniNorth/Intrepid2",
5997fce260aSOlof Johansson 	},
6001da177e4SLinus Torvalds };
6011da177e4SLinus Torvalds 
agp_uninorth_probe(struct pci_dev * pdev,const struct pci_device_id * ent)602bcd2982aSGreg Kroah-Hartman static int agp_uninorth_probe(struct pci_dev *pdev,
6031da177e4SLinus Torvalds 			      const struct pci_device_id *ent)
6041da177e4SLinus Torvalds {
6051da177e4SLinus Torvalds 	struct agp_device_ids *devs = uninorth_agp_device_ids;
6061da177e4SLinus Torvalds 	struct agp_bridge_data *bridge;
6071da177e4SLinus Torvalds 	struct device_node *uninorth_node;
6081da177e4SLinus Torvalds 	u8 cap_ptr;
6091da177e4SLinus Torvalds 	int j;
6101da177e4SLinus Torvalds 
6111da177e4SLinus Torvalds 	cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP);
6121da177e4SLinus Torvalds 	if (cap_ptr == 0)
6131da177e4SLinus Torvalds 		return -ENODEV;
6141da177e4SLinus Torvalds 
6151da177e4SLinus Torvalds 	/* probe for known chipsets */
6161da177e4SLinus Torvalds 	for (j = 0; devs[j].chipset_name != NULL; ++j) {
6171da177e4SLinus Torvalds 		if (pdev->device == devs[j].device_id) {
618e3cf6951SBjorn Helgaas 			dev_info(&pdev->dev, "Apple %s chipset\n",
6191da177e4SLinus Torvalds 				 devs[j].chipset_name);
6201da177e4SLinus Torvalds 			goto found;
6211da177e4SLinus Torvalds 		}
6221da177e4SLinus Torvalds 	}
6231da177e4SLinus Torvalds 
624e3cf6951SBjorn Helgaas 	dev_err(&pdev->dev, "unsupported Apple chipset [%04x/%04x]\n",
625e3cf6951SBjorn Helgaas 		pdev->vendor, pdev->device);
6261da177e4SLinus Torvalds 	return -ENODEV;
6271da177e4SLinus Torvalds 
6281da177e4SLinus Torvalds  found:
6291da177e4SLinus Torvalds 	/* Set revision to 0 if we could not read it. */
6301da177e4SLinus Torvalds 	uninorth_rev = 0;
6311da177e4SLinus Torvalds 	is_u3 = 0;
6321da177e4SLinus Torvalds 	/* Locate core99 Uni-N */
6331da177e4SLinus Torvalds 	uninorth_node = of_find_node_by_name(NULL, "uni-n");
6341da177e4SLinus Torvalds 	/* Locate G5 u3 */
6351da177e4SLinus Torvalds 	if (uninorth_node == NULL) {
6361da177e4SLinus Torvalds 		is_u3 = 1;
6371da177e4SLinus Torvalds 		uninorth_node = of_find_node_by_name(NULL, "u3");
6381da177e4SLinus Torvalds 	}
6391da177e4SLinus Torvalds 	if (uninorth_node) {
64040cd3a45SStephen Rothwell 		const int *revprop = of_get_property(uninorth_node,
641b04e3dd4SJeremy Kerr 				"device-rev", NULL);
6421da177e4SLinus Torvalds 		if (revprop != NULL)
6431da177e4SLinus Torvalds 			uninorth_rev = *revprop & 0x3f;
6441da177e4SLinus Torvalds 		of_node_put(uninorth_node);
6451da177e4SLinus Torvalds 	}
6461da177e4SLinus Torvalds 
6470c541b44SBenjamin Herrenschmidt #ifdef CONFIG_PM
6480c541b44SBenjamin Herrenschmidt 	/* Inform platform of our suspend/resume caps */
6490c541b44SBenjamin Herrenschmidt 	pmac_register_agp_pm(pdev, agp_uninorth_suspend, agp_uninorth_resume);
6500c541b44SBenjamin Herrenschmidt #endif
6510c541b44SBenjamin Herrenschmidt 
6520c541b44SBenjamin Herrenschmidt 	/* Allocate & setup our driver */
6531da177e4SLinus Torvalds 	bridge = agp_alloc_bridge();
6541da177e4SLinus Torvalds 	if (!bridge)
6551da177e4SLinus Torvalds 		return -ENOMEM;
6561da177e4SLinus Torvalds 
6571da177e4SLinus Torvalds 	if (is_u3)
6581da177e4SLinus Torvalds 		bridge->driver = &u3_agp_driver;
6591da177e4SLinus Torvalds 	else
6601da177e4SLinus Torvalds 		bridge->driver = &uninorth_agp_driver;
6611da177e4SLinus Torvalds 
6621da177e4SLinus Torvalds 	bridge->dev = pdev;
6631da177e4SLinus Torvalds 	bridge->capndx = cap_ptr;
6641da177e4SLinus Torvalds 	bridge->flags = AGP_ERRATA_FASTWRITES;
6651da177e4SLinus Torvalds 
6661da177e4SLinus Torvalds 	/* Fill in the mode register */
6671da177e4SLinus Torvalds 	pci_read_config_dword(pdev, cap_ptr+PCI_AGP_STATUS, &bridge->mode);
6681da177e4SLinus Torvalds 
6691da177e4SLinus Torvalds 	pci_set_drvdata(pdev, bridge);
6701da177e4SLinus Torvalds 	return agp_add_bridge(bridge);
6711da177e4SLinus Torvalds }
6721da177e4SLinus Torvalds 
agp_uninorth_remove(struct pci_dev * pdev)67339af33fcSBill Pemberton static void agp_uninorth_remove(struct pci_dev *pdev)
6741da177e4SLinus Torvalds {
6751da177e4SLinus Torvalds 	struct agp_bridge_data *bridge = pci_get_drvdata(pdev);
6761da177e4SLinus Torvalds 
6770c541b44SBenjamin Herrenschmidt #ifdef CONFIG_PM
6780c541b44SBenjamin Herrenschmidt 	/* Inform platform of our suspend/resume caps */
6790c541b44SBenjamin Herrenschmidt 	pmac_register_agp_pm(pdev, NULL, NULL);
6800c541b44SBenjamin Herrenschmidt #endif
6810c541b44SBenjamin Herrenschmidt 
6821da177e4SLinus Torvalds 	agp_remove_bridge(bridge);
6831da177e4SLinus Torvalds 	agp_put_bridge(bridge);
6841da177e4SLinus Torvalds }
6851da177e4SLinus Torvalds 
686ba67a31aSArvind Yadav static const struct pci_device_id agp_uninorth_pci_table[] = {
6871da177e4SLinus Torvalds 	{
6881da177e4SLinus Torvalds 	.class		= (PCI_CLASS_BRIDGE_HOST << 8),
6891da177e4SLinus Torvalds 	.class_mask	= ~0,
6901da177e4SLinus Torvalds 	.vendor		= PCI_VENDOR_ID_APPLE,
6911da177e4SLinus Torvalds 	.device		= PCI_ANY_ID,
6921da177e4SLinus Torvalds 	.subvendor	= PCI_ANY_ID,
6931da177e4SLinus Torvalds 	.subdevice	= PCI_ANY_ID,
6941da177e4SLinus Torvalds 	},
6951da177e4SLinus Torvalds 	{ }
6961da177e4SLinus Torvalds };
6971da177e4SLinus Torvalds 
6981da177e4SLinus Torvalds MODULE_DEVICE_TABLE(pci, agp_uninorth_pci_table);
6991da177e4SLinus Torvalds 
7001da177e4SLinus Torvalds static struct pci_driver agp_uninorth_pci_driver = {
7011da177e4SLinus Torvalds 	.name		= "agpgart-uninorth",
7021da177e4SLinus Torvalds 	.id_table	= agp_uninorth_pci_table,
7031da177e4SLinus Torvalds 	.probe		= agp_uninorth_probe,
7041da177e4SLinus Torvalds 	.remove		= agp_uninorth_remove,
7051da177e4SLinus Torvalds };
7061da177e4SLinus Torvalds 
agp_uninorth_init(void)7071da177e4SLinus Torvalds static int __init agp_uninorth_init(void)
7081da177e4SLinus Torvalds {
7091da177e4SLinus Torvalds 	if (agp_off)
7101da177e4SLinus Torvalds 		return -EINVAL;
7111da177e4SLinus Torvalds 	return pci_register_driver(&agp_uninorth_pci_driver);
7121da177e4SLinus Torvalds }
7131da177e4SLinus Torvalds 
agp_uninorth_cleanup(void)7141da177e4SLinus Torvalds static void __exit agp_uninorth_cleanup(void)
7151da177e4SLinus Torvalds {
7161da177e4SLinus Torvalds 	pci_unregister_driver(&agp_uninorth_pci_driver);
7171da177e4SLinus Torvalds }
7181da177e4SLinus Torvalds 
7191da177e4SLinus Torvalds module_init(agp_uninorth_init);
7201da177e4SLinus Torvalds module_exit(agp_uninorth_cleanup);
7211da177e4SLinus Torvalds 
72218088748SMichel Dänzer module_param(aperture, charp, 0);
72318088748SMichel Dänzer MODULE_PARM_DESC(aperture,
72418088748SMichel Dänzer 		 "Aperture size, must be power of two between 4MB and an\n"
72518088748SMichel Dänzer 		 "\t\tupper limit specific to the UniNorth revision.\n"
72652f072cbSMichel Dänzer 		 "\t\tDefault: " DEFAULT_APERTURE_STRING "M");
72718088748SMichel Dänzer 
7281da177e4SLinus Torvalds MODULE_AUTHOR("Ben Herrenschmidt & Paul Mackerras");
7291da177e4SLinus Torvalds MODULE_LICENSE("GPL");
730