162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * UniNorth AGPGART routines.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci#include <linux/module.h>
662306a36Sopenharmony_ci#include <linux/of.h>
762306a36Sopenharmony_ci#include <linux/pci.h>
862306a36Sopenharmony_ci#include <linux/slab.h>
962306a36Sopenharmony_ci#include <linux/init.h>
1062306a36Sopenharmony_ci#include <linux/pagemap.h>
1162306a36Sopenharmony_ci#include <linux/agp_backend.h>
1262306a36Sopenharmony_ci#include <linux/delay.h>
1362306a36Sopenharmony_ci#include <linux/vmalloc.h>
1462306a36Sopenharmony_ci#include <asm/uninorth.h>
1562306a36Sopenharmony_ci#include <asm/prom.h>
1662306a36Sopenharmony_ci#include <asm/pmac_feature.h>
1762306a36Sopenharmony_ci#include "agp.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci/*
2062306a36Sopenharmony_ci * NOTES for uninorth3 (G5 AGP) supports :
2162306a36Sopenharmony_ci *
2262306a36Sopenharmony_ci * There maybe also possibility to have bigger cache line size for
2362306a36Sopenharmony_ci * agp (see pmac_pci.c and look for cache line). Need to be investigated
2462306a36Sopenharmony_ci * by someone.
2562306a36Sopenharmony_ci *
2662306a36Sopenharmony_ci * PAGE size are hardcoded but this may change, see asm/page.h.
2762306a36Sopenharmony_ci *
2862306a36Sopenharmony_ci * Jerome Glisse <j.glisse@gmail.com>
2962306a36Sopenharmony_ci */
3062306a36Sopenharmony_cistatic int uninorth_rev;
3162306a36Sopenharmony_cistatic int is_u3;
3262306a36Sopenharmony_cistatic u32 scratch_value;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#define DEFAULT_APERTURE_SIZE 256
3562306a36Sopenharmony_ci#define DEFAULT_APERTURE_STRING "256"
3662306a36Sopenharmony_cistatic char *aperture = NULL;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic int uninorth_fetch_size(void)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	int i, size = 0;
4162306a36Sopenharmony_ci	struct aper_size_info_32 *values =
4262306a36Sopenharmony_ci	    A_SIZE_32(agp_bridge->driver->aperture_sizes);
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	if (aperture) {
4562306a36Sopenharmony_ci		char *save = aperture;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci		size = memparse(aperture, &aperture) >> 20;
4862306a36Sopenharmony_ci		aperture = save;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci		for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++)
5162306a36Sopenharmony_ci			if (size == values[i].size)
5262306a36Sopenharmony_ci				break;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci		if (i == agp_bridge->driver->num_aperture_sizes) {
5562306a36Sopenharmony_ci			dev_err(&agp_bridge->dev->dev, "invalid aperture size, "
5662306a36Sopenharmony_ci				"using default\n");
5762306a36Sopenharmony_ci			size = 0;
5862306a36Sopenharmony_ci			aperture = NULL;
5962306a36Sopenharmony_ci		}
6062306a36Sopenharmony_ci	}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	if (!size) {
6362306a36Sopenharmony_ci		for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++)
6462306a36Sopenharmony_ci			if (values[i].size == DEFAULT_APERTURE_SIZE)
6562306a36Sopenharmony_ci				break;
6662306a36Sopenharmony_ci	}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	agp_bridge->previous_size =
6962306a36Sopenharmony_ci	    agp_bridge->current_size = (void *)(values + i);
7062306a36Sopenharmony_ci	agp_bridge->aperture_size_idx = i;
7162306a36Sopenharmony_ci	return values[i].size;
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic void uninorth_tlbflush(struct agp_memory *mem)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	u32 ctrl = UNI_N_CFG_GART_ENABLE;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	if (is_u3)
7962306a36Sopenharmony_ci		ctrl |= U3_N_CFG_GART_PERFRD;
8062306a36Sopenharmony_ci	pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL,
8162306a36Sopenharmony_ci			       ctrl | UNI_N_CFG_GART_INVAL);
8262306a36Sopenharmony_ci	pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL, ctrl);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	if (!mem && uninorth_rev <= 0x30) {
8562306a36Sopenharmony_ci		pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL,
8662306a36Sopenharmony_ci				       ctrl | UNI_N_CFG_GART_2xRESET);
8762306a36Sopenharmony_ci		pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL,
8862306a36Sopenharmony_ci				       ctrl);
8962306a36Sopenharmony_ci	}
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic void uninorth_cleanup(void)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	u32 tmp;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	pci_read_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL, &tmp);
9762306a36Sopenharmony_ci	if (!(tmp & UNI_N_CFG_GART_ENABLE))
9862306a36Sopenharmony_ci		return;
9962306a36Sopenharmony_ci	tmp |= UNI_N_CFG_GART_INVAL;
10062306a36Sopenharmony_ci	pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL, tmp);
10162306a36Sopenharmony_ci	pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL, 0);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	if (uninorth_rev <= 0x30) {
10462306a36Sopenharmony_ci		pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL,
10562306a36Sopenharmony_ci				       UNI_N_CFG_GART_2xRESET);
10662306a36Sopenharmony_ci		pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL,
10762306a36Sopenharmony_ci				       0);
10862306a36Sopenharmony_ci	}
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic int uninorth_configure(void)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	struct aper_size_info_32 *current_size;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	current_size = A_SIZE_32(agp_bridge->current_size);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	dev_info(&agp_bridge->dev->dev, "configuring for size idx: %d\n",
11862306a36Sopenharmony_ci		 current_size->size_value);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	/* aperture size and gatt addr */
12162306a36Sopenharmony_ci	pci_write_config_dword(agp_bridge->dev,
12262306a36Sopenharmony_ci		UNI_N_CFG_GART_BASE,
12362306a36Sopenharmony_ci		(agp_bridge->gatt_bus_addr & 0xfffff000)
12462306a36Sopenharmony_ci			| current_size->size_value);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	/* HACK ALERT
12762306a36Sopenharmony_ci	 * UniNorth seem to be buggy enough not to handle properly when
12862306a36Sopenharmony_ci	 * the AGP aperture isn't mapped at bus physical address 0
12962306a36Sopenharmony_ci	 */
13062306a36Sopenharmony_ci	agp_bridge->gart_bus_addr = 0;
13162306a36Sopenharmony_ci#ifdef CONFIG_PPC64
13262306a36Sopenharmony_ci	/* Assume U3 or later on PPC64 systems */
13362306a36Sopenharmony_ci	/* high 4 bits of GART physical address go in UNI_N_CFG_AGP_BASE */
13462306a36Sopenharmony_ci	pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_AGP_BASE,
13562306a36Sopenharmony_ci			       (agp_bridge->gatt_bus_addr >> 32) & 0xf);
13662306a36Sopenharmony_ci#else
13762306a36Sopenharmony_ci	pci_write_config_dword(agp_bridge->dev,
13862306a36Sopenharmony_ci		UNI_N_CFG_AGP_BASE, agp_bridge->gart_bus_addr);
13962306a36Sopenharmony_ci#endif
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	if (is_u3) {
14262306a36Sopenharmony_ci		pci_write_config_dword(agp_bridge->dev,
14362306a36Sopenharmony_ci				       UNI_N_CFG_GART_DUMMY_PAGE,
14462306a36Sopenharmony_ci				       page_to_phys(agp_bridge->scratch_page_page) >> 12);
14562306a36Sopenharmony_ci	}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	return 0;
14862306a36Sopenharmony_ci}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cistatic int uninorth_insert_memory(struct agp_memory *mem, off_t pg_start, int type)
15162306a36Sopenharmony_ci{
15262306a36Sopenharmony_ci	int i, num_entries;
15362306a36Sopenharmony_ci	void *temp;
15462306a36Sopenharmony_ci	u32 *gp;
15562306a36Sopenharmony_ci	int mask_type;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	if (type != mem->type)
15862306a36Sopenharmony_ci		return -EINVAL;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	mask_type = agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type);
16162306a36Sopenharmony_ci	if (mask_type != 0) {
16262306a36Sopenharmony_ci		/* We know nothing of memory types */
16362306a36Sopenharmony_ci		return -EINVAL;
16462306a36Sopenharmony_ci	}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	if (mem->page_count == 0)
16762306a36Sopenharmony_ci		return 0;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	temp = agp_bridge->current_size;
17062306a36Sopenharmony_ci	num_entries = A_SIZE_32(temp)->num_entries;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	if ((pg_start + mem->page_count) > num_entries)
17362306a36Sopenharmony_ci		return -EINVAL;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	gp = (u32 *) &agp_bridge->gatt_table[pg_start];
17662306a36Sopenharmony_ci	for (i = 0; i < mem->page_count; ++i) {
17762306a36Sopenharmony_ci		if (gp[i] != scratch_value) {
17862306a36Sopenharmony_ci			dev_info(&agp_bridge->dev->dev,
17962306a36Sopenharmony_ci				 "uninorth_insert_memory: entry 0x%x occupied (%x)\n",
18062306a36Sopenharmony_ci				 i, gp[i]);
18162306a36Sopenharmony_ci			return -EBUSY;
18262306a36Sopenharmony_ci		}
18362306a36Sopenharmony_ci	}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	for (i = 0; i < mem->page_count; i++) {
18662306a36Sopenharmony_ci		if (is_u3)
18762306a36Sopenharmony_ci			gp[i] = (page_to_phys(mem->pages[i]) >> PAGE_SHIFT) | 0x80000000UL;
18862306a36Sopenharmony_ci		else
18962306a36Sopenharmony_ci			gp[i] =	cpu_to_le32((page_to_phys(mem->pages[i]) & 0xFFFFF000UL) |
19062306a36Sopenharmony_ci					    0x1UL);
19162306a36Sopenharmony_ci		flush_dcache_range((unsigned long)__va(page_to_phys(mem->pages[i])),
19262306a36Sopenharmony_ci				   (unsigned long)__va(page_to_phys(mem->pages[i]))+0x1000);
19362306a36Sopenharmony_ci	}
19462306a36Sopenharmony_ci	mb();
19562306a36Sopenharmony_ci	uninorth_tlbflush(mem);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	return 0;
19862306a36Sopenharmony_ci}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_cistatic int uninorth_remove_memory(struct agp_memory *mem, off_t pg_start, int type)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	size_t i;
20362306a36Sopenharmony_ci	u32 *gp;
20462306a36Sopenharmony_ci	int mask_type;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	if (type != mem->type)
20762306a36Sopenharmony_ci		return -EINVAL;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	mask_type = agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type);
21062306a36Sopenharmony_ci	if (mask_type != 0) {
21162306a36Sopenharmony_ci		/* We know nothing of memory types */
21262306a36Sopenharmony_ci		return -EINVAL;
21362306a36Sopenharmony_ci	}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	if (mem->page_count == 0)
21662306a36Sopenharmony_ci		return 0;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	gp = (u32 *) &agp_bridge->gatt_table[pg_start];
21962306a36Sopenharmony_ci	for (i = 0; i < mem->page_count; ++i) {
22062306a36Sopenharmony_ci		gp[i] = scratch_value;
22162306a36Sopenharmony_ci	}
22262306a36Sopenharmony_ci	mb();
22362306a36Sopenharmony_ci	uninorth_tlbflush(mem);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	return 0;
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_cistatic void uninorth_agp_enable(struct agp_bridge_data *bridge, u32 mode)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	u32 command, scratch, status;
23162306a36Sopenharmony_ci	int timeout;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	pci_read_config_dword(bridge->dev,
23462306a36Sopenharmony_ci			      bridge->capndx + PCI_AGP_STATUS,
23562306a36Sopenharmony_ci			      &status);
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	command = agp_collect_device_status(bridge, mode, status);
23862306a36Sopenharmony_ci	command |= PCI_AGP_COMMAND_AGP;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	if (uninorth_rev == 0x21) {
24162306a36Sopenharmony_ci		/*
24262306a36Sopenharmony_ci		 * Darwin disable AGP 4x on this revision, thus we
24362306a36Sopenharmony_ci		 * may assume it's broken. This is an AGP2 controller.
24462306a36Sopenharmony_ci		 */
24562306a36Sopenharmony_ci		command &= ~AGPSTAT2_4X;
24662306a36Sopenharmony_ci	}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	if ((uninorth_rev >= 0x30) && (uninorth_rev <= 0x33)) {
24962306a36Sopenharmony_ci		/*
25062306a36Sopenharmony_ci		 * We need to set REQ_DEPTH to 7 for U3 versions 1.0, 2.1,
25162306a36Sopenharmony_ci		 * 2.2 and 2.3, Darwin do so.
25262306a36Sopenharmony_ci		 */
25362306a36Sopenharmony_ci		if ((command >> AGPSTAT_RQ_DEPTH_SHIFT) > 7)
25462306a36Sopenharmony_ci			command = (command & ~AGPSTAT_RQ_DEPTH)
25562306a36Sopenharmony_ci				| (7 << AGPSTAT_RQ_DEPTH_SHIFT);
25662306a36Sopenharmony_ci	}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	uninorth_tlbflush(NULL);
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	timeout = 0;
26162306a36Sopenharmony_ci	do {
26262306a36Sopenharmony_ci		pci_write_config_dword(bridge->dev,
26362306a36Sopenharmony_ci				       bridge->capndx + PCI_AGP_COMMAND,
26462306a36Sopenharmony_ci				       command);
26562306a36Sopenharmony_ci		pci_read_config_dword(bridge->dev,
26662306a36Sopenharmony_ci				      bridge->capndx + PCI_AGP_COMMAND,
26762306a36Sopenharmony_ci				       &scratch);
26862306a36Sopenharmony_ci	} while ((scratch & PCI_AGP_COMMAND_AGP) == 0 && ++timeout < 1000);
26962306a36Sopenharmony_ci	if ((scratch & PCI_AGP_COMMAND_AGP) == 0)
27062306a36Sopenharmony_ci		dev_err(&bridge->dev->dev, "can't write UniNorth AGP "
27162306a36Sopenharmony_ci			"command register\n");
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	if (uninorth_rev >= 0x30) {
27462306a36Sopenharmony_ci		/* This is an AGP V3 */
27562306a36Sopenharmony_ci		agp_device_command(command, (status & AGPSTAT_MODE_3_0) != 0);
27662306a36Sopenharmony_ci	} else {
27762306a36Sopenharmony_ci		/* AGP V2 */
27862306a36Sopenharmony_ci		agp_device_command(command, false);
27962306a36Sopenharmony_ci	}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	uninorth_tlbflush(NULL);
28262306a36Sopenharmony_ci}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci#ifdef CONFIG_PM
28562306a36Sopenharmony_ci/*
28662306a36Sopenharmony_ci * These Power Management routines are _not_ called by the normal PCI PM layer,
28762306a36Sopenharmony_ci * but directly by the video driver through function pointers in the device
28862306a36Sopenharmony_ci * tree.
28962306a36Sopenharmony_ci */
29062306a36Sopenharmony_cistatic int agp_uninorth_suspend(struct pci_dev *pdev)
29162306a36Sopenharmony_ci{
29262306a36Sopenharmony_ci	struct agp_bridge_data *bridge;
29362306a36Sopenharmony_ci	u32 cmd;
29462306a36Sopenharmony_ci	u8 agp;
29562306a36Sopenharmony_ci	struct pci_dev *device = NULL;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	bridge = agp_find_bridge(pdev);
29862306a36Sopenharmony_ci	if (bridge == NULL)
29962306a36Sopenharmony_ci		return -ENODEV;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	/* Only one suspend supported */
30262306a36Sopenharmony_ci	if (bridge->dev_private_data)
30362306a36Sopenharmony_ci		return 0;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	/* turn off AGP on the video chip, if it was enabled */
30662306a36Sopenharmony_ci	for_each_pci_dev(device) {
30762306a36Sopenharmony_ci		/* Don't touch the bridge yet, device first */
30862306a36Sopenharmony_ci		if (device == pdev)
30962306a36Sopenharmony_ci			continue;
31062306a36Sopenharmony_ci		/* Only deal with devices on the same bus here, no Mac has a P2P
31162306a36Sopenharmony_ci		 * bridge on the AGP port, and mucking around the entire PCI
31262306a36Sopenharmony_ci		 * tree is source of problems on some machines because of a bug
31362306a36Sopenharmony_ci		 * in some versions of pci_find_capability() when hitting a dead
31462306a36Sopenharmony_ci		 * device
31562306a36Sopenharmony_ci		 */
31662306a36Sopenharmony_ci		if (device->bus != pdev->bus)
31762306a36Sopenharmony_ci			continue;
31862306a36Sopenharmony_ci		agp = pci_find_capability(device, PCI_CAP_ID_AGP);
31962306a36Sopenharmony_ci		if (!agp)
32062306a36Sopenharmony_ci			continue;
32162306a36Sopenharmony_ci		pci_read_config_dword(device, agp + PCI_AGP_COMMAND, &cmd);
32262306a36Sopenharmony_ci		if (!(cmd & PCI_AGP_COMMAND_AGP))
32362306a36Sopenharmony_ci			continue;
32462306a36Sopenharmony_ci		dev_info(&pdev->dev, "disabling AGP on device %s\n",
32562306a36Sopenharmony_ci			 pci_name(device));
32662306a36Sopenharmony_ci		cmd &= ~PCI_AGP_COMMAND_AGP;
32762306a36Sopenharmony_ci		pci_write_config_dword(device, agp + PCI_AGP_COMMAND, cmd);
32862306a36Sopenharmony_ci	}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	/* turn off AGP on the bridge */
33162306a36Sopenharmony_ci	agp = pci_find_capability(pdev, PCI_CAP_ID_AGP);
33262306a36Sopenharmony_ci	pci_read_config_dword(pdev, agp + PCI_AGP_COMMAND, &cmd);
33362306a36Sopenharmony_ci	bridge->dev_private_data = (void *)(long)cmd;
33462306a36Sopenharmony_ci	if (cmd & PCI_AGP_COMMAND_AGP) {
33562306a36Sopenharmony_ci		dev_info(&pdev->dev, "disabling AGP on bridge\n");
33662306a36Sopenharmony_ci		cmd &= ~PCI_AGP_COMMAND_AGP;
33762306a36Sopenharmony_ci		pci_write_config_dword(pdev, agp + PCI_AGP_COMMAND, cmd);
33862306a36Sopenharmony_ci	}
33962306a36Sopenharmony_ci	/* turn off the GART */
34062306a36Sopenharmony_ci	uninorth_cleanup();
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	return 0;
34362306a36Sopenharmony_ci}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_cistatic int agp_uninorth_resume(struct pci_dev *pdev)
34662306a36Sopenharmony_ci{
34762306a36Sopenharmony_ci	struct agp_bridge_data *bridge;
34862306a36Sopenharmony_ci	u32 command;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	bridge = agp_find_bridge(pdev);
35162306a36Sopenharmony_ci	if (bridge == NULL)
35262306a36Sopenharmony_ci		return -ENODEV;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	command = (long)bridge->dev_private_data;
35562306a36Sopenharmony_ci	bridge->dev_private_data = NULL;
35662306a36Sopenharmony_ci	if (!(command & PCI_AGP_COMMAND_AGP))
35762306a36Sopenharmony_ci		return 0;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	uninorth_agp_enable(bridge, command);
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	return 0;
36262306a36Sopenharmony_ci}
36362306a36Sopenharmony_ci#endif /* CONFIG_PM */
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_cistatic struct {
36662306a36Sopenharmony_ci	struct page **pages_arr;
36762306a36Sopenharmony_ci} uninorth_priv;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_cistatic int uninorth_create_gatt_table(struct agp_bridge_data *bridge)
37062306a36Sopenharmony_ci{
37162306a36Sopenharmony_ci	char *table;
37262306a36Sopenharmony_ci	char *table_end;
37362306a36Sopenharmony_ci	int size;
37462306a36Sopenharmony_ci	int page_order;
37562306a36Sopenharmony_ci	int num_entries;
37662306a36Sopenharmony_ci	int i;
37762306a36Sopenharmony_ci	void *temp;
37862306a36Sopenharmony_ci	struct page *page;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	/* We can't handle 2 level gatt's */
38162306a36Sopenharmony_ci	if (bridge->driver->size_type == LVL2_APER_SIZE)
38262306a36Sopenharmony_ci		return -EINVAL;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	table = NULL;
38562306a36Sopenharmony_ci	i = bridge->aperture_size_idx;
38662306a36Sopenharmony_ci	temp = bridge->current_size;
38762306a36Sopenharmony_ci	size = page_order = num_entries = 0;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	do {
39062306a36Sopenharmony_ci		size = A_SIZE_32(temp)->size;
39162306a36Sopenharmony_ci		page_order = A_SIZE_32(temp)->page_order;
39262306a36Sopenharmony_ci		num_entries = A_SIZE_32(temp)->num_entries;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci		table = (char *) __get_free_pages(GFP_KERNEL, page_order);
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci		if (table == NULL) {
39762306a36Sopenharmony_ci			i++;
39862306a36Sopenharmony_ci			bridge->current_size = A_IDX32(bridge);
39962306a36Sopenharmony_ci		} else {
40062306a36Sopenharmony_ci			bridge->aperture_size_idx = i;
40162306a36Sopenharmony_ci		}
40262306a36Sopenharmony_ci	} while (!table && (i < bridge->driver->num_aperture_sizes));
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	if (table == NULL)
40562306a36Sopenharmony_ci		return -ENOMEM;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	uninorth_priv.pages_arr = kmalloc_array(1 << page_order,
40862306a36Sopenharmony_ci						sizeof(struct page *),
40962306a36Sopenharmony_ci						GFP_KERNEL);
41062306a36Sopenharmony_ci	if (uninorth_priv.pages_arr == NULL)
41162306a36Sopenharmony_ci		goto enomem;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1);
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	for (page = virt_to_page(table), i = 0; page <= virt_to_page(table_end);
41662306a36Sopenharmony_ci	     page++, i++) {
41762306a36Sopenharmony_ci		SetPageReserved(page);
41862306a36Sopenharmony_ci		uninorth_priv.pages_arr[i] = page;
41962306a36Sopenharmony_ci	}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	bridge->gatt_table_real = (u32 *) table;
42262306a36Sopenharmony_ci	/* Need to clear out any dirty data still sitting in caches */
42362306a36Sopenharmony_ci	flush_dcache_range((unsigned long)table,
42462306a36Sopenharmony_ci			   (unsigned long)table_end + 1);
42562306a36Sopenharmony_ci	bridge->gatt_table = vmap(uninorth_priv.pages_arr, (1 << page_order), 0, PAGE_KERNEL_NCG);
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	if (bridge->gatt_table == NULL)
42862306a36Sopenharmony_ci		goto enomem;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	bridge->gatt_bus_addr = virt_to_phys(table);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	if (is_u3)
43362306a36Sopenharmony_ci		scratch_value = (page_to_phys(agp_bridge->scratch_page_page) >> PAGE_SHIFT) | 0x80000000UL;
43462306a36Sopenharmony_ci	else
43562306a36Sopenharmony_ci		scratch_value =	cpu_to_le32((page_to_phys(agp_bridge->scratch_page_page) & 0xFFFFF000UL) |
43662306a36Sopenharmony_ci				0x1UL);
43762306a36Sopenharmony_ci	for (i = 0; i < num_entries; i++)
43862306a36Sopenharmony_ci		bridge->gatt_table[i] = scratch_value;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	return 0;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_cienomem:
44362306a36Sopenharmony_ci	kfree(uninorth_priv.pages_arr);
44462306a36Sopenharmony_ci	if (table)
44562306a36Sopenharmony_ci		free_pages((unsigned long)table, page_order);
44662306a36Sopenharmony_ci	return -ENOMEM;
44762306a36Sopenharmony_ci}
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_cistatic int uninorth_free_gatt_table(struct agp_bridge_data *bridge)
45062306a36Sopenharmony_ci{
45162306a36Sopenharmony_ci	int page_order;
45262306a36Sopenharmony_ci	char *table, *table_end;
45362306a36Sopenharmony_ci	void *temp;
45462306a36Sopenharmony_ci	struct page *page;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	temp = bridge->current_size;
45762306a36Sopenharmony_ci	page_order = A_SIZE_32(temp)->page_order;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	/* Do not worry about freeing memory, because if this is
46062306a36Sopenharmony_ci	 * called, then all agp memory is deallocated and removed
46162306a36Sopenharmony_ci	 * from the table.
46262306a36Sopenharmony_ci	 */
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	vunmap(bridge->gatt_table);
46562306a36Sopenharmony_ci	kfree(uninorth_priv.pages_arr);
46662306a36Sopenharmony_ci	table = (char *) bridge->gatt_table_real;
46762306a36Sopenharmony_ci	table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1);
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	for (page = virt_to_page(table); page <= virt_to_page(table_end); page++)
47062306a36Sopenharmony_ci		ClearPageReserved(page);
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	free_pages((unsigned long) bridge->gatt_table_real, page_order);
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	return 0;
47562306a36Sopenharmony_ci}
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_cistatic void null_cache_flush(void)
47862306a36Sopenharmony_ci{
47962306a36Sopenharmony_ci	mb();
48062306a36Sopenharmony_ci}
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci/* Setup function */
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_cistatic const struct aper_size_info_32 uninorth_sizes[] =
48562306a36Sopenharmony_ci{
48662306a36Sopenharmony_ci	{256, 65536, 6, 64},
48762306a36Sopenharmony_ci	{128, 32768, 5, 32},
48862306a36Sopenharmony_ci	{64, 16384, 4, 16},
48962306a36Sopenharmony_ci	{32, 8192, 3, 8},
49062306a36Sopenharmony_ci	{16, 4096, 2, 4},
49162306a36Sopenharmony_ci	{8, 2048, 1, 2},
49262306a36Sopenharmony_ci	{4, 1024, 0, 1}
49362306a36Sopenharmony_ci};
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci/*
49662306a36Sopenharmony_ci * Not sure that u3 supports that high aperture sizes but it
49762306a36Sopenharmony_ci * would strange if it did not :)
49862306a36Sopenharmony_ci */
49962306a36Sopenharmony_cistatic const struct aper_size_info_32 u3_sizes[] =
50062306a36Sopenharmony_ci{
50162306a36Sopenharmony_ci	{512, 131072, 7, 128},
50262306a36Sopenharmony_ci	{256, 65536, 6, 64},
50362306a36Sopenharmony_ci	{128, 32768, 5, 32},
50462306a36Sopenharmony_ci	{64, 16384, 4, 16},
50562306a36Sopenharmony_ci	{32, 8192, 3, 8},
50662306a36Sopenharmony_ci	{16, 4096, 2, 4},
50762306a36Sopenharmony_ci	{8, 2048, 1, 2},
50862306a36Sopenharmony_ci	{4, 1024, 0, 1}
50962306a36Sopenharmony_ci};
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ciconst struct agp_bridge_driver uninorth_agp_driver = {
51262306a36Sopenharmony_ci	.owner			= THIS_MODULE,
51362306a36Sopenharmony_ci	.aperture_sizes		= (void *)uninorth_sizes,
51462306a36Sopenharmony_ci	.size_type		= U32_APER_SIZE,
51562306a36Sopenharmony_ci	.num_aperture_sizes	= ARRAY_SIZE(uninorth_sizes),
51662306a36Sopenharmony_ci	.configure		= uninorth_configure,
51762306a36Sopenharmony_ci	.fetch_size		= uninorth_fetch_size,
51862306a36Sopenharmony_ci	.cleanup		= uninorth_cleanup,
51962306a36Sopenharmony_ci	.tlb_flush		= uninorth_tlbflush,
52062306a36Sopenharmony_ci	.mask_memory		= agp_generic_mask_memory,
52162306a36Sopenharmony_ci	.masks			= NULL,
52262306a36Sopenharmony_ci	.cache_flush		= null_cache_flush,
52362306a36Sopenharmony_ci	.agp_enable		= uninorth_agp_enable,
52462306a36Sopenharmony_ci	.create_gatt_table	= uninorth_create_gatt_table,
52562306a36Sopenharmony_ci	.free_gatt_table	= uninorth_free_gatt_table,
52662306a36Sopenharmony_ci	.insert_memory		= uninorth_insert_memory,
52762306a36Sopenharmony_ci	.remove_memory		= uninorth_remove_memory,
52862306a36Sopenharmony_ci	.alloc_by_type		= agp_generic_alloc_by_type,
52962306a36Sopenharmony_ci	.free_by_type		= agp_generic_free_by_type,
53062306a36Sopenharmony_ci	.agp_alloc_page		= agp_generic_alloc_page,
53162306a36Sopenharmony_ci	.agp_alloc_pages	= agp_generic_alloc_pages,
53262306a36Sopenharmony_ci	.agp_destroy_page	= agp_generic_destroy_page,
53362306a36Sopenharmony_ci	.agp_destroy_pages	= agp_generic_destroy_pages,
53462306a36Sopenharmony_ci	.agp_type_to_mask_type  = agp_generic_type_to_mask_type,
53562306a36Sopenharmony_ci	.cant_use_aperture	= true,
53662306a36Sopenharmony_ci	.needs_scratch_page	= true,
53762306a36Sopenharmony_ci};
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ciconst struct agp_bridge_driver u3_agp_driver = {
54062306a36Sopenharmony_ci	.owner			= THIS_MODULE,
54162306a36Sopenharmony_ci	.aperture_sizes		= (void *)u3_sizes,
54262306a36Sopenharmony_ci	.size_type		= U32_APER_SIZE,
54362306a36Sopenharmony_ci	.num_aperture_sizes	= ARRAY_SIZE(u3_sizes),
54462306a36Sopenharmony_ci	.configure		= uninorth_configure,
54562306a36Sopenharmony_ci	.fetch_size		= uninorth_fetch_size,
54662306a36Sopenharmony_ci	.cleanup		= uninorth_cleanup,
54762306a36Sopenharmony_ci	.tlb_flush		= uninorth_tlbflush,
54862306a36Sopenharmony_ci	.mask_memory		= agp_generic_mask_memory,
54962306a36Sopenharmony_ci	.masks			= NULL,
55062306a36Sopenharmony_ci	.cache_flush		= null_cache_flush,
55162306a36Sopenharmony_ci	.agp_enable		= uninorth_agp_enable,
55262306a36Sopenharmony_ci	.create_gatt_table	= uninorth_create_gatt_table,
55362306a36Sopenharmony_ci	.free_gatt_table	= uninorth_free_gatt_table,
55462306a36Sopenharmony_ci	.insert_memory		= uninorth_insert_memory,
55562306a36Sopenharmony_ci	.remove_memory		= uninorth_remove_memory,
55662306a36Sopenharmony_ci	.alloc_by_type		= agp_generic_alloc_by_type,
55762306a36Sopenharmony_ci	.free_by_type		= agp_generic_free_by_type,
55862306a36Sopenharmony_ci	.agp_alloc_page		= agp_generic_alloc_page,
55962306a36Sopenharmony_ci	.agp_alloc_pages	= agp_generic_alloc_pages,
56062306a36Sopenharmony_ci	.agp_destroy_page	= agp_generic_destroy_page,
56162306a36Sopenharmony_ci	.agp_destroy_pages	= agp_generic_destroy_pages,
56262306a36Sopenharmony_ci	.agp_type_to_mask_type  = agp_generic_type_to_mask_type,
56362306a36Sopenharmony_ci	.cant_use_aperture	= true,
56462306a36Sopenharmony_ci	.needs_scratch_page	= true,
56562306a36Sopenharmony_ci};
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_cistatic struct agp_device_ids uninorth_agp_device_ids[] = {
56862306a36Sopenharmony_ci	{
56962306a36Sopenharmony_ci		.device_id	= PCI_DEVICE_ID_APPLE_UNI_N_AGP,
57062306a36Sopenharmony_ci		.chipset_name	= "UniNorth",
57162306a36Sopenharmony_ci	},
57262306a36Sopenharmony_ci	{
57362306a36Sopenharmony_ci		.device_id	= PCI_DEVICE_ID_APPLE_UNI_N_AGP_P,
57462306a36Sopenharmony_ci		.chipset_name	= "UniNorth/Pangea",
57562306a36Sopenharmony_ci	},
57662306a36Sopenharmony_ci	{
57762306a36Sopenharmony_ci		.device_id	= PCI_DEVICE_ID_APPLE_UNI_N_AGP15,
57862306a36Sopenharmony_ci		.chipset_name	= "UniNorth 1.5",
57962306a36Sopenharmony_ci	},
58062306a36Sopenharmony_ci	{
58162306a36Sopenharmony_ci		.device_id	= PCI_DEVICE_ID_APPLE_UNI_N_AGP2,
58262306a36Sopenharmony_ci		.chipset_name	= "UniNorth 2",
58362306a36Sopenharmony_ci	},
58462306a36Sopenharmony_ci	{
58562306a36Sopenharmony_ci		.device_id	= PCI_DEVICE_ID_APPLE_U3_AGP,
58662306a36Sopenharmony_ci		.chipset_name	= "U3",
58762306a36Sopenharmony_ci	},
58862306a36Sopenharmony_ci	{
58962306a36Sopenharmony_ci		.device_id	= PCI_DEVICE_ID_APPLE_U3L_AGP,
59062306a36Sopenharmony_ci		.chipset_name	= "U3L",
59162306a36Sopenharmony_ci	},
59262306a36Sopenharmony_ci	{
59362306a36Sopenharmony_ci		.device_id	= PCI_DEVICE_ID_APPLE_U3H_AGP,
59462306a36Sopenharmony_ci		.chipset_name	= "U3H",
59562306a36Sopenharmony_ci	},
59662306a36Sopenharmony_ci	{
59762306a36Sopenharmony_ci		.device_id	= PCI_DEVICE_ID_APPLE_IPID2_AGP,
59862306a36Sopenharmony_ci		.chipset_name	= "UniNorth/Intrepid2",
59962306a36Sopenharmony_ci	},
60062306a36Sopenharmony_ci};
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_cistatic int agp_uninorth_probe(struct pci_dev *pdev,
60362306a36Sopenharmony_ci			      const struct pci_device_id *ent)
60462306a36Sopenharmony_ci{
60562306a36Sopenharmony_ci	struct agp_device_ids *devs = uninorth_agp_device_ids;
60662306a36Sopenharmony_ci	struct agp_bridge_data *bridge;
60762306a36Sopenharmony_ci	struct device_node *uninorth_node;
60862306a36Sopenharmony_ci	u8 cap_ptr;
60962306a36Sopenharmony_ci	int j;
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP);
61262306a36Sopenharmony_ci	if (cap_ptr == 0)
61362306a36Sopenharmony_ci		return -ENODEV;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	/* probe for known chipsets */
61662306a36Sopenharmony_ci	for (j = 0; devs[j].chipset_name != NULL; ++j) {
61762306a36Sopenharmony_ci		if (pdev->device == devs[j].device_id) {
61862306a36Sopenharmony_ci			dev_info(&pdev->dev, "Apple %s chipset\n",
61962306a36Sopenharmony_ci				 devs[j].chipset_name);
62062306a36Sopenharmony_ci			goto found;
62162306a36Sopenharmony_ci		}
62262306a36Sopenharmony_ci	}
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	dev_err(&pdev->dev, "unsupported Apple chipset [%04x/%04x]\n",
62562306a36Sopenharmony_ci		pdev->vendor, pdev->device);
62662306a36Sopenharmony_ci	return -ENODEV;
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci found:
62962306a36Sopenharmony_ci	/* Set revision to 0 if we could not read it. */
63062306a36Sopenharmony_ci	uninorth_rev = 0;
63162306a36Sopenharmony_ci	is_u3 = 0;
63262306a36Sopenharmony_ci	/* Locate core99 Uni-N */
63362306a36Sopenharmony_ci	uninorth_node = of_find_node_by_name(NULL, "uni-n");
63462306a36Sopenharmony_ci	/* Locate G5 u3 */
63562306a36Sopenharmony_ci	if (uninorth_node == NULL) {
63662306a36Sopenharmony_ci		is_u3 = 1;
63762306a36Sopenharmony_ci		uninorth_node = of_find_node_by_name(NULL, "u3");
63862306a36Sopenharmony_ci	}
63962306a36Sopenharmony_ci	if (uninorth_node) {
64062306a36Sopenharmony_ci		const int *revprop = of_get_property(uninorth_node,
64162306a36Sopenharmony_ci				"device-rev", NULL);
64262306a36Sopenharmony_ci		if (revprop != NULL)
64362306a36Sopenharmony_ci			uninorth_rev = *revprop & 0x3f;
64462306a36Sopenharmony_ci		of_node_put(uninorth_node);
64562306a36Sopenharmony_ci	}
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci#ifdef CONFIG_PM
64862306a36Sopenharmony_ci	/* Inform platform of our suspend/resume caps */
64962306a36Sopenharmony_ci	pmac_register_agp_pm(pdev, agp_uninorth_suspend, agp_uninorth_resume);
65062306a36Sopenharmony_ci#endif
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	/* Allocate & setup our driver */
65362306a36Sopenharmony_ci	bridge = agp_alloc_bridge();
65462306a36Sopenharmony_ci	if (!bridge)
65562306a36Sopenharmony_ci		return -ENOMEM;
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	if (is_u3)
65862306a36Sopenharmony_ci		bridge->driver = &u3_agp_driver;
65962306a36Sopenharmony_ci	else
66062306a36Sopenharmony_ci		bridge->driver = &uninorth_agp_driver;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	bridge->dev = pdev;
66362306a36Sopenharmony_ci	bridge->capndx = cap_ptr;
66462306a36Sopenharmony_ci	bridge->flags = AGP_ERRATA_FASTWRITES;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	/* Fill in the mode register */
66762306a36Sopenharmony_ci	pci_read_config_dword(pdev, cap_ptr+PCI_AGP_STATUS, &bridge->mode);
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	pci_set_drvdata(pdev, bridge);
67062306a36Sopenharmony_ci	return agp_add_bridge(bridge);
67162306a36Sopenharmony_ci}
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_cistatic void agp_uninorth_remove(struct pci_dev *pdev)
67462306a36Sopenharmony_ci{
67562306a36Sopenharmony_ci	struct agp_bridge_data *bridge = pci_get_drvdata(pdev);
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci#ifdef CONFIG_PM
67862306a36Sopenharmony_ci	/* Inform platform of our suspend/resume caps */
67962306a36Sopenharmony_ci	pmac_register_agp_pm(pdev, NULL, NULL);
68062306a36Sopenharmony_ci#endif
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	agp_remove_bridge(bridge);
68362306a36Sopenharmony_ci	agp_put_bridge(bridge);
68462306a36Sopenharmony_ci}
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_cistatic const struct pci_device_id agp_uninorth_pci_table[] = {
68762306a36Sopenharmony_ci	{
68862306a36Sopenharmony_ci	.class		= (PCI_CLASS_BRIDGE_HOST << 8),
68962306a36Sopenharmony_ci	.class_mask	= ~0,
69062306a36Sopenharmony_ci	.vendor		= PCI_VENDOR_ID_APPLE,
69162306a36Sopenharmony_ci	.device		= PCI_ANY_ID,
69262306a36Sopenharmony_ci	.subvendor	= PCI_ANY_ID,
69362306a36Sopenharmony_ci	.subdevice	= PCI_ANY_ID,
69462306a36Sopenharmony_ci	},
69562306a36Sopenharmony_ci	{ }
69662306a36Sopenharmony_ci};
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, agp_uninorth_pci_table);
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_cistatic struct pci_driver agp_uninorth_pci_driver = {
70162306a36Sopenharmony_ci	.name		= "agpgart-uninorth",
70262306a36Sopenharmony_ci	.id_table	= agp_uninorth_pci_table,
70362306a36Sopenharmony_ci	.probe		= agp_uninorth_probe,
70462306a36Sopenharmony_ci	.remove		= agp_uninorth_remove,
70562306a36Sopenharmony_ci};
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_cistatic int __init agp_uninorth_init(void)
70862306a36Sopenharmony_ci{
70962306a36Sopenharmony_ci	if (agp_off)
71062306a36Sopenharmony_ci		return -EINVAL;
71162306a36Sopenharmony_ci	return pci_register_driver(&agp_uninorth_pci_driver);
71262306a36Sopenharmony_ci}
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_cistatic void __exit agp_uninorth_cleanup(void)
71562306a36Sopenharmony_ci{
71662306a36Sopenharmony_ci	pci_unregister_driver(&agp_uninorth_pci_driver);
71762306a36Sopenharmony_ci}
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_cimodule_init(agp_uninorth_init);
72062306a36Sopenharmony_cimodule_exit(agp_uninorth_cleanup);
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_cimodule_param(aperture, charp, 0);
72362306a36Sopenharmony_ciMODULE_PARM_DESC(aperture,
72462306a36Sopenharmony_ci		 "Aperture size, must be power of two between 4MB and an\n"
72562306a36Sopenharmony_ci		 "\t\tupper limit specific to the UniNorth revision.\n"
72662306a36Sopenharmony_ci		 "\t\tDefault: " DEFAULT_APERTURE_STRING "M");
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ciMODULE_AUTHOR("Ben Herrenschmidt & Paul Mackerras");
72962306a36Sopenharmony_ciMODULE_LICENSE("GPL");
730