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