summaryrefslogtreecommitdiffstats
path: root/drivers/char/agp
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/char/agp
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'drivers/char/agp')
-rw-r--r--drivers/char/agp/Kconfig171
-rw-r--r--drivers/char/agp/Makefile18
-rw-r--r--drivers/char/agp/agp.h331
-rw-r--r--drivers/char/agp/ali-agp.c414
-rw-r--r--drivers/char/agp/alpha-agp.c216
-rw-r--r--drivers/char/agp/amd-k7-agp.c542
-rw-r--r--drivers/char/agp/amd64-agp.c761
-rw-r--r--drivers/char/agp/ati-agp.c548
-rw-r--r--drivers/char/agp/backend.c348
-rw-r--r--drivers/char/agp/efficeon-agp.c463
-rw-r--r--drivers/char/agp/frontend.c1103
-rw-r--r--drivers/char/agp/generic.c1222
-rw-r--r--drivers/char/agp/hp-agp.c552
-rw-r--r--drivers/char/agp/i460-agp.c642
-rw-r--r--drivers/char/agp/intel-agp.c1833
-rw-r--r--drivers/char/agp/isoch.c470
-rw-r--r--drivers/char/agp/nvidia-agp.c424
-rw-r--r--drivers/char/agp/sgi-agp.c331
-rw-r--r--drivers/char/agp/sis-agp.c360
-rw-r--r--drivers/char/agp/sworks-agp.c556
-rw-r--r--drivers/char/agp/uninorth-agp.c647
-rw-r--r--drivers/char/agp/via-agp.c548
22 files changed, 12500 insertions, 0 deletions
diff --git a/drivers/char/agp/Kconfig b/drivers/char/agp/Kconfig
new file mode 100644
index 00000000000..7f8c1b53b75
--- /dev/null
+++ b/drivers/char/agp/Kconfig
@@ -0,0 +1,171 @@
+config AGP
+ tristate "/dev/agpgart (AGP Support)" if !GART_IOMMU
+ depends on ALPHA || IA64 || PPC || X86
+ default y if GART_IOMMU
+ ---help---
+ AGP (Accelerated Graphics Port) is a bus system mainly used to
+ connect graphics cards to the rest of the system.
+
+ If you have an AGP system and you say Y here, it will be possible to
+ use the AGP features of your 3D rendering video card. This code acts
+ as a sort of "AGP driver" for the motherboard's chipset.
+
+ If you need more texture memory than you can get with the AGP GART
+ (theoretically up to 256 MB, but in practice usually 64 or 128 MB
+ due to kernel allocation issues), you could use PCI accesses
+ and have up to a couple gigs of texture space.
+
+ Note that this is the only means to have XFree4/GLX use
+ write-combining with MTRR support on the AGP bus. Without it, OpenGL
+ direct rendering will be a lot slower but still faster than PIO.
+
+ You should say Y here if you use XFree86 3.3.6 or 4.x and want to
+ use GLX or DRI. If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called agpgart.
+
+config AGP_ALI
+ tristate "ALI chipset support"
+ depends on AGP && X86 && !X86_64
+ ---help---
+ This option gives you AGP support for the GLX component of
+ XFree86 4.x on the following ALi chipsets. The supported chipsets
+ include M1541, M1621, M1631, M1632, M1641,M1647,and M1651.
+ For the ALi-chipset question, ALi suggests you refer to
+ <http://www.ali.com.tw/eng/support/index.shtml>.
+
+ The M1541 chipset can do AGP 1x and 2x, but note that there is an
+ acknowledged incompatibility with Matrox G200 cards. Due to
+ timing issues, this chipset cannot do AGP 2x with the G200.
+ This is a hardware limitation. AGP 1x seems to be fine, though.
+
+ You should say Y here if you use XFree86 3.3.6 or 4.x and want to
+ use GLX or DRI. If unsure, say N.
+
+config AGP_ATI
+ tristate "ATI chipset support"
+ depends on AGP && X86 && !X86_64
+ ---help---
+ This option gives you AGP support for the GLX component of
+ XFree86 4.x on the ATI RadeonIGP family of chipsets.
+
+ You should say Y here if you use XFree86 3.3.6 or 4.x and want to
+ use GLX or DRI. If unsure, say N.
+
+config AGP_AMD
+ tristate "AMD Irongate, 761, and 762 chipset support"
+ depends on AGP && X86 && !X86_64
+ help
+ This option gives you AGP support for the GLX component of
+ XFree86 4.x on AMD Irongate, 761, and 762 chipsets.
+
+ You should say Y here if you use XFree86 3.3.6 or 4.x and want to
+ use GLX or DRI. If unsure, say N.
+
+config AGP_AMD64
+ tristate "AMD Opteron/Athlon64 on-CPU GART support" if !GART_IOMMU
+ depends on AGP && X86
+ default y if GART_IOMMU
+ help
+ This option gives you AGP support for the GLX component of
+ XFree86 4.x using the on-CPU northbridge of the AMD Athlon64/Opteron CPUs.
+ You still need an external AGP bridge like the AMD 8151, VIA
+ K8T400M, SiS755. It may also support other AGP bridges when loaded
+ with agp_try_unsupported=1.
+ You should say Y here if you use XFree86 3.3.6 or 4.x and want to
+ use GLX or DRI. If unsure, say Y
+
+config AGP_INTEL
+ tristate "Intel 440LX/BX/GX, I8xx and E7x05 chipset support"
+ depends on AGP && X86
+ help
+ This option gives you AGP support for the GLX component of XFree86 4.x
+ on Intel 440LX/BX/GX, 815, 820, 830, 840, 845, 850, 860, 875,
+ E7205 and E7505 chipsets and full support for the 810, 815, 830M, 845G,
+ 852GM, 855GM, 865G and I915 integrated graphics chipsets.
+
+ You should say Y here if you use XFree86 3.3.6 or 4.x and want to
+ use GLX or DRI, or if you have any Intel integrated graphics
+ chipsets. If unsure, say Y.
+
+config AGP_NVIDIA
+ tristate "NVIDIA nForce/nForce2 chipset support"
+ depends on AGP && X86 && !X86_64
+ help
+ This option gives you AGP support for the GLX component of
+ XFree86 4.x on the following NVIDIA chipsets. The supported chipsets
+ include nForce and nForce2
+
+config AGP_SIS
+ tristate "SiS chipset support"
+ depends on AGP && X86 && !X86_64
+ help
+ This option gives you AGP support for the GLX component of
+ XFree86 4.x on Silicon Integrated Systems [SiS] chipsets.
+
+ Note that 5591/5592 AGP chipsets are NOT supported.
+
+ You should say Y here if you use XFree86 3.3.6 or 4.x and want to
+ use GLX or DRI. If unsure, say N.
+
+config AGP_SWORKS
+ tristate "Serverworks LE/HE chipset support"
+ depends on AGP && X86 && !X86_64
+ help
+ Say Y here to support the Serverworks AGP card. See
+ <http://www.serverworks.com/> for product descriptions and images.
+
+config AGP_VIA
+ tristate "VIA chipset support"
+ depends on AGP && X86 && !X86_64
+ help
+ This option gives you AGP support for the GLX component of
+ XFree86 4.x on VIA MVP3/Apollo Pro chipsets.
+
+ You should say Y here if you use XFree86 3.3.6 or 4.x and want to
+ use GLX or DRI. If unsure, say N.
+
+config AGP_I460
+ tristate "Intel 460GX chipset support"
+ depends on AGP && (IA64_DIG || IA64_GENERIC)
+ help
+ This option gives you AGP GART support for the Intel 460GX chipset
+ for IA64 processors.
+
+config AGP_HP_ZX1
+ tristate "HP ZX1 chipset AGP support"
+ depends on AGP && (IA64_HP_ZX1 || IA64_HP_ZX1_SWIOTLB || IA64_GENERIC)
+ help
+ This option gives you AGP GART support for the HP ZX1 chipset
+ for IA64 processors.
+
+config AGP_ALPHA_CORE
+ tristate "Alpha AGP support"
+ depends on AGP && (ALPHA_GENERIC || ALPHA_TITAN || ALPHA_MARVEL)
+ default AGP
+
+config AGP_UNINORTH
+ tristate "Apple UniNorth & U3 AGP support"
+ depends on AGP && PPC_PMAC
+ help
+ This option gives you AGP support for Apple machines with a
+ UniNorth or U3 (Apple G5) bridge.
+
+config AGP_EFFICEON
+ tristate "Transmeta Efficeon support"
+ depends on AGP && X86 && !X86_64
+ help
+ This option gives you AGP support for the Transmeta Efficeon
+ series processors with integrated northbridges.
+
+ You should say Y here if you use XFree86 3.3.6 or 4.x and want to
+ use GLX or DRI. If unsure, say Y.
+
+config AGP_SGI_TIOCA
+ tristate "SGI TIO chipset AGP support"
+ depends on AGP && (IA64_SGI_SN2 || IA64_GENERIC)
+ help
+ This option gives you AGP GART support for the SGI TIO chipset
+ for IA64 processors.
+
diff --git a/drivers/char/agp/Makefile b/drivers/char/agp/Makefile
new file mode 100644
index 00000000000..d33a22f2fa0
--- /dev/null
+++ b/drivers/char/agp/Makefile
@@ -0,0 +1,18 @@
+agpgart-y := backend.o frontend.o generic.o isoch.o
+
+obj-$(CONFIG_AGP) += agpgart.o
+obj-$(CONFIG_AGP_ALI) += ali-agp.o
+obj-$(CONFIG_AGP_ATI) += ati-agp.o
+obj-$(CONFIG_AGP_AMD) += amd-k7-agp.o
+obj-$(CONFIG_AGP_AMD64) += amd64-agp.o
+obj-$(CONFIG_AGP_ALPHA_CORE) += alpha-agp.o
+obj-$(CONFIG_AGP_EFFICEON) += efficeon-agp.o
+obj-$(CONFIG_AGP_HP_ZX1) += hp-agp.o
+obj-$(CONFIG_AGP_I460) += i460-agp.o
+obj-$(CONFIG_AGP_INTEL) += intel-agp.o
+obj-$(CONFIG_AGP_NVIDIA) += nvidia-agp.o
+obj-$(CONFIG_AGP_SGI_TIOCA) += sgi-agp.o
+obj-$(CONFIG_AGP_SIS) += sis-agp.o
+obj-$(CONFIG_AGP_SWORKS) += sworks-agp.o
+obj-$(CONFIG_AGP_UNINORTH) += uninorth-agp.o
+obj-$(CONFIG_AGP_VIA) += via-agp.o
diff --git a/drivers/char/agp/agp.h b/drivers/char/agp/agp.h
new file mode 100644
index 00000000000..ad9c11391d8
--- /dev/null
+++ b/drivers/char/agp/agp.h
@@ -0,0 +1,331 @@
+/*
+ * AGPGART
+ * Copyright (C) 2004 Silicon Graphics, Inc.
+ * Copyright (C) 2002-2004 Dave Jones
+ * Copyright (C) 1999 Jeff Hartmann
+ * Copyright (C) 1999 Precision Insight, Inc.
+ * Copyright (C) 1999 Xi Graphics, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * JEFF HARTMANN, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef _AGP_BACKEND_PRIV_H
+#define _AGP_BACKEND_PRIV_H 1
+
+#include <asm/agp.h> /* for flush_agp_cache() */
+
+#define PFX "agpgart: "
+
+//#define AGP_DEBUG 1
+#ifdef AGP_DEBUG
+#define DBG(x,y...) printk (KERN_DEBUG PFX "%s: " x "\n", __FUNCTION__ , ## y)
+#else
+#define DBG(x,y...) do { } while (0)
+#endif
+
+extern struct agp_bridge_data *agp_bridge;
+
+enum aper_size_type {
+ U8_APER_SIZE,
+ U16_APER_SIZE,
+ U32_APER_SIZE,
+ LVL2_APER_SIZE,
+ FIXED_APER_SIZE
+};
+
+struct gatt_mask {
+ unsigned long mask;
+ u32 type;
+ /* totally device specific, for integrated chipsets that
+ * might have different types of memory masks. For other
+ * devices this will probably be ignored */
+};
+
+struct aper_size_info_8 {
+ int size;
+ int num_entries;
+ int page_order;
+ u8 size_value;
+};
+
+struct aper_size_info_16 {
+ int size;
+ int num_entries;
+ int page_order;
+ u16 size_value;
+};
+
+struct aper_size_info_32 {
+ int size;
+ int num_entries;
+ int page_order;
+ u32 size_value;
+};
+
+struct aper_size_info_lvl2 {
+ int size;
+ int num_entries;
+ u32 size_value;
+};
+
+struct aper_size_info_fixed {
+ int size;
+ int num_entries;
+ int page_order;
+};
+
+struct agp_bridge_driver {
+ struct module *owner;
+ void *aperture_sizes;
+ int num_aperture_sizes;
+ enum aper_size_type size_type;
+ int cant_use_aperture;
+ int needs_scratch_page;
+ struct gatt_mask *masks;
+ int (*fetch_size)(void);
+ int (*configure)(void);
+ void (*agp_enable)(struct agp_bridge_data *, u32);
+ void (*cleanup)(void);
+ void (*tlb_flush)(struct agp_memory *);
+ unsigned long (*mask_memory)(struct agp_bridge_data *,
+ unsigned long, int);
+ void (*cache_flush)(void);
+ int (*create_gatt_table)(struct agp_bridge_data *);
+ int (*free_gatt_table)(struct agp_bridge_data *);
+ int (*insert_memory)(struct agp_memory *, off_t, int);
+ int (*remove_memory)(struct agp_memory *, off_t, int);
+ struct agp_memory *(*alloc_by_type) (size_t, int);
+ void (*free_by_type)(struct agp_memory *);
+ void *(*agp_alloc_page)(struct agp_bridge_data *);
+ void (*agp_destroy_page)(void *);
+};
+
+struct agp_bridge_data {
+ struct agp_version *version;
+ struct agp_bridge_driver *driver;
+ struct vm_operations_struct *vm_ops;
+ void *previous_size;
+ void *current_size;
+ void *dev_private_data;
+ struct pci_dev *dev;
+ u32 __iomem *gatt_table;
+ u32 *gatt_table_real;
+ unsigned long scratch_page;
+ unsigned long scratch_page_real;
+ unsigned long gart_bus_addr;
+ unsigned long gatt_bus_addr;
+ u32 mode;
+ enum chipset_type type;
+ unsigned long *key_list;
+ atomic_t current_memory_agp;
+ atomic_t agp_in_use;
+ int max_memory_agp; /* in number of pages */
+ int aperture_size_idx;
+ int capndx;
+ int flags;
+ char major_version;
+ char minor_version;
+ struct list_head list;
+};
+
+#define KB(x) ((x) * 1024)
+#define MB(x) (KB (KB (x)))
+#define GB(x) (MB (KB (x)))
+
+#define A_SIZE_8(x) ((struct aper_size_info_8 *) x)
+#define A_SIZE_16(x) ((struct aper_size_info_16 *) x)
+#define A_SIZE_32(x) ((struct aper_size_info_32 *) x)
+#define A_SIZE_LVL2(x) ((struct aper_size_info_lvl2 *) x)
+#define A_SIZE_FIX(x) ((struct aper_size_info_fixed *) x)
+#define A_IDX8(bridge) (A_SIZE_8((bridge)->driver->aperture_sizes) + i)
+#define A_IDX16(bridge) (A_SIZE_16((bridge)->driver->aperture_sizes) + i)
+#define A_IDX32(bridge) (A_SIZE_32((bridge)->driver->aperture_sizes) + i)
+#define MAXKEY (4096 * 32)
+
+#define PGE_EMPTY(b, p) (!(p) || (p) == (unsigned long) (b)->scratch_page)
+
+
+/* Intel registers */
+#define INTEL_APSIZE 0xb4
+#define INTEL_ATTBASE 0xb8
+#define INTEL_AGPCTRL 0xb0
+#define INTEL_NBXCFG 0x50
+#define INTEL_ERRSTS 0x91
+
+/* Intel i830 registers */
+#define I830_GMCH_CTRL 0x52
+#define I830_GMCH_ENABLED 0x4
+#define I830_GMCH_MEM_MASK 0x1
+#define I830_GMCH_MEM_64M 0x1
+#define I830_GMCH_MEM_128M 0
+#define I830_GMCH_GMS_MASK 0x70
+#define I830_GMCH_GMS_DISABLED 0x00
+#define I830_GMCH_GMS_LOCAL 0x10
+#define I830_GMCH_GMS_STOLEN_512 0x20
+#define I830_GMCH_GMS_STOLEN_1024 0x30
+#define I830_GMCH_GMS_STOLEN_8192 0x40
+#define I830_RDRAM_CHANNEL_TYPE 0x03010
+#define I830_RDRAM_ND(x) (((x) & 0x20) >> 5)
+#define I830_RDRAM_DDT(x) (((x) & 0x18) >> 3)
+
+/* This one is for I830MP w. an external graphic card */
+#define INTEL_I830_ERRSTS 0x92
+
+/* Intel 855GM/852GM registers */
+#define I855_GMCH_GMS_STOLEN_0M 0x0
+#define I855_GMCH_GMS_STOLEN_1M (0x1 << 4)
+#define I855_GMCH_GMS_STOLEN_4M (0x2 << 4)
+#define I855_GMCH_GMS_STOLEN_8M (0x3 << 4)
+#define I855_GMCH_GMS_STOLEN_16M (0x4 << 4)
+#define I855_GMCH_GMS_STOLEN_32M (0x5 << 4)
+#define I85X_CAPID 0x44
+#define I85X_VARIANT_MASK 0x7
+#define I85X_VARIANT_SHIFT 5
+#define I855_GME 0x0
+#define I855_GM 0x4
+#define I852_GME 0x2
+#define I852_GM 0x5
+
+/* Intel i845 registers */
+#define INTEL_I845_AGPM 0x51
+#define INTEL_I845_ERRSTS 0xc8
+
+/* Intel i860 registers */
+#define INTEL_I860_MCHCFG 0x50
+#define INTEL_I860_ERRSTS 0xc8
+
+/* Intel i810 registers */
+#define I810_GMADDR 0x10
+#define I810_MMADDR 0x14
+#define I810_PTE_BASE 0x10000
+#define I810_PTE_MAIN_UNCACHED 0x00000000
+#define I810_PTE_LOCAL 0x00000002
+#define I810_PTE_VALID 0x00000001
+#define I810_SMRAM_MISCC 0x70
+#define I810_GFX_MEM_WIN_SIZE 0x00010000
+#define I810_GFX_MEM_WIN_32M 0x00010000
+#define I810_GMS 0x000000c0
+#define I810_GMS_DISABLE 0x00000000
+#define I810_PGETBL_CTL 0x2020
+#define I810_PGETBL_ENABLED 0x00000001
+#define I810_DRAM_CTL 0x3000
+#define I810_DRAM_ROW_0 0x00000001
+#define I810_DRAM_ROW_0_SDRAM 0x00000001
+
+struct agp_device_ids {
+ unsigned short device_id; /* first, to make table easier to read */
+ enum chipset_type chipset;
+ const char *chipset_name;
+ int (*chipset_setup) (struct pci_dev *pdev); /* used to override generic */
+};
+
+/* Driver registration */
+struct agp_bridge_data *agp_alloc_bridge(void);
+void agp_put_bridge(struct agp_bridge_data *bridge);
+int agp_add_bridge(struct agp_bridge_data *bridge);
+void agp_remove_bridge(struct agp_bridge_data *bridge);
+
+/* Frontend routines. */
+int agp_frontend_initialize(void);
+void agp_frontend_cleanup(void);
+
+/* Generic routines. */
+void agp_generic_enable(struct agp_bridge_data *bridge, u32 mode);
+int agp_generic_create_gatt_table(struct agp_bridge_data *bridge);
+int agp_generic_free_gatt_table(struct agp_bridge_data *bridge);
+struct agp_memory *agp_create_memory(int scratch_pages);
+int agp_generic_insert_memory(struct agp_memory *mem, off_t pg_start, int type);
+int agp_generic_remove_memory(struct agp_memory *mem, off_t pg_start, int type);
+struct agp_memory *agp_generic_alloc_by_type(size_t page_count, int type);
+void agp_generic_free_by_type(struct agp_memory *curr);
+void *agp_generic_alloc_page(struct agp_bridge_data *bridge);
+void agp_generic_destroy_page(void *addr);
+void agp_free_key(int key);
+int agp_num_entries(void);
+u32 agp_collect_device_status(struct agp_bridge_data *bridge, u32 mode, u32 command);
+void agp_device_command(u32 command, int agp_v3);
+int agp_3_5_enable(struct agp_bridge_data *bridge);
+void global_cache_flush(void);
+void get_agp_version(struct agp_bridge_data *bridge);
+unsigned long agp_generic_mask_memory(struct agp_bridge_data *bridge,
+ unsigned long addr, int type);
+struct agp_bridge_data *agp_generic_find_bridge(struct pci_dev *pdev);
+
+/* generic routines for agp>=3 */
+int agp3_generic_fetch_size(void);
+void agp3_generic_tlbflush(struct agp_memory *mem);
+int agp3_generic_configure(void);
+void agp3_generic_cleanup(void);
+
+/* aperture sizes have been standardised since v3 */
+#define AGP_GENERIC_SIZES_ENTRIES 11
+extern struct aper_size_info_16 agp3_generic_sizes[];
+
+
+extern int agp_off;
+extern int agp_try_unsupported_boot;
+
+/* Chipset independant registers (from AGP Spec) */
+#define AGP_APBASE 0x10
+
+#define AGPSTAT 0x4
+#define AGPCMD 0x8
+#define AGPNISTAT 0xc
+#define AGPCTRL 0x10
+#define AGPAPSIZE 0x14
+#define AGPNEPG 0x16
+#define AGPGARTLO 0x18
+#define AGPGARTHI 0x1c
+#define AGPNICMD 0x20
+
+#define AGP_MAJOR_VERSION_SHIFT (20)
+#define AGP_MINOR_VERSION_SHIFT (16)
+
+#define AGPSTAT_RQ_DEPTH (0xff000000)
+#define AGPSTAT_RQ_DEPTH_SHIFT 24
+
+#define AGPSTAT_CAL_MASK (1<<12|1<<11|1<<10)
+#define AGPSTAT_ARQSZ (1<<15|1<<14|1<<13)
+#define AGPSTAT_ARQSZ_SHIFT 13
+
+#define AGPSTAT_SBA (1<<9)
+#define AGPSTAT_AGP_ENABLE (1<<8)
+#define AGPSTAT_FW (1<<4)
+#define AGPSTAT_MODE_3_0 (1<<3)
+
+#define AGPSTAT2_1X (1<<0)
+#define AGPSTAT2_2X (1<<1)
+#define AGPSTAT2_4X (1<<2)
+
+#define AGPSTAT3_RSVD (1<<2)
+#define AGPSTAT3_8X (1<<1)
+#define AGPSTAT3_4X (1)
+
+#define AGPCTRL_APERENB (1<<8)
+#define AGPCTRL_GTLBEN (1<<7)
+
+#define AGP2_RESERVED_MASK 0x00fffcc8
+#define AGP3_RESERVED_MASK 0x00ff00c4
+
+#define AGP_ERRATA_FASTWRITES 1<<0
+#define AGP_ERRATA_SBA 1<<1
+#define AGP_ERRATA_1X 1<<2
+
+#endif /* _AGP_BACKEND_PRIV_H */
diff --git a/drivers/char/agp/ali-agp.c b/drivers/char/agp/ali-agp.c
new file mode 100644
index 00000000000..c86a22c5499
--- /dev/null
+++ b/drivers/char/agp/ali-agp.c
@@ -0,0 +1,414 @@
+/*
+ * ALi AGPGART routines.
+ */
+
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/agp_backend.h>
+#include "agp.h"
+
+#define ALI_AGPCTRL 0xb8
+#define ALI_ATTBASE 0xbc
+#define ALI_TLBCTRL 0xc0
+#define ALI_TAGCTRL 0xc4
+#define ALI_CACHE_FLUSH_CTRL 0xD0
+#define ALI_CACHE_FLUSH_ADDR_MASK 0xFFFFF000
+#define ALI_CACHE_FLUSH_EN 0x100
+
+static int ali_fetch_size(void)
+{
+ int i;
+ u32 temp;
+ struct aper_size_info_32 *values;
+
+ pci_read_config_dword(agp_bridge->dev, ALI_ATTBASE, &temp);
+ temp &= ~(0xfffffff0);
+ values = A_SIZE_32(agp_bridge->driver->aperture_sizes);
+
+ for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++) {
+ if (temp == values[i].size_value) {
+ agp_bridge->previous_size =
+ agp_bridge->current_size = (void *) (values + i);
+ agp_bridge->aperture_size_idx = i;
+ return values[i].size;
+ }
+ }
+
+ return 0;
+}
+
+static void ali_tlbflush(struct agp_memory *mem)
+{
+ u32 temp;
+
+ pci_read_config_dword(agp_bridge->dev, ALI_TLBCTRL, &temp);
+ temp &= 0xfffffff0;
+ temp |= (1<<0 | 1<<1);
+ pci_write_config_dword(agp_bridge->dev, ALI_TAGCTRL, temp);
+}
+
+static void ali_cleanup(void)
+{
+ struct aper_size_info_32 *previous_size;
+ u32 temp;
+
+ previous_size = A_SIZE_32(agp_bridge->previous_size);
+
+ pci_read_config_dword(agp_bridge->dev, ALI_TLBCTRL, &temp);
+// clear tag
+ pci_write_config_dword(agp_bridge->dev, ALI_TAGCTRL,
+ ((temp & 0xffffff00) | 0x00000001|0x00000002));
+
+ pci_read_config_dword(agp_bridge->dev, ALI_ATTBASE, &temp);
+ pci_write_config_dword(agp_bridge->dev, ALI_ATTBASE,
+ ((temp & 0x00000ff0) | previous_size->size_value));
+}
+
+static int ali_configure(void)
+{
+ u32 temp;
+ struct aper_size_info_32 *current_size;
+
+ current_size = A_SIZE_32(agp_bridge->current_size);
+
+ /* aperture size and gatt addr */
+ pci_read_config_dword(agp_bridge->dev, ALI_ATTBASE, &temp);
+ temp = (((temp & 0x00000ff0) | (agp_bridge->gatt_bus_addr & 0xfffff000))
+ | (current_size->size_value & 0xf));
+ pci_write_config_dword(agp_bridge->dev, ALI_ATTBASE, temp);
+
+ /* tlb control */
+ pci_read_config_dword(agp_bridge->dev, ALI_TLBCTRL, &temp);
+ pci_write_config_dword(agp_bridge->dev, ALI_TLBCTRL, ((temp & 0xffffff00) | 0x00000010));
+
+ /* address to map to */
+ pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp);
+ agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+
+#if 0
+ if (agp_bridge->type == ALI_M1541) {
+ u32 nlvm_addr = 0;
+
+ switch (current_size->size_value) {
+ case 0: break;
+ case 1: nlvm_addr = 0x100000;break;
+ case 2: nlvm_addr = 0x200000;break;
+ case 3: nlvm_addr = 0x400000;break;
+ case 4: nlvm_addr = 0x800000;break;
+ case 6: nlvm_addr = 0x1000000;break;
+ case 7: nlvm_addr = 0x2000000;break;
+ case 8: nlvm_addr = 0x4000000;break;
+ case 9: nlvm_addr = 0x8000000;break;
+ case 10: nlvm_addr = 0x10000000;break;
+ default: break;
+ }
+ nlvm_addr--;
+ nlvm_addr&=0xfff00000;
+
+ nlvm_addr+= agp_bridge->gart_bus_addr;
+ nlvm_addr|=(agp_bridge->gart_bus_addr>>12);
+ printk(KERN_INFO PFX "nlvm top &base = %8x\n",nlvm_addr);
+ }
+#endif
+
+ pci_read_config_dword(agp_bridge->dev, ALI_TLBCTRL, &temp);
+ temp &= 0xffffff7f; //enable TLB
+ pci_write_config_dword(agp_bridge->dev, ALI_TLBCTRL, temp);
+
+ return 0;
+}
+
+
+static void m1541_cache_flush(void)
+{
+ int i, page_count;
+ u32 temp;
+
+ global_cache_flush();
+
+ page_count = 1 << A_SIZE_32(agp_bridge->current_size)->page_order;
+ for (i = 0; i < PAGE_SIZE * page_count; i += PAGE_SIZE) {
+ pci_read_config_dword(agp_bridge->dev, ALI_CACHE_FLUSH_CTRL,
+ &temp);
+ pci_write_config_dword(agp_bridge->dev, ALI_CACHE_FLUSH_CTRL,
+ (((temp & ALI_CACHE_FLUSH_ADDR_MASK) |
+ (agp_bridge->gatt_bus_addr + i)) |
+ ALI_CACHE_FLUSH_EN));
+ }
+}
+
+static void *m1541_alloc_page(struct agp_bridge_data *bridge)
+{
+ void *addr = agp_generic_alloc_page(agp_bridge);
+ u32 temp;
+
+ if (!addr)
+ return NULL;
+
+ pci_read_config_dword(agp_bridge->dev, ALI_CACHE_FLUSH_CTRL, &temp);
+ pci_write_config_dword(agp_bridge->dev, ALI_CACHE_FLUSH_CTRL,
+ (((temp & ALI_CACHE_FLUSH_ADDR_MASK) |
+ virt_to_phys(addr)) | ALI_CACHE_FLUSH_EN ));
+ return addr;
+}
+
+static void ali_destroy_page(void * addr)
+{
+ if (addr) {
+ global_cache_flush(); /* is this really needed? --hch */
+ agp_generic_destroy_page(addr);
+ }
+}
+
+static void m1541_destroy_page(void * addr)
+{
+ u32 temp;
+
+ if (addr == NULL)
+ return;
+
+ global_cache_flush();
+
+ pci_read_config_dword(agp_bridge->dev, ALI_CACHE_FLUSH_CTRL, &temp);
+ pci_write_config_dword(agp_bridge->dev, ALI_CACHE_FLUSH_CTRL,
+ (((temp & ALI_CACHE_FLUSH_ADDR_MASK) |
+ virt_to_phys(addr)) | ALI_CACHE_FLUSH_EN));
+ agp_generic_destroy_page(addr);
+}
+
+
+/* Setup function */
+
+static struct aper_size_info_32 ali_generic_sizes[7] =
+{
+ {256, 65536, 6, 10},
+ {128, 32768, 5, 9},
+ {64, 16384, 4, 8},
+ {32, 8192, 3, 7},
+ {16, 4096, 2, 6},
+ {8, 2048, 1, 4},
+ {4, 1024, 0, 3}
+};
+
+struct agp_bridge_driver ali_generic_bridge = {
+ .owner = THIS_MODULE,
+ .aperture_sizes = ali_generic_sizes,
+ .size_type = U32_APER_SIZE,
+ .num_aperture_sizes = 7,
+ .configure = ali_configure,
+ .fetch_size = ali_fetch_size,
+ .cleanup = ali_cleanup,
+ .tlb_flush = ali_tlbflush,
+ .mask_memory = agp_generic_mask_memory,
+ .masks = NULL,
+ .agp_enable = agp_generic_enable,
+ .cache_flush = global_cache_flush,
+ .create_gatt_table = agp_generic_create_gatt_table,
+ .free_gatt_table = agp_generic_free_gatt_table,
+ .insert_memory = agp_generic_insert_memory,
+ .remove_memory = agp_generic_remove_memory,
+ .alloc_by_type = agp_generic_alloc_by_type,
+ .free_by_type = agp_generic_free_by_type,
+ .agp_alloc_page = agp_generic_alloc_page,
+ .agp_destroy_page = ali_destroy_page,
+};
+
+struct agp_bridge_driver ali_m1541_bridge = {
+ .owner = THIS_MODULE,
+ .aperture_sizes = ali_generic_sizes,
+ .size_type = U32_APER_SIZE,
+ .num_aperture_sizes = 7,
+ .configure = ali_configure,
+ .fetch_size = ali_fetch_size,
+ .cleanup = ali_cleanup,
+ .tlb_flush = ali_tlbflush,
+ .mask_memory = agp_generic_mask_memory,
+ .masks = NULL,
+ .agp_enable = agp_generic_enable,
+ .cache_flush = m1541_cache_flush,
+ .create_gatt_table = agp_generic_create_gatt_table,
+ .free_gatt_table = agp_generic_free_gatt_table,
+ .insert_memory = agp_generic_insert_memory,
+ .remove_memory = agp_generic_remove_memory,
+ .alloc_by_type = agp_generic_alloc_by_type,
+ .free_by_type = agp_generic_free_by_type,
+ .agp_alloc_page = m1541_alloc_page,
+ .agp_destroy_page = m1541_destroy_page,
+};
+
+
+static struct agp_device_ids ali_agp_device_ids[] __devinitdata =
+{
+ {
+ .device_id = PCI_DEVICE_ID_AL_M1541,
+ .chipset_name = "M1541",
+ },
+ {
+ .device_id = PCI_DEVICE_ID_AL_M1621,
+ .chipset_name = "M1621",
+ },
+ {
+ .device_id = PCI_DEVICE_ID_AL_M1631,
+ .chipset_name = "M1631",
+ },
+ {
+ .device_id = PCI_DEVICE_ID_AL_M1632,
+ .chipset_name = "M1632",
+ },
+ {
+ .device_id = PCI_DEVICE_ID_AL_M1641,
+ .chipset_name = "M1641",
+ },
+ {
+ .device_id = PCI_DEVICE_ID_AL_M1644,
+ .chipset_name = "M1644",
+ },
+ {
+ .device_id = PCI_DEVICE_ID_AL_M1647,
+ .chipset_name = "M1647",
+ },
+ {
+ .device_id = PCI_DEVICE_ID_AL_M1651,
+ .chipset_name = "M1651",
+ },
+ {
+ .device_id = PCI_DEVICE_ID_AL_M1671,
+ .chipset_name = "M1671",
+ },
+ {
+ .device_id = PCI_DEVICE_ID_AL_M1681,
+ .chipset_name = "M1681",
+ },
+ {
+ .device_id = PCI_DEVICE_ID_AL_M1683,
+ .chipset_name = "M1683",
+ },
+
+ { }, /* dummy final entry, always present */
+};
+
+static int __devinit agp_ali_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct agp_device_ids *devs = ali_agp_device_ids;
+ struct agp_bridge_data *bridge;
+ u8 hidden_1621_id, cap_ptr;
+ int j;
+
+ cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP);
+ if (!cap_ptr)
+ return -ENODEV;
+
+ /* probe for known chipsets */
+ for (j = 0; devs[j].chipset_name; j++) {
+ if (pdev->device == devs[j].device_id)
+ goto found;
+ }
+
+ printk(KERN_ERR PFX "Unsupported ALi chipset (device id: %04x)\n",
+ pdev->device);
+ return -ENODEV;
+
+
+found:
+ bridge = agp_alloc_bridge();
+ if (!bridge)
+ return -ENOMEM;
+
+ bridge->dev = pdev;
+ bridge->capndx = cap_ptr;
+
+ switch (pdev->device) {
+ case PCI_DEVICE_ID_AL_M1541:
+ bridge->driver = &ali_m1541_bridge;
+ break;
+ case PCI_DEVICE_ID_AL_M1621:
+ pci_read_config_byte(pdev, 0xFB, &hidden_1621_id);
+ switch (hidden_1621_id) {
+ case 0x31:
+ devs[j].chipset_name = "M1631";
+ break;
+ case 0x32:
+ devs[j].chipset_name = "M1632";
+ break;
+ case 0x41:
+ devs[j].chipset_name = "M1641";
+ break;
+ case 0x43:
+ devs[j].chipset_name = "M????";
+ break;
+ case 0x47:
+ devs[j].chipset_name = "M1647";
+ break;
+ case 0x51:
+ devs[j].chipset_name = "M1651";
+ break;
+ default:
+ break;
+ }
+ /*FALLTHROUGH*/
+ default:
+ bridge->driver = &ali_generic_bridge;
+ }
+
+ printk(KERN_INFO PFX "Detected ALi %s chipset\n",
+ devs[j].chipset_name);
+
+ /* Fill in the mode register */
+ pci_read_config_dword(pdev,
+ bridge->capndx+PCI_AGP_STATUS,
+ &bridge->mode);
+
+ pci_set_drvdata(pdev, bridge);
+ return agp_add_bridge(bridge);
+}
+
+static void __devexit agp_ali_remove(struct pci_dev *pdev)
+{
+ struct agp_bridge_data *bridge = pci_get_drvdata(pdev);
+
+ agp_remove_bridge(bridge);
+ agp_put_bridge(bridge);
+}
+
+static struct pci_device_id agp_ali_pci_table[] = {
+ {
+ .class = (PCI_CLASS_BRIDGE_HOST << 8),
+ .class_mask = ~0,
+ .vendor = PCI_VENDOR_ID_AL,
+ .device = PCI_ANY_ID,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ },
+ { }
+};
+
+MODULE_DEVICE_TABLE(pci, agp_ali_pci_table);
+
+static struct pci_driver agp_ali_pci_driver = {
+ .name = "agpgart-ali",
+ .id_table = agp_ali_pci_table,
+ .probe = agp_ali_probe,
+ .remove = agp_ali_remove,
+};
+
+static int __init agp_ali_init(void)
+{
+ if (agp_off)
+ return -EINVAL;
+ return pci_register_driver(&agp_ali_pci_driver);
+}
+
+static void __exit agp_ali_cleanup(void)
+{
+ pci_unregister_driver(&agp_ali_pci_driver);
+}
+
+module_init(agp_ali_init);
+module_exit(agp_ali_cleanup);
+
+MODULE_AUTHOR("Dave Jones <davej@codemonkey.org.uk>");
+MODULE_LICENSE("GPL and additional rights");
+
diff --git a/drivers/char/agp/alpha-agp.c b/drivers/char/agp/alpha-agp.c
new file mode 100644
index 00000000000..a072d32005a
--- /dev/null
+++ b/drivers/char/agp/alpha-agp.c
@@ -0,0 +1,216 @@
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/agp_backend.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+
+#include <asm/machvec.h>
+#include <asm/agp_backend.h>
+#include "../../../arch/alpha/kernel/pci_impl.h"
+
+#include "agp.h"
+
+static struct page *alpha_core_agp_vm_nopage(struct vm_area_struct *vma,
+ unsigned long address,
+ int *type)
+{
+ alpha_agp_info *agp = agp_bridge->dev_private_data;
+ dma_addr_t dma_addr;
+ unsigned long pa;
+ struct page *page;
+
+ dma_addr = address - vma->vm_start + agp->aperture.bus_base;
+ pa = agp->ops->translate(agp, dma_addr);
+
+ if (pa == (unsigned long)-EINVAL) return NULL; /* no translation */
+
+ /*
+ * Get the page, inc the use count, and return it
+ */
+ page = virt_to_page(__va(pa));
+ get_page(page);
+ if (type)
+ *type = VM_FAULT_MINOR;
+ return page;
+}
+
+static struct aper_size_info_fixed alpha_core_agp_sizes[] =
+{
+ { 0, 0, 0 }, /* filled in by alpha_core_agp_setup */
+};
+
+struct vm_operations_struct alpha_core_agp_vm_ops = {
+ .nopage = alpha_core_agp_vm_nopage,
+};
+
+
+static int alpha_core_agp_nop(void)
+{
+ /* just return success */
+ return 0;
+}
+
+static int alpha_core_agp_fetch_size(void)
+{
+ return alpha_core_agp_sizes[0].size;
+}
+
+static int alpha_core_agp_configure(void)
+{
+ alpha_agp_info *agp = agp_bridge->dev_private_data;
+ agp_bridge->gart_bus_addr = agp->aperture.bus_base;
+ return 0;
+}
+
+static void alpha_core_agp_cleanup(void)
+{
+ alpha_agp_info *agp = agp_bridge->dev_private_data;
+
+ agp->ops->cleanup(agp);
+}
+
+static void alpha_core_agp_tlbflush(struct agp_memory *mem)
+{
+ alpha_agp_info *agp = agp_bridge->dev_private_data;
+ alpha_mv.mv_pci_tbi(agp->hose, 0, -1);
+}
+
+static void alpha_core_agp_enable(struct agp_bridge_data *bridge, u32 mode)
+{
+ alpha_agp_info *agp = bridge->dev_private_data;
+
+ agp->mode.lw = agp_collect_device_status(bridge, mode,
+ agp->capability.lw);
+
+ agp->mode.bits.enable = 1;
+ agp->ops->configure(agp);
+
+ agp_device_command(agp->mode.lw, 0);
+}
+
+static int alpha_core_agp_insert_memory(struct agp_memory *mem, off_t pg_start,
+ int type)
+{
+ alpha_agp_info *agp = agp_bridge->dev_private_data;
+ int num_entries, status;
+ void *temp;
+
+ temp = agp_bridge->current_size;
+ num_entries = A_SIZE_FIX(temp)->num_entries;
+ if ((pg_start + mem->page_count) > num_entries) return -EINVAL;
+
+ status = agp->ops->bind(agp, pg_start, mem);
+ mb();
+ alpha_core_agp_tlbflush(mem);
+
+ return status;
+}
+
+static int alpha_core_agp_remove_memory(struct agp_memory *mem, off_t pg_start,
+ int type)
+{
+ alpha_agp_info *agp = agp_bridge->dev_private_data;
+ int status;
+
+ status = agp->ops->unbind(agp, pg_start, mem);
+ alpha_core_agp_tlbflush(mem);
+ return status;
+}
+
+struct agp_bridge_driver alpha_core_agp_driver = {
+ .owner = THIS_MODULE,
+ .aperture_sizes = alpha_core_agp_sizes,
+ .num_aperture_sizes = 1,
+ .size_type = FIXED_APER_SIZE,
+ .cant_use_aperture = 1,
+ .masks = NULL,
+
+ .fetch_size = alpha_core_agp_fetch_size,
+ .configure = alpha_core_agp_configure,
+ .agp_enable = alpha_core_agp_enable,
+ .cleanup = alpha_core_agp_cleanup,
+ .tlb_flush = alpha_core_agp_tlbflush,
+ .mask_memory = agp_generic_mask_memory,
+ .cache_flush = global_cache_flush,
+ .create_gatt_table = alpha_core_agp_nop,
+ .free_gatt_table = alpha_core_agp_nop,
+ .insert_memory = alpha_core_agp_insert_memory,
+ .remove_memory = alpha_core_agp_remove_memory,
+ .alloc_by_type = agp_generic_alloc_by_type,
+ .free_by_type = agp_generic_free_by_type,
+ .agp_alloc_page = agp_generic_alloc_page,
+ .agp_destroy_page = agp_generic_destroy_page,
+};
+
+struct agp_bridge_data *alpha_bridge;
+
+int __init
+alpha_core_agp_setup(void)
+{
+ alpha_agp_info *agp = alpha_mv.agp_info();
+ struct pci_dev *pdev; /* faked */
+ struct aper_size_info_fixed *aper_size;
+
+ if (!agp)
+ return -ENODEV;
+ if (agp->ops->setup(agp))
+ return -ENODEV;
+
+ /*
+ * Build the aperture size descriptor
+ */
+ aper_size = alpha_core_agp_sizes;
+ aper_size->size = agp->aperture.size / (1024 * 1024);
+ aper_size->num_entries = agp->aperture.size / PAGE_SIZE;
+ aper_size->page_order = __ffs(aper_size->num_entries / 1024);
+
+ /*
+ * Build a fake pci_dev struct
+ */
+ pdev = kmalloc(sizeof(struct pci_dev), GFP_KERNEL);
+ if (!pdev)
+ return -ENOMEM;
+ pdev->vendor = 0xffff;
+ pdev->device = 0xffff;
+ pdev->sysdata = agp->hose;
+
+ alpha_bridge = agp_alloc_bridge();
+ if (!alpha_bridge)
+ goto fail;
+
+ alpha_bridge->driver = &alpha_core_agp_driver;
+ alpha_bridge->vm_ops = &alpha_core_agp_vm_ops;
+ alpha_bridge->current_size = aper_size; /* only 1 size */
+ alpha_bridge->dev_private_data = agp;
+ alpha_bridge->dev = pdev;
+ alpha_bridge->mode = agp->capability.lw;
+
+ printk(KERN_INFO PFX "Detected AGP on hose %d\n", agp->hose->index);
+ return agp_add_bridge(alpha_bridge);
+
+ fail:
+ kfree(pdev);
+ return -ENOMEM;
+}
+
+static int __init agp_alpha_core_init(void)
+{
+ if (agp_off)
+ return -EINVAL;
+ if (alpha_mv.agp_info)
+ return alpha_core_agp_setup();
+ return -ENODEV;
+}
+
+static void __exit agp_alpha_core_cleanup(void)
+{
+ agp_remove_bridge(alpha_bridge);
+ agp_put_bridge(alpha_bridge);
+}
+
+module_init(agp_alpha_core_init);
+module_exit(agp_alpha_core_cleanup);
+
+MODULE_AUTHOR("Jeff Wiedemeier <Jeff.Wiedemeier@hp.com>");
+MODULE_LICENSE("GPL and additional rights");
diff --git a/drivers/char/agp/amd-k7-agp.c b/drivers/char/agp/amd-k7-agp.c
new file mode 100644
index 00000000000..f1ea87ea6b6
--- /dev/null
+++ b/drivers/char/agp/amd-k7-agp.c
@@ -0,0 +1,542 @@
+/*
+ * AMD K7 AGPGART routines.
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/agp_backend.h>
+#include <linux/gfp.h>
+#include <linux/page-flags.h>
+#include <linux/mm.h>
+#include "agp.h"
+
+#define AMD_MMBASE 0x14
+#define AMD_APSIZE 0xac
+#define AMD_MODECNTL 0xb0
+#define AMD_MODECNTL2 0xb2
+#define AMD_GARTENABLE 0x02 /* In mmio region (16-bit register) */
+#define AMD_ATTBASE 0x04 /* In mmio region (32-bit register) */
+#define AMD_TLBFLUSH 0x0c /* In mmio region (32-bit register) */
+#define AMD_CACHEENTRY 0x10 /* In mmio region (32-bit register) */
+
+static struct pci_device_id agp_amdk7_pci_table[];
+
+struct amd_page_map {
+ unsigned long *real;
+ unsigned long __iomem *remapped;
+};
+
+static struct _amd_irongate_private {
+ volatile u8 __iomem *registers;
+ struct amd_page_map **gatt_pages;
+ int num_tables;
+} amd_irongate_private;
+
+static int amd_create_page_map(struct amd_page_map *page_map)
+{
+ int i;
+
+ page_map->real = (unsigned long *) __get_free_page(GFP_KERNEL);
+ if (page_map->real == NULL)
+ return -ENOMEM;
+
+ SetPageReserved(virt_to_page(page_map->real));
+ global_cache_flush();
+ page_map->remapped = ioremap_nocache(virt_to_phys(page_map->real),
+ PAGE_SIZE);
+ if (page_map->remapped == NULL) {
+ ClearPageReserved(virt_to_page(page_map->real));
+ free_page((unsigned long) page_map->real);
+ page_map->real = NULL;
+ return -ENOMEM;
+ }
+ global_cache_flush();
+
+ for (i = 0; i < PAGE_SIZE / sizeof(unsigned long); i++) {
+ writel(agp_bridge->scratch_page, page_map->remapped+i);
+ readl(page_map->remapped+i); /* PCI Posting. */
+ }
+
+ return 0;
+}
+
+static void amd_free_page_map(struct amd_page_map *page_map)
+{
+ iounmap(page_map->remapped);
+ ClearPageReserved(virt_to_page(page_map->real));
+ free_page((unsigned long) page_map->real);
+}
+
+static void amd_free_gatt_pages(void)
+{
+ int i;
+ struct amd_page_map **tables;
+ struct amd_page_map *entry;
+
+ tables = amd_irongate_private.gatt_pages;
+ for (i = 0; i < amd_irongate_private.num_tables; i++) {
+ entry = tables[i];
+ if (entry != NULL) {
+ if (entry->real != NULL)
+ amd_free_page_map(entry);
+ kfree(entry);
+ }
+ }
+ kfree(tables);
+ amd_irongate_private.gatt_pages = NULL;
+}
+
+static int amd_create_gatt_pages(int nr_tables)
+{
+ struct amd_page_map **tables;
+ struct amd_page_map *entry;
+ int retval = 0;
+ int i;
+
+ tables = kmalloc((nr_tables + 1) * sizeof(struct amd_page_map *),
+ GFP_KERNEL);
+ if (tables == NULL)
+ return -ENOMEM;
+
+ memset (tables, 0, sizeof(struct amd_page_map *) * (nr_tables + 1));
+ for (i = 0; i < nr_tables; i++) {
+ entry = kmalloc(sizeof(struct amd_page_map), GFP_KERNEL);
+ if (entry == NULL) {
+ retval = -ENOMEM;
+ break;
+ }
+ memset (entry, 0, sizeof(struct amd_page_map));
+ tables[i] = entry;
+ retval = amd_create_page_map(entry);
+ if (retval != 0)
+ break;
+ }
+ amd_irongate_private.num_tables = nr_tables;
+ amd_irongate_private.gatt_pages = tables;
+
+ if (retval != 0)
+ amd_free_gatt_pages();
+
+ return retval;
+}
+
+/* Since we don't need contigious memory we just try
+ * to get the gatt table once
+ */
+
+#define GET_PAGE_DIR_OFF(addr) (addr >> 22)
+#define GET_PAGE_DIR_IDX(addr) (GET_PAGE_DIR_OFF(addr) - \
+ GET_PAGE_DIR_OFF(agp_bridge->gart_bus_addr))
+#define GET_GATT_OFF(addr) ((addr & 0x003ff000) >> 12)
+#define GET_GATT(addr) (amd_irongate_private.gatt_pages[\
+ GET_PAGE_DIR_IDX(addr)]->remapped)
+
+static int amd_create_gatt_table(struct agp_bridge_data *bridge)
+{
+ struct aper_size_info_lvl2 *value;
+ struct amd_page_map page_dir;
+ unsigned long addr;
+ int retval;
+ u32 temp;
+ int i;
+
+ value = A_SIZE_LVL2(agp_bridge->current_size);
+ retval = amd_create_page_map(&page_dir);
+ if (retval != 0)
+ return retval;
+
+ retval = amd_create_gatt_pages(value->num_entries / 1024);
+ if (retval != 0) {
+ amd_free_page_map(&page_dir);
+ return retval;
+ }
+
+ agp_bridge->gatt_table_real = (u32 *)page_dir.real;
+ agp_bridge->gatt_table = (u32 __iomem *)page_dir.remapped;
+ agp_bridge->gatt_bus_addr = virt_to_phys(page_dir.real);
+
+ /* Get the address for the gart region.
+ * This is a bus address even on the alpha, b/c its
+ * used to program the agp master not the cpu
+ */
+
+ pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp);
+ addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+ agp_bridge->gart_bus_addr = addr;
+
+ /* Calculate the agp offset */
+ for (i = 0; i < value->num_entries / 1024; i++, addr += 0x00400000) {
+ writel(virt_to_phys(amd_irongate_private.gatt_pages[i]->real) | 1,
+ page_dir.remapped+GET_PAGE_DIR_OFF(addr));
+ readl(page_dir.remapped+GET_PAGE_DIR_OFF(addr)); /* PCI Posting. */
+ }
+
+ return 0;
+}
+
+static int amd_free_gatt_table(struct agp_bridge_data *bridge)
+{
+ struct amd_page_map page_dir;
+
+ page_dir.real = (unsigned long *)agp_bridge->gatt_table_real;
+ page_dir.remapped = (unsigned long __iomem *)agp_bridge->gatt_table;
+
+ amd_free_gatt_pages();
+ amd_free_page_map(&page_dir);
+ return 0;
+}
+
+static int amd_irongate_fetch_size(void)
+{
+ int i;
+ u32 temp;
+ struct aper_size_info_lvl2 *values;
+
+ pci_read_config_dword(agp_bridge->dev, AMD_APSIZE, &temp);
+ temp = (temp & 0x0000000e);
+ values = A_SIZE_LVL2(agp_bridge->driver->aperture_sizes);
+ for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++) {
+ if (temp == values[i].size_value) {
+ agp_bridge->previous_size =
+ agp_bridge->current_size = (void *) (values + i);
+
+ agp_bridge->aperture_size_idx = i;
+ return values[i].size;
+ }
+ }
+
+ return 0;
+}
+
+static int amd_irongate_configure(void)
+{
+ struct aper_size_info_lvl2 *current_size;
+ u32 temp;
+ u16 enable_reg;
+
+ current_size = A_SIZE_LVL2(agp_bridge->current_size);
+
+ /* Get the memory mapped registers */
+ pci_read_config_dword(agp_bridge->dev, AMD_MMBASE, &temp);
+ temp = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+ amd_irongate_private.registers = (volatile u8 __iomem *) ioremap(temp, 4096);
+
+ /* Write out the address of the gatt table */
+ writel(agp_bridge->gatt_bus_addr, amd_irongate_private.registers+AMD_ATTBASE);
+ readl(amd_irongate_private.registers+AMD_ATTBASE); /* PCI Posting. */
+
+ /* Write the Sync register */
+ pci_write_config_byte(agp_bridge->dev, AMD_MODECNTL, 0x80);
+
+ /* Set indexing mode */
+ pci_write_config_byte(agp_bridge->dev, AMD_MODECNTL2, 0x00);
+
+ /* Write the enable register */
+ enable_reg = readw(amd_irongate_private.registers+AMD_GARTENABLE);
+ enable_reg = (enable_reg | 0x0004);
+ writew(enable_reg, amd_irongate_private.registers+AMD_GARTENABLE);
+ readw(amd_irongate_private.registers+AMD_GARTENABLE); /* PCI Posting. */
+
+ /* Write out the size register */
+ pci_read_config_dword(agp_bridge->dev, AMD_APSIZE, &temp);
+ temp = (((temp & ~(0x0000000e)) | current_size->size_value) | 1);
+ pci_write_config_dword(agp_bridge->dev, AMD_APSIZE, temp);
+
+ /* Flush the tlb */
+ writel(1, amd_irongate_private.registers+AMD_TLBFLUSH);
+ readl(amd_irongate_private.registers+AMD_TLBFLUSH); /* PCI Posting.*/
+ return 0;
+}
+
+static void amd_irongate_cleanup(void)
+{
+ struct aper_size_info_lvl2 *previous_size;
+ u32 temp;
+ u16 enable_reg;
+
+ previous_size = A_SIZE_LVL2(agp_bridge->previous_size);
+
+ enable_reg = readw(amd_irongate_private.registers+AMD_GARTENABLE);
+ enable_reg = (enable_reg & ~(0x0004));
+ writew(enable_reg, amd_irongate_private.registers+AMD_GARTENABLE);
+ readw(amd_irongate_private.registers+AMD_GARTENABLE); /* PCI Posting. */
+
+ /* Write back the previous size and disable gart translation */
+ pci_read_config_dword(agp_bridge->dev, AMD_APSIZE, &temp);
+ temp = ((temp & ~(0x0000000f)) | previous_size->size_value);
+ pci_write_config_dword(agp_bridge->dev, AMD_APSIZE, temp);
+ iounmap((void __iomem *) amd_irongate_private.registers);
+}
+
+/*
+ * This routine could be implemented by taking the addresses
+ * written to the GATT, and flushing them individually. However
+ * currently it just flushes the whole table. Which is probably
+ * more efficent, since agp_memory blocks can be a large number of
+ * entries.
+ */
+
+static void amd_irongate_tlbflush(struct agp_memory *temp)
+{
+ writel(1, amd_irongate_private.registers+AMD_TLBFLUSH);
+ readl(amd_irongate_private.registers+AMD_TLBFLUSH); /* PCI Posting. */
+}
+
+static int amd_insert_memory(struct agp_memory *mem, off_t pg_start, int type)
+{
+ int i, j, num_entries;
+ unsigned long __iomem *cur_gatt;
+ unsigned long addr;
+
+ num_entries = A_SIZE_LVL2(agp_bridge->current_size)->num_entries;
+
+ if (type != 0 || mem->type != 0)
+ return -EINVAL;
+
+ if ((pg_start + mem->page_count) > num_entries)
+ return -EINVAL;
+
+ j = pg_start;
+ while (j < (pg_start + mem->page_count)) {
+ addr = (j * PAGE_SIZE) + agp_bridge->gart_bus_addr;
+ cur_gatt = GET_GATT(addr);
+ if (!PGE_EMPTY(agp_bridge, readl(cur_gatt+GET_GATT_OFF(addr))))
+ return -EBUSY;
+ j++;
+ }
+
+ if (mem->is_flushed == FALSE) {
+ global_cache_flush();
+ mem->is_flushed = TRUE;
+ }
+
+ for (i = 0, j = pg_start; i < mem->page_count; i++, j++) {
+ addr = (j * PAGE_SIZE) + agp_bridge->gart_bus_addr;
+ cur_gatt = GET_GATT(addr);
+ writel(agp_generic_mask_memory(agp_bridge,
+ mem->memory[i], mem->type), cur_gatt+GET_GATT_OFF(addr));
+ readl(cur_gatt+GET_GATT_OFF(addr)); /* PCI Posting. */
+ }
+ amd_irongate_tlbflush(mem);
+ return 0;
+}
+
+static int amd_remove_memory(struct agp_memory *mem, off_t pg_start, int type)
+{
+ int i;
+ unsigned long __iomem *cur_gatt;
+ unsigned long addr;
+
+ if (type != 0 || mem->type != 0)
+ return -EINVAL;
+
+ for (i = pg_start; i < (mem->page_count + pg_start); i++) {
+ addr = (i * PAGE_SIZE) + agp_bridge->gart_bus_addr;
+ cur_gatt = GET_GATT(addr);
+ writel(agp_bridge->scratch_page, cur_gatt+GET_GATT_OFF(addr));
+ readl(cur_gatt+GET_GATT_OFF(addr)); /* PCI Posting. */
+ }
+
+ amd_irongate_tlbflush(mem);
+ return 0;
+}
+
+static struct aper_size_info_lvl2 amd_irongate_sizes[7] =
+{
+ {2048, 524288, 0x0000000c},
+ {1024, 262144, 0x0000000a},
+ {512, 131072, 0x00000008},
+ {256, 65536, 0x00000006},
+ {128, 32768, 0x00000004},
+ {64, 16384, 0x00000002},
+ {32, 8192, 0x00000000}
+};
+
+static struct gatt_mask amd_irongate_masks[] =
+{
+ {.mask = 1, .type = 0}
+};
+
+struct agp_bridge_driver amd_irongate_driver = {
+ .owner = THIS_MODULE,
+ .aperture_sizes = amd_irongate_sizes,
+ .size_type = LVL2_APER_SIZE,
+ .num_aperture_sizes = 7,
+ .configure = amd_irongate_configure,
+ .fetch_size = amd_irongate_fetch_size,
+ .cleanup = amd_irongate_cleanup,
+ .tlb_flush = amd_irongate_tlbflush,
+ .mask_memory = agp_generic_mask_memory,
+ .masks = amd_irongate_masks,
+ .agp_enable = agp_generic_enable,
+ .cache_flush = global_cache_flush,
+ .create_gatt_table = amd_create_gatt_table,
+ .free_gatt_table = amd_free_gatt_table,
+ .insert_memory = amd_insert_memory,
+ .remove_memory = amd_remove_memory,
+ .alloc_by_type = agp_generic_alloc_by_type,
+ .free_by_type = agp_generic_free_by_type,
+ .agp_alloc_page = agp_generic_alloc_page,
+ .agp_destroy_page = agp_generic_destroy_page,
+};
+
+static struct agp_device_ids amd_agp_device_ids[] __devinitdata =
+{
+ {
+ .device_id = PCI_DEVICE_ID_AMD_FE_GATE_7006,
+ .chipset_name = "Irongate",
+ },
+ {
+ .device_id = PCI_DEVICE_ID_AMD_FE_GATE_700E,
+ .chipset_name = "761",
+ },
+ {
+ .device_id = PCI_DEVICE_ID_AMD_FE_GATE_700C,
+ .chipset_name = "760MP",
+ },
+ { }, /* dummy final entry, always present */
+};
+
+static int __devinit agp_amdk7_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct agp_bridge_data *bridge;
+ u8 cap_ptr;
+ int j;
+
+ cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP);
+ if (!cap_ptr)
+ return -ENODEV;
+
+ j = ent - agp_amdk7_pci_table;
+ printk(KERN_INFO PFX "Detected AMD %s chipset\n",
+ amd_agp_device_ids[j].chipset_name);
+
+ bridge = agp_alloc_bridge();
+ if (!bridge)
+ return -ENOMEM;
+
+ bridge->driver = &amd_irongate_driver;
+ bridge->dev_private_data = &amd_irongate_private,
+ bridge->dev = pdev;
+ bridge->capndx = cap_ptr;
+
+ /* 751 Errata (22564_B-1.PDF)
+ erratum 20: strobe glitch with Nvidia NV10 GeForce cards.
+ system controller may experience noise due to strong drive strengths
+ */
+ if (agp_bridge->dev->device == PCI_DEVICE_ID_AMD_FE_GATE_7006) {
+ u8 cap_ptr=0;
+ struct pci_dev *gfxcard=NULL;
+ while (!cap_ptr) {
+ gfxcard = pci_get_class(PCI_CLASS_DISPLAY_VGA<<8, gfxcard);
+ if (!gfxcard) {
+ printk (KERN_INFO PFX "Couldn't find an AGP VGA controller.\n");
+ return -ENODEV;
+ }
+ cap_ptr = pci_find_capability(gfxcard, PCI_CAP_ID_AGP);
+ if (!cap_ptr) {
+ pci_dev_put(gfxcard);
+ continue;
+ }
+ }
+
+ /* With so many variants of NVidia cards, it's simpler just
+ to blacklist them all, and then whitelist them as needed
+ (if necessary at all). */
+ if (gfxcard->vendor == PCI_VENDOR_ID_NVIDIA) {
+ agp_bridge->flags |= AGP_ERRATA_1X;
+ printk (KERN_INFO PFX "AMD 751 chipset with NVidia GeForce detected. Forcing to 1X due to errata.\n");
+ }
+ pci_dev_put(gfxcard);
+ }
+
+ /* 761 Errata (23613_F.pdf)
+ * Revisions B0/B1 were a disaster.
+ * erratum 44: SYSCLK/AGPCLK skew causes 2X failures -- Force mode to 1X
+ * erratum 45: Timing problem prevents fast writes -- Disable fast write.
+ * erratum 46: Setup violation on AGP SBA pins - Disable side band addressing.
+ * With this lot disabled, we should prevent lockups. */
+ if (agp_bridge->dev->device == PCI_DEVICE_ID_AMD_FE_GATE_700E) {
+ u8 revision=0;
+ pci_read_config_byte(pdev, PCI_REVISION_ID, &revision);
+ if (revision == 0x10 || revision == 0x11) {
+ agp_bridge->flags = AGP_ERRATA_FASTWRITES;
+ agp_bridge->flags |= AGP_ERRATA_SBA;
+ agp_bridge->flags |= AGP_ERRATA_1X;
+ printk (KERN_INFO PFX "AMD 761 chipset with errata detected - disabling AGP fast writes & SBA and forcing to 1X.\n");
+ }
+ }
+
+ /* Fill in the mode register */
+ pci_read_config_dword(pdev,
+ bridge->capndx+PCI_AGP_STATUS,
+ &bridge->mode);
+
+ pci_set_drvdata(pdev, bridge);
+ return agp_add_bridge(bridge);
+}
+
+static void __devexit agp_amdk7_remove(struct pci_dev *pdev)
+{
+ struct agp_bridge_data *bridge = pci_get_drvdata(pdev);
+
+ agp_remove_bridge(bridge);
+ agp_put_bridge(bridge);
+}
+
+/* must be the same order as name table above */
+static struct pci_device_id agp_amdk7_pci_table[] = {
+ {
+ .class = (PCI_CLASS_BRIDGE_HOST << 8),
+ .class_mask = ~0,
+ .vendor = PCI_VENDOR_ID_AMD,
+ .device = PCI_DEVICE_ID_AMD_FE_GATE_7006,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ },
+ {
+ .class = (PCI_CLASS_BRIDGE_HOST << 8),
+ .class_mask = ~0,
+ .vendor = PCI_VENDOR_ID_AMD,
+ .device = PCI_DEVICE_ID_AMD_FE_GATE_700E,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ },
+ {
+ .class = (PCI_CLASS_BRIDGE_HOST << 8),
+ .class_mask = ~0,
+ .vendor = PCI_VENDOR_ID_AMD,
+ .device = PCI_DEVICE_ID_AMD_FE_GATE_700C,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ },
+ { }
+};
+
+MODULE_DEVICE_TABLE(pci, agp_amdk7_pci_table);
+
+static struct pci_driver agp_amdk7_pci_driver = {
+ .name = "agpgart-amdk7",
+ .id_table = agp_amdk7_pci_table,
+ .probe = agp_amdk7_probe,
+ .remove = agp_amdk7_remove,
+};
+
+static int __init agp_amdk7_init(void)
+{
+ if (agp_off)
+ return -EINVAL;
+ return pci_register_driver(&agp_amdk7_pci_driver);
+}
+
+static void __exit agp_amdk7_cleanup(void)
+{
+ pci_unregister_driver(&agp_amdk7_pci_driver);
+}
+
+module_init(agp_amdk7_init);
+module_exit(agp_amdk7_cleanup);
+
+MODULE_LICENSE("GPL and additional rights");
diff --git a/drivers/char/agp/amd64-agp.c b/drivers/char/agp/amd64-agp.c
new file mode 100644
index 00000000000..905f0629c44
--- /dev/null
+++ b/drivers/char/agp/amd64-agp.c
@@ -0,0 +1,761 @@
+/*
+ * Copyright 2001-2003 SuSE Labs.
+ * Distributed under the GNU public license, v2.
+ *
+ * This is a GART driver for the AMD Opteron/Athlon64 on-CPU northbridge.
+ * It also includes support for the AMD 8151 AGP bridge,
+ * although it doesn't actually do much, as all the real
+ * work is done in the northbridge(s).
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/agp_backend.h>
+#include "agp.h"
+
+/* Will need to be increased if AMD64 ever goes >8-way. */
+#define MAX_HAMMER_GARTS 8
+
+/* PTE bits. */
+#define GPTE_VALID 1
+#define GPTE_COHERENT 2
+
+/* Aperture control register bits. */
+#define GARTEN (1<<0)
+#define DISGARTCPU (1<<4)
+#define DISGARTIO (1<<5)
+
+/* GART cache control register bits. */
+#define INVGART (1<<0)
+#define GARTPTEERR (1<<1)
+
+/* K8 On-cpu GART registers */
+#define AMD64_GARTAPERTURECTL 0x90
+#define AMD64_GARTAPERTUREBASE 0x94
+#define AMD64_GARTTABLEBASE 0x98
+#define AMD64_GARTCACHECTL 0x9c
+#define AMD64_GARTEN (1<<0)
+
+/* NVIDIA K8 registers */
+#define NVIDIA_X86_64_0_APBASE 0x10
+#define NVIDIA_X86_64_1_APBASE1 0x50
+#define NVIDIA_X86_64_1_APLIMIT1 0x54
+#define NVIDIA_X86_64_1_APSIZE 0xa8
+#define NVIDIA_X86_64_1_APBASE2 0xd8
+#define NVIDIA_X86_64_1_APLIMIT2 0xdc
+
+/* ULi K8 registers */
+#define ULI_X86_64_BASE_ADDR 0x10
+#define ULI_X86_64_HTT_FEA_REG 0x50
+#define ULI_X86_64_ENU_SCR_REG 0x54
+
+static int nr_garts;
+static struct pci_dev * hammers[MAX_HAMMER_GARTS];
+
+static struct resource *aperture_resource;
+static int __initdata agp_try_unsupported;
+
+static int gart_iterator;
+#define for_each_nb() for(gart_iterator=0;gart_iterator<nr_garts;gart_iterator++)
+
+static void flush_amd64_tlb(struct pci_dev *dev)
+{
+ u32 tmp;
+
+ pci_read_config_dword (dev, AMD64_GARTCACHECTL, &tmp);
+ tmp |= INVGART;
+ pci_write_config_dword (dev, AMD64_GARTCACHECTL, tmp);
+}
+
+static void amd64_tlbflush(struct agp_memory *temp)
+{
+ for_each_nb()
+ flush_amd64_tlb(hammers[gart_iterator]);
+}
+
+static int amd64_insert_memory(struct agp_memory *mem, off_t pg_start, int type)
+{
+ int i, j, num_entries;
+ long long tmp;
+ u32 pte;
+
+ num_entries = agp_num_entries();
+
+ if (type != 0 || mem->type != 0)
+ return -EINVAL;
+
+ /* Make sure we can fit the range in the gatt table. */
+ /* FIXME: could wrap */
+ if (((unsigned long)pg_start + mem->page_count) > num_entries)
+ return -EINVAL;
+
+ j = pg_start;
+
+ /* gatt table should be empty. */
+ while (j < (pg_start + mem->page_count)) {
+ if (!PGE_EMPTY(agp_bridge, readl(agp_bridge->gatt_table+j)))
+ return -EBUSY;
+ j++;
+ }
+
+ if (mem->is_flushed == FALSE) {
+ global_cache_flush();
+ mem->is_flushed = TRUE;
+ }
+
+ for (i = 0, j = pg_start; i < mem->page_count; i++, j++) {
+ tmp = agp_bridge->driver->mask_memory(agp_bridge,
+ mem->memory[i], mem->type);
+
+ BUG_ON(tmp & 0xffffff0000000ffcULL);
+ pte = (tmp & 0x000000ff00000000ULL) >> 28;
+ pte |=(tmp & 0x00000000fffff000ULL);
+ pte |= GPTE_VALID | GPTE_COHERENT;
+
+ writel(pte, agp_bridge->gatt_table+j);
+ readl(agp_bridge->gatt_table+j); /* PCI Posting. */
+ }
+ amd64_tlbflush(mem);
+ return 0;
+}
+
+/*
+ * This hack alters the order element according
+ * to the size of a long. It sucks. I totally disown this, even
+ * though it does appear to work for the most part.
+ */
+static struct aper_size_info_32 amd64_aperture_sizes[7] =
+{
+ {32, 8192, 3+(sizeof(long)/8), 0 },
+ {64, 16384, 4+(sizeof(long)/8), 1<<1 },
+ {128, 32768, 5+(sizeof(long)/8), 1<<2 },
+ {256, 65536, 6+(sizeof(long)/8), 1<<1 | 1<<2 },
+ {512, 131072, 7+(sizeof(long)/8), 1<<3 },
+ {1024, 262144, 8+(sizeof(long)/8), 1<<1 | 1<<3},
+ {2048, 524288, 9+(sizeof(long)/8), 1<<2 | 1<<3}
+};
+
+
+/*
+ * Get the current Aperture size from the x86-64.
+ * Note, that there may be multiple x86-64's, but we just return
+ * the value from the first one we find. The set_size functions
+ * keep the rest coherent anyway. Or at least should do.
+ */
+static int amd64_fetch_size(void)
+{
+ struct pci_dev *dev;
+ int i;
+ u32 temp;
+ struct aper_size_info_32 *values;
+
+ dev = hammers[0];
+ if (dev==NULL)
+ return 0;
+
+ pci_read_config_dword(dev, AMD64_GARTAPERTURECTL, &temp);
+ temp = (temp & 0xe);
+ values = A_SIZE_32(amd64_aperture_sizes);
+
+ for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++) {
+ if (temp == values[i].size_value) {
+ agp_bridge->previous_size =
+ agp_bridge->current_size = (void *) (values + i);
+
+ agp_bridge->aperture_size_idx = i;
+ return values[i].size;
+ }
+ }
+ return 0;
+}
+
+/*
+ * In a multiprocessor x86-64 system, this function gets
+ * called once for each CPU.
+ */
+static u64 amd64_configure (struct pci_dev *hammer, u64 gatt_table)
+{
+ u64 aperturebase;
+ u32 tmp;
+ u64 addr, aper_base;
+
+ /* Address to map to */
+ pci_read_config_dword (hammer, AMD64_GARTAPERTUREBASE, &tmp);
+ aperturebase = tmp << 25;
+ aper_base = (aperturebase & PCI_BASE_ADDRESS_MEM_MASK);
+
+ /* address of the mappings table */
+ addr = (u64) gatt_table;
+ addr >>= 12;
+ tmp = (u32) addr<<4;
+ tmp &= ~0xf;
+ pci_write_config_dword (hammer, AMD64_GARTTABLEBASE, tmp);
+
+ /* Enable GART translation for this hammer. */
+ pci_read_config_dword(hammer, AMD64_GARTAPERTURECTL, &tmp);
+ tmp |= GARTEN;
+ tmp &= ~(DISGARTCPU | DISGARTIO);
+ pci_write_config_dword(hammer, AMD64_GARTAPERTURECTL, tmp);
+
+ /* keep CPU's coherent. */
+ flush_amd64_tlb (hammer);
+
+ return aper_base;
+}
+
+
+static struct aper_size_info_32 amd_8151_sizes[7] =
+{
+ {2048, 524288, 9, 0x00000000 }, /* 0 0 0 0 0 0 */
+ {1024, 262144, 8, 0x00000400 }, /* 1 0 0 0 0 0 */
+ {512, 131072, 7, 0x00000600 }, /* 1 1 0 0 0 0 */
+ {256, 65536, 6, 0x00000700 }, /* 1 1 1 0 0 0 */
+ {128, 32768, 5, 0x00000720 }, /* 1 1 1 1 0 0 */
+ {64, 16384, 4, 0x00000730 }, /* 1 1 1 1 1 0 */
+ {32, 8192, 3, 0x00000738 } /* 1 1 1 1 1 1 */
+};
+
+static int amd_8151_configure(void)
+{
+ unsigned long gatt_bus = virt_to_phys(agp_bridge->gatt_table_real);
+
+ /* Configure AGP regs in each x86-64 host bridge. */
+ for_each_nb() {
+ agp_bridge->gart_bus_addr =
+ amd64_configure(hammers[gart_iterator],gatt_bus);
+ }
+ return 0;
+}
+
+
+static void amd64_cleanup(void)
+{
+ u32 tmp;
+
+ for_each_nb() {
+ /* disable gart translation */
+ pci_read_config_dword (hammers[gart_iterator], AMD64_GARTAPERTURECTL, &tmp);
+ tmp &= ~AMD64_GARTEN;
+ pci_write_config_dword (hammers[gart_iterator], AMD64_GARTAPERTURECTL, tmp);
+ }
+}
+
+
+struct agp_bridge_driver amd_8151_driver = {
+ .owner = THIS_MODULE,
+ .aperture_sizes = amd_8151_sizes,
+ .size_type = U32_APER_SIZE,
+ .num_aperture_sizes = 7,
+ .configure = amd_8151_configure,
+ .fetch_size = amd64_fetch_size,
+ .cleanup = amd64_cleanup,
+ .tlb_flush = amd64_tlbflush,
+ .mask_memory = agp_generic_mask_memory,
+ .masks = NULL,
+ .agp_enable = agp_generic_enable,
+ .cache_flush = global_cache_flush,
+ .create_gatt_table = agp_generic_create_gatt_table,
+ .free_gatt_table = agp_generic_free_gatt_table,
+ .insert_memory = amd64_insert_memory,
+ .remove_memory = agp_generic_remove_memory,
+ .alloc_by_type = agp_generic_alloc_by_type,
+ .free_by_type = agp_generic_free_by_type,
+ .agp_alloc_page = agp_generic_alloc_page,
+ .agp_destroy_page = agp_generic_destroy_page,
+};
+
+/* Some basic sanity checks for the aperture. */
+static int __devinit aperture_valid(u64 aper, u32 size)
+{
+ u32 pfn, c;
+ if (aper == 0) {
+ printk(KERN_ERR PFX "No aperture\n");
+ return 0;
+ }
+ if (size < 32*1024*1024) {
+ printk(KERN_ERR PFX "Aperture too small (%d MB)\n", size>>20);
+ return 0;
+ }
+ if (aper + size > 0xffffffff) {
+ printk(KERN_ERR PFX "Aperture out of bounds\n");
+ return 0;
+ }
+ pfn = aper >> PAGE_SHIFT;
+ for (c = 0; c < size/PAGE_SIZE; c++) {
+ if (!pfn_valid(pfn + c))
+ break;
+ if (!PageReserved(pfn_to_page(pfn + c))) {
+ printk(KERN_ERR PFX "Aperture pointing to RAM\n");
+ return 0;
+ }
+ }
+
+ /* Request the Aperture. This catches cases when someone else
+ already put a mapping in there - happens with some very broken BIOS
+
+ Maybe better to use pci_assign_resource/pci_enable_device instead
+ trusting the bridges? */
+ if (!aperture_resource &&
+ !(aperture_resource = request_mem_region(aper, size, "aperture"))) {
+ printk(KERN_ERR PFX "Aperture conflicts with PCI mapping.\n");
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * W*s centric BIOS sometimes only set up the aperture in the AGP
+ * bridge, not the northbridge. On AMD64 this is handled early
+ * in aperture.c, but when GART_IOMMU is not enabled or we run
+ * on a 32bit kernel this needs to be redone.
+ * Unfortunately it is impossible to fix the aperture here because it's too late
+ * to allocate that much memory. But at least error out cleanly instead of
+ * crashing.
+ */
+static __devinit int fix_northbridge(struct pci_dev *nb, struct pci_dev *agp,
+ u16 cap)
+{
+ u32 aper_low, aper_hi;
+ u64 aper, nb_aper;
+ int order = 0;
+ u32 nb_order, nb_base;
+ u16 apsize;
+
+ pci_read_config_dword(nb, 0x90, &nb_order);
+ nb_order = (nb_order >> 1) & 7;
+ pci_read_config_dword(nb, 0x94, &nb_base);
+ nb_aper = nb_base << 25;
+ if (aperture_valid(nb_aper, (32*1024*1024)<<nb_order)) {
+ return 0;
+ }
+
+ /* Northbridge seems to contain crap. Try the AGP bridge. */
+
+ pci_read_config_word(agp, cap+0x14, &apsize);
+ if (apsize == 0xffff)
+ return -1;
+
+ apsize &= 0xfff;
+ /* Some BIOS use weird encodings not in the AGPv3 table. */
+ if (apsize & 0xff)
+ apsize |= 0xf00;
+ order = 7 - hweight16(apsize);
+
+ pci_read_config_dword(agp, 0x10, &aper_low);
+ pci_read_config_dword(agp, 0x14, &aper_hi);
+ aper = (aper_low & ~((1<<22)-1)) | ((u64)aper_hi << 32);
+ printk(KERN_INFO PFX "Aperture from AGP @ %Lx size %u MB\n", aper, 32 << order);
+ if (order < 0 || !aperture_valid(aper, (32*1024*1024)<<order))
+ return -1;
+
+ pci_write_config_dword(nb, 0x90, order << 1);
+ pci_write_config_dword(nb, 0x94, aper >> 25);
+
+ return 0;
+}
+
+static __devinit int cache_nbs (struct pci_dev *pdev, u32 cap_ptr)
+{
+ struct pci_dev *loop_dev = NULL;
+ int i = 0;
+
+ /* cache pci_devs of northbridges. */
+ while ((loop_dev = pci_get_device(PCI_VENDOR_ID_AMD, 0x1103, loop_dev))
+ != NULL) {
+ if (i == MAX_HAMMER_GARTS) {
+ printk(KERN_ERR PFX "Too many northbridges for AGP\n");
+ return -1;
+ }
+ if (fix_northbridge(loop_dev, pdev, cap_ptr) < 0) {
+ printk(KERN_ERR PFX "No usable aperture found.\n");
+#ifdef __x86_64__
+ /* should port this to i386 */
+ printk(KERN_ERR PFX "Consider rebooting with iommu=memaper=2 to get a good aperture.\n");
+#endif
+ return -1;
+ }
+ hammers[i++] = loop_dev;
+ }
+ nr_garts = i;
+ return i == 0 ? -1 : 0;
+}
+
+/* Handle AMD 8151 quirks */
+static void __devinit amd8151_init(struct pci_dev *pdev, struct agp_bridge_data *bridge)
+{
+ char *revstring;
+ u8 rev_id;
+
+ pci_read_config_byte(pdev, PCI_REVISION_ID, &rev_id);
+ switch (rev_id) {
+ case 0x01: revstring="A0"; break;
+ case 0x02: revstring="A1"; break;
+ case 0x11: revstring="B0"; break;
+ case 0x12: revstring="B1"; break;
+ case 0x13: revstring="B2"; break;
+ case 0x14: revstring="B3"; break;
+ default: revstring="??"; break;
+ }
+
+ printk (KERN_INFO PFX "Detected AMD 8151 AGP Bridge rev %s\n", revstring);
+
+ /*
+ * Work around errata.
+ * Chips before B2 stepping incorrectly reporting v3.5
+ */
+ if (rev_id < 0x13) {
+ printk (KERN_INFO PFX "Correcting AGP revision (reports 3.5, is really 3.0)\n");
+ bridge->major_version = 3;
+ bridge->minor_version = 0;
+ }
+}
+
+
+static struct aper_size_info_32 uli_sizes[7] =
+{
+ {256, 65536, 6, 10},
+ {128, 32768, 5, 9},
+ {64, 16384, 4, 8},
+ {32, 8192, 3, 7},
+ {16, 4096, 2, 6},
+ {8, 2048, 1, 4},
+ {4, 1024, 0, 3}
+};
+static int __devinit uli_agp_init(struct pci_dev *pdev)
+{
+ u32 httfea,baseaddr,enuscr;
+ struct pci_dev *dev1;
+ int i;
+ unsigned size = amd64_fetch_size();
+ printk(KERN_INFO "Setting up ULi AGP. \n");
+ dev1 = pci_find_slot ((unsigned int)pdev->bus->number,PCI_DEVFN(0,0));
+ if (dev1 == NULL) {
+ printk(KERN_INFO PFX "Detected a ULi chipset, "
+ "but could not fine the secondary device.\n");
+ return -ENODEV;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(uli_sizes); i++)
+ if (uli_sizes[i].size == size)
+ break;
+
+ if (i == ARRAY_SIZE(uli_sizes)) {
+ printk(KERN_INFO PFX "No ULi size found for %d\n", size);
+ return -ENODEV;
+ }
+
+ /* shadow x86-64 registers into ULi registers */
+ pci_read_config_dword (hammers[0], AMD64_GARTAPERTUREBASE, &httfea);
+
+ /* if x86-64 aperture base is beyond 4G, exit here */
+ if ((httfea & 0x7fff) >> (32 - 25))
+ return -ENODEV;
+
+ httfea = (httfea& 0x7fff) << 25;
+
+ pci_read_config_dword(pdev, ULI_X86_64_BASE_ADDR, &baseaddr);
+ baseaddr&= ~PCI_BASE_ADDRESS_MEM_MASK;
+ baseaddr|= httfea;
+ pci_write_config_dword(pdev, ULI_X86_64_BASE_ADDR, baseaddr);
+
+ enuscr= httfea+ (size * 1024 * 1024) - 1;
+ pci_write_config_dword(dev1, ULI_X86_64_HTT_FEA_REG, httfea);
+ pci_write_config_dword(dev1, ULI_X86_64_ENU_SCR_REG, enuscr);
+ return 0;
+}
+
+
+static struct aper_size_info_32 nforce3_sizes[5] =
+{
+ {512, 131072, 7, 0x00000000 },
+ {256, 65536, 6, 0x00000008 },
+ {128, 32768, 5, 0x0000000C },
+ {64, 16384, 4, 0x0000000E },
+ {32, 8192, 3, 0x0000000F }
+};
+
+/* Handle shadow device of the Nvidia NForce3 */
+/* CHECK-ME original 2.4 version set up some IORRs. Check if that is needed. */
+static int __devinit nforce3_agp_init(struct pci_dev *pdev)
+{
+ u32 tmp, apbase, apbar, aplimit;
+ struct pci_dev *dev1;
+ int i;
+ unsigned size = amd64_fetch_size();
+
+ printk(KERN_INFO PFX "Setting up Nforce3 AGP.\n");
+
+ dev1 = pci_find_slot((unsigned int)pdev->bus->number, PCI_DEVFN(11, 0));
+ if (dev1 == NULL) {
+ printk(KERN_INFO PFX "agpgart: Detected an NVIDIA "
+ "nForce3 chipset, but could not find "
+ "the secondary device.\n");
+ return -ENODEV;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(nforce3_sizes); i++)
+ if (nforce3_sizes[i].size == size)
+ break;
+
+ if (i == ARRAY_SIZE(nforce3_sizes)) {
+ printk(KERN_INFO PFX "No NForce3 size found for %d\n", size);
+ return -ENODEV;
+ }
+
+ pci_read_config_dword(dev1, NVIDIA_X86_64_1_APSIZE, &tmp);
+ tmp &= ~(0xf);
+ tmp |= nforce3_sizes[i].size_value;
+ pci_write_config_dword(dev1, NVIDIA_X86_64_1_APSIZE, tmp);
+
+ /* shadow x86-64 registers into NVIDIA registers */
+ pci_read_config_dword (hammers[0], AMD64_GARTAPERTUREBASE, &apbase);
+
+ /* if x86-64 aperture base is beyond 4G, exit here */
+ if ( (apbase & 0x7fff) >> (32 - 25) )
+ return -ENODEV;
+
+ apbase = (apbase & 0x7fff) << 25;
+
+ pci_read_config_dword(pdev, NVIDIA_X86_64_0_APBASE, &apbar);
+ apbar &= ~PCI_BASE_ADDRESS_MEM_MASK;
+ apbar |= apbase;
+ pci_write_config_dword(pdev, NVIDIA_X86_64_0_APBASE, apbar);
+
+ aplimit = apbase + (size * 1024 * 1024) - 1;
+ pci_write_config_dword(dev1, NVIDIA_X86_64_1_APBASE1, apbase);
+ pci_write_config_dword(dev1, NVIDIA_X86_64_1_APLIMIT1, aplimit);
+ pci_write_config_dword(dev1, NVIDIA_X86_64_1_APBASE2, apbase);
+ pci_write_config_dword(dev1, NVIDIA_X86_64_1_APLIMIT2, aplimit);
+
+ return 0;
+}
+
+static int __devinit agp_amd64_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct agp_bridge_data *bridge;
+ u8 cap_ptr;
+
+ cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP);
+ if (!cap_ptr)
+ return -ENODEV;
+
+ /* Could check for AGPv3 here */
+
+ bridge = agp_alloc_bridge();
+ if (!bridge)
+ return -ENOMEM;
+
+ if (pdev->vendor == PCI_VENDOR_ID_AMD &&
+ pdev->device == PCI_DEVICE_ID_AMD_8151_0) {
+ amd8151_init(pdev, bridge);
+ } else {
+ printk(KERN_INFO PFX "Detected AGP bridge %x\n", pdev->devfn);
+ }
+
+ bridge->driver = &amd_8151_driver;
+ bridge->dev = pdev;
+ bridge->capndx = cap_ptr;
+
+ /* Fill in the mode register */
+ pci_read_config_dword(pdev, bridge->capndx+PCI_AGP_STATUS, &bridge->mode);
+
+ if (cache_nbs(pdev, cap_ptr) == -1) {
+ agp_put_bridge(bridge);
+ return -ENODEV;
+ }
+
+ if (pdev->vendor == PCI_VENDOR_ID_NVIDIA) {
+ int ret = nforce3_agp_init(pdev);
+ if (ret) {
+ agp_put_bridge(bridge);
+ return ret;
+ }
+ }
+
+ if (pdev->vendor == PCI_VENDOR_ID_AL) {
+ int ret = uli_agp_init(pdev);
+ if (ret) {
+ agp_put_bridge(bridge);
+ return ret;
+ }
+ }
+
+ pci_set_drvdata(pdev, bridge);
+ return agp_add_bridge(bridge);
+}
+
+static void __devexit agp_amd64_remove(struct pci_dev *pdev)
+{
+ struct agp_bridge_data *bridge = pci_get_drvdata(pdev);
+
+ release_mem_region(virt_to_phys(bridge->gatt_table_real),
+ amd64_aperture_sizes[bridge->aperture_size_idx].size);
+ agp_remove_bridge(bridge);
+ agp_put_bridge(bridge);
+}
+
+static struct pci_device_id agp_amd64_pci_table[] = {
+ {
+ .class = (PCI_CLASS_BRIDGE_HOST << 8),
+ .class_mask = ~0,
+ .vendor = PCI_VENDOR_ID_AMD,
+ .device = PCI_DEVICE_ID_AMD_8151_0,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ },
+ /* ULi M1689 */
+ {
+ .class = (PCI_CLASS_BRIDGE_HOST << 8),
+ .class_mask = ~0,
+ .vendor = PCI_VENDOR_ID_AL,
+ .device = PCI_DEVICE_ID_AL_M1689,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ },
+ /* VIA K8T800Pro */
+ {
+ .class = (PCI_CLASS_BRIDGE_HOST << 8),
+ .class_mask = ~0,
+ .vendor = PCI_VENDOR_ID_VIA,
+ .device = PCI_DEVICE_ID_VIA_K8T800PRO_0,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ },
+ /* VIA K8T800 */
+ {
+ .class = (PCI_CLASS_BRIDGE_HOST << 8),
+ .class_mask = ~0,
+ .vendor = PCI_VENDOR_ID_VIA,
+ .device = PCI_DEVICE_ID_VIA_8385_0,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ },
+ /* VIA K8M800 / K8N800 */
+ {
+ .class = (PCI_CLASS_BRIDGE_HOST << 8),
+ .class_mask = ~0,
+ .vendor = PCI_VENDOR_ID_VIA,
+ .device = PCI_DEVICE_ID_VIA_8380_0,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ },
+ /* VIA K8T890 */
+ {
+ .class = (PCI_CLASS_BRIDGE_HOST << 8),
+ .class_mask = ~0,
+ .vendor = PCI_VENDOR_ID_VIA,
+ .device = PCI_DEVICE_ID_VIA_3238_0,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ },
+ /* VIA K8T800/K8M800/K8N800 */
+ {
+ .class = (PCI_CLASS_BRIDGE_HOST << 8),
+ .class_mask = ~0,
+ .vendor = PCI_VENDOR_ID_VIA,
+ .device = PCI_DEVICE_ID_VIA_838X_1,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ },
+ /* NForce3 */
+ {
+ .class = (PCI_CLASS_BRIDGE_HOST << 8),
+ .class_mask = ~0,
+ .vendor = PCI_VENDOR_ID_NVIDIA,
+ .device = PCI_DEVICE_ID_NVIDIA_NFORCE3,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ },
+ {
+ .class = (PCI_CLASS_BRIDGE_HOST << 8),
+ .class_mask = ~0,
+ .vendor = PCI_VENDOR_ID_NVIDIA,
+ .device = PCI_DEVICE_ID_NVIDIA_NFORCE3S,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ },
+ /* SIS 755 */
+ {
+ .class = (PCI_CLASS_BRIDGE_HOST << 8),
+ .class_mask = ~0,
+ .vendor = PCI_VENDOR_ID_SI,
+ .device = PCI_DEVICE_ID_SI_755,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ },
+ { }
+};
+
+MODULE_DEVICE_TABLE(pci, agp_amd64_pci_table);
+
+static struct pci_driver agp_amd64_pci_driver = {
+ .name = "agpgart-amd64",
+ .id_table = agp_amd64_pci_table,
+ .probe = agp_amd64_probe,
+ .remove = agp_amd64_remove,
+};
+
+
+/* Not static due to IOMMU code calling it early. */
+int __init agp_amd64_init(void)
+{
+ int err = 0;
+ static struct pci_device_id amd64nb[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x1103) },
+ { },
+ };
+
+ if (agp_off)
+ return -EINVAL;
+ if (pci_register_driver(&agp_amd64_pci_driver) > 0) {
+ struct pci_dev *dev;
+ if (!agp_try_unsupported && !agp_try_unsupported_boot) {
+ printk(KERN_INFO PFX "No supported AGP bridge found.\n");
+#ifdef MODULE
+ printk(KERN_INFO PFX "You can try agp_try_unsupported=1\n");
+#else
+ printk(KERN_INFO PFX "You can boot with agp=try_unsupported\n");
+#endif
+ return -ENODEV;
+ }
+
+ /* First check that we have at least one AMD64 NB */
+ if (!pci_dev_present(amd64nb))
+ return -ENODEV;
+
+ /* Look for any AGP bridge */
+ dev = NULL;
+ err = -ENODEV;
+ for_each_pci_dev(dev) {
+ if (!pci_find_capability(dev, PCI_CAP_ID_AGP))
+ continue;
+ /* Only one bridge supported right now */
+ if (agp_amd64_probe(dev, NULL) == 0) {
+ err = 0;
+ break;
+ }
+ }
+ }
+ return err;
+}
+
+static void __exit agp_amd64_cleanup(void)
+{
+ if (aperture_resource)
+ release_resource(aperture_resource);
+ pci_unregister_driver(&agp_amd64_pci_driver);
+}
+
+/* On AMD64 the PCI driver needs to initialize this driver early
+ for the IOMMU, so it has to be called via a backdoor. */
+#ifndef CONFIG_GART_IOMMU
+module_init(agp_amd64_init);
+module_exit(agp_amd64_cleanup);
+#endif
+
+MODULE_AUTHOR("Dave Jones <davej@codemonkey.org.uk>, Andi Kleen");
+module_param(agp_try_unsupported, bool, 0);
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/agp/ati-agp.c b/drivers/char/agp/ati-agp.c
new file mode 100644
index 00000000000..757dde006fc
--- /dev/null
+++ b/drivers/char/agp/ati-agp.c
@@ -0,0 +1,548 @@
+/*
+ * ATi AGPGART routines.
+ */
+
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/agp_backend.h>
+#include <asm/agp.h>
+#include "agp.h"
+
+#define ATI_GART_MMBASE_ADDR 0x14
+#define ATI_RS100_APSIZE 0xac
+#define ATI_RS100_IG_AGPMODE 0xb0
+#define ATI_RS300_APSIZE 0xf8
+#define ATI_RS300_IG_AGPMODE 0xfc
+#define ATI_GART_FEATURE_ID 0x00
+#define ATI_GART_BASE 0x04
+#define ATI_GART_CACHE_SZBASE 0x08
+#define ATI_GART_CACHE_CNTRL 0x0c
+#define ATI_GART_CACHE_ENTRY_CNTRL 0x10
+
+
+static struct aper_size_info_lvl2 ati_generic_sizes[7] =
+{
+ {2048, 524288, 0x0000000c},
+ {1024, 262144, 0x0000000a},
+ {512, 131072, 0x00000008},
+ {256, 65536, 0x00000006},
+ {128, 32768, 0x00000004},
+ {64, 16384, 0x00000002},
+ {32, 8192, 0x00000000}
+};
+
+static struct gatt_mask ati_generic_masks[] =
+{
+ { .mask = 1, .type = 0}
+};
+
+
+
+typedef struct _ati_page_map {
+ unsigned long *real;
+ unsigned long __iomem *remapped;
+} ati_page_map;
+
+static struct _ati_generic_private {
+ volatile u8 __iomem *registers;
+ ati_page_map **gatt_pages;
+ int num_tables;
+} ati_generic_private;
+
+static int ati_create_page_map(ati_page_map *page_map)
+{
+ int i, err = 0;
+
+ page_map->real = (unsigned long *) __get_free_page(GFP_KERNEL);
+ if (page_map->real == NULL)
+ return -ENOMEM;
+
+ SetPageReserved(virt_to_page(page_map->real));
+ err = map_page_into_agp(virt_to_page(page_map->real));
+ page_map->remapped = ioremap_nocache(virt_to_phys(page_map->real),
+ PAGE_SIZE);
+ if (page_map->remapped == NULL || err) {
+ ClearPageReserved(virt_to_page(page_map->real));
+ free_page((unsigned long) page_map->real);
+ page_map->real = NULL;
+ return -ENOMEM;
+ }
+ /*CACHE_FLUSH();*/
+ global_cache_flush();
+
+ for(i = 0; i < PAGE_SIZE / sizeof(unsigned long); i++) {
+ writel(agp_bridge->scratch_page, page_map->remapped+i);
+ readl(page_map->remapped+i); /* PCI Posting. */
+ }
+
+ return 0;
+}
+
+
+static void ati_free_page_map(ati_page_map *page_map)
+{
+ unmap_page_from_agp(virt_to_page(page_map->real));
+ iounmap(page_map->remapped);
+ ClearPageReserved(virt_to_page(page_map->real));
+ free_page((unsigned long) page_map->real);
+}
+
+
+static void ati_free_gatt_pages(void)
+{
+ int i;
+ ati_page_map **tables;
+ ati_page_map *entry;
+
+ tables = ati_generic_private.gatt_pages;
+ for(i = 0; i < ati_generic_private.num_tables; i++) {
+ entry = tables[i];
+ if (entry != NULL) {
+ if (entry->real != NULL)
+ ati_free_page_map(entry);
+ kfree(entry);
+ }
+ }
+ kfree(tables);
+}
+
+
+static int ati_create_gatt_pages(int nr_tables)
+{
+ ati_page_map **tables;
+ ati_page_map *entry;
+ int retval = 0;
+ int i;
+
+ tables = kmalloc((nr_tables + 1) * sizeof(ati_page_map *),
+ GFP_KERNEL);
+ if (tables == NULL)
+ return -ENOMEM;
+
+ memset(tables, 0, sizeof(ati_page_map *) * (nr_tables + 1));
+ for (i = 0; i < nr_tables; i++) {
+ entry = kmalloc(sizeof(ati_page_map), GFP_KERNEL);
+ if (entry == NULL) {
+ while (i>0) {
+ kfree (tables[i-1]);
+ i--;
+ }
+ kfree (tables);
+ tables = NULL;
+ retval = -ENOMEM;
+ break;
+ }
+ memset(entry, 0, sizeof(ati_page_map));
+ tables[i] = entry;
+ retval = ati_create_page_map(entry);
+ if (retval != 0) break;
+ }
+ ati_generic_private.num_tables = nr_tables;
+ ati_generic_private.gatt_pages = tables;
+
+ if (retval != 0) ati_free_gatt_pages();
+
+ return retval;
+}
+
+static int is_r200(void)
+{
+ if ((agp_bridge->dev->device == PCI_DEVICE_ID_ATI_RS100) ||
+ (agp_bridge->dev->device == PCI_DEVICE_ID_ATI_RS200) ||
+ (agp_bridge->dev->device == PCI_DEVICE_ID_ATI_RS200_B) ||
+ (agp_bridge->dev->device == PCI_DEVICE_ID_ATI_RS250))
+ return 1;
+ return 0;
+}
+
+static int ati_fetch_size(void)
+{
+ int i;
+ u32 temp;
+ struct aper_size_info_lvl2 *values;
+
+ if (is_r200())
+ pci_read_config_dword(agp_bridge->dev, ATI_RS100_APSIZE, &temp);
+ else
+ pci_read_config_dword(agp_bridge->dev, ATI_RS300_APSIZE, &temp);
+
+ temp = (temp & 0x0000000e);
+ values = A_SIZE_LVL2(agp_bridge->driver->aperture_sizes);
+ for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++) {
+ if (temp == values[i].size_value) {
+ agp_bridge->previous_size =
+ agp_bridge->current_size = (void *) (values + i);
+
+ agp_bridge->aperture_size_idx = i;
+ return values[i].size;
+ }
+ }
+
+ return 0;
+}
+
+static void ati_tlbflush(struct agp_memory * mem)
+{
+ writel(1, ati_generic_private.registers+ATI_GART_CACHE_CNTRL);
+ readl(ati_generic_private.registers+ATI_GART_CACHE_CNTRL); /* PCI Posting. */
+}
+
+static void ati_cleanup(void)
+{
+ struct aper_size_info_lvl2 *previous_size;
+ u32 temp;
+
+ previous_size = A_SIZE_LVL2(agp_bridge->previous_size);
+
+ /* Write back the previous size and disable gart translation */
+ if (is_r200()) {
+ pci_read_config_dword(agp_bridge->dev, ATI_RS100_APSIZE, &temp);
+ temp = ((temp & ~(0x0000000f)) | previous_size->size_value);
+ pci_write_config_dword(agp_bridge->dev, ATI_RS100_APSIZE, temp);
+ } else {
+ pci_read_config_dword(agp_bridge->dev, ATI_RS300_APSIZE, &temp);
+ temp = ((temp & ~(0x0000000f)) | previous_size->size_value);
+ pci_write_config_dword(agp_bridge->dev, ATI_RS300_APSIZE, temp);
+ }
+ iounmap((volatile u8 __iomem *)ati_generic_private.registers);
+}
+
+
+static int ati_configure(void)
+{
+ u32 temp;
+
+ /* Get the memory mapped registers */
+ pci_read_config_dword(agp_bridge->dev, ATI_GART_MMBASE_ADDR, &temp);
+ temp = (temp & 0xfffff000);
+ ati_generic_private.registers = (volatile u8 __iomem *) ioremap(temp, 4096);
+
+ if (is_r200())
+ pci_write_config_dword(agp_bridge->dev, ATI_RS100_IG_AGPMODE, 0x20000);
+ else
+ pci_write_config_dword(agp_bridge->dev, ATI_RS300_IG_AGPMODE, 0x20000);
+
+ /* address to map too */
+ /*
+ pci_read_config_dword(agp_bridge.dev, AGP_APBASE, &temp);
+ agp_bridge.gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+ printk(KERN_INFO PFX "IGP320 gart_bus_addr: %x\n", agp_bridge.gart_bus_addr);
+ */
+ writel(0x60000, ati_generic_private.registers+ATI_GART_FEATURE_ID);
+ readl(ati_generic_private.registers+ATI_GART_FEATURE_ID); /* PCI Posting.*/
+
+ /* SIGNALED_SYSTEM_ERROR @ NB_STATUS */
+ pci_read_config_dword(agp_bridge->dev, 4, &temp);
+ pci_write_config_dword(agp_bridge->dev, 4, temp | (1<<14));
+
+ /* Write out the address of the gatt table */
+ writel(agp_bridge->gatt_bus_addr, ati_generic_private.registers+ATI_GART_BASE);
+ readl(ati_generic_private.registers+ATI_GART_BASE); /* PCI Posting. */
+
+ return 0;
+}
+
+
+/*
+ *Since we don't need contigious memory we just try
+ * to get the gatt table once
+ */
+
+#define GET_PAGE_DIR_OFF(addr) (addr >> 22)
+#define GET_PAGE_DIR_IDX(addr) (GET_PAGE_DIR_OFF(addr) - \
+ GET_PAGE_DIR_OFF(agp_bridge->gart_bus_addr))
+#define GET_GATT_OFF(addr) ((addr & 0x003ff000) >> 12)
+#undef GET_GATT
+#define GET_GATT(addr) (ati_generic_private.gatt_pages[\
+ GET_PAGE_DIR_IDX(addr)]->remapped)
+
+static int ati_insert_memory(struct agp_memory * mem,
+ off_t pg_start, int type)
+{
+ int i, j, num_entries;
+ unsigned long __iomem *cur_gatt;
+ unsigned long addr;
+
+ num_entries = A_SIZE_LVL2(agp_bridge->current_size)->num_entries;
+
+ if (type != 0 || mem->type != 0)
+ return -EINVAL;
+
+ if ((pg_start + mem->page_count) > num_entries)
+ return -EINVAL;
+
+ j = pg_start;
+ while (j < (pg_start + mem->page_count)) {
+ addr = (j * PAGE_SIZE) + agp_bridge->gart_bus_addr;
+ cur_gatt = GET_GATT(addr);
+ if (!PGE_EMPTY(agp_bridge,readl(cur_gatt+GET_GATT_OFF(addr))))
+ return -EBUSY;
+ j++;
+ }
+
+ if (mem->is_flushed == FALSE) {
+ /*CACHE_FLUSH(); */
+ global_cache_flush();
+ mem->is_flushed = TRUE;
+ }
+
+ for (i = 0, j = pg_start; i < mem->page_count; i++, j++) {
+ addr = (j * PAGE_SIZE) + agp_bridge->gart_bus_addr;
+ cur_gatt = GET_GATT(addr);
+ writel(agp_bridge->driver->mask_memory(agp_bridge,
+ mem->memory[i], mem->type), cur_gatt+GET_GATT_OFF(addr));
+ readl(cur_gatt+GET_GATT_OFF(addr)); /* PCI Posting. */
+ }
+ agp_bridge->driver->tlb_flush(mem);
+ return 0;
+}
+
+static int ati_remove_memory(struct agp_memory * mem, off_t pg_start,
+ int type)
+{
+ int i;
+ unsigned long __iomem *cur_gatt;
+ unsigned long addr;
+
+ if (type != 0 || mem->type != 0) {
+ return -EINVAL;
+ }
+ for (i = pg_start; i < (mem->page_count + pg_start); i++) {
+ addr = (i * PAGE_SIZE) + agp_bridge->gart_bus_addr;
+ cur_gatt = GET_GATT(addr);
+ writel(agp_bridge->scratch_page, cur_gatt+GET_GATT_OFF(addr));
+ readl(cur_gatt+GET_GATT_OFF(addr)); /* PCI Posting. */
+ }
+
+ agp_bridge->driver->tlb_flush(mem);
+ return 0;
+}
+
+static int ati_create_gatt_table(struct agp_bridge_data *bridge)
+{
+ struct aper_size_info_lvl2 *value;
+ ati_page_map page_dir;
+ unsigned long addr;
+ int retval;
+ u32 temp;
+ int i;
+ struct aper_size_info_lvl2 *current_size;
+
+ value = A_SIZE_LVL2(agp_bridge->current_size);
+ retval = ati_create_page_map(&page_dir);
+ if (retval != 0)
+ return retval;
+
+ retval = ati_create_gatt_pages(value->num_entries / 1024);
+ if (retval != 0) {
+ ati_free_page_map(&page_dir);
+ return retval;
+ }
+
+ agp_bridge->gatt_table_real = (u32 *)page_dir.real;
+ agp_bridge->gatt_table = (u32 __iomem *) page_dir.remapped;
+ agp_bridge->gatt_bus_addr = virt_to_bus(page_dir.real);
+
+ /* Write out the size register */
+ current_size = A_SIZE_LVL2(agp_bridge->current_size);
+
+ if (is_r200()) {
+ pci_read_config_dword(agp_bridge->dev, ATI_RS100_APSIZE, &temp);
+ temp = (((temp & ~(0x0000000e)) | current_size->size_value)
+ | 0x00000001);
+ pci_write_config_dword(agp_bridge->dev, ATI_RS100_APSIZE, temp);
+ pci_read_config_dword(agp_bridge->dev, ATI_RS100_APSIZE, &temp);
+ } else {
+ pci_read_config_dword(agp_bridge->dev, ATI_RS300_APSIZE, &temp);
+ temp = (((temp & ~(0x0000000e)) | current_size->size_value)
+ | 0x00000001);
+ pci_write_config_dword(agp_bridge->dev, ATI_RS300_APSIZE, temp);
+ pci_read_config_dword(agp_bridge->dev, ATI_RS300_APSIZE, &temp);
+ }
+
+ /*
+ * Get the address for the gart region.
+ * This is a bus address even on the alpha, b/c its
+ * used to program the agp master not the cpu
+ */
+ pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp);
+ addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+ agp_bridge->gart_bus_addr = addr;
+
+ /* Calculate the agp offset */
+ for(i = 0; i < value->num_entries / 1024; i++, addr += 0x00400000) {
+ writel(virt_to_bus(ati_generic_private.gatt_pages[i]->real) | 1,
+ page_dir.remapped+GET_PAGE_DIR_OFF(addr));
+ readl(page_dir.remapped+GET_PAGE_DIR_OFF(addr)); /* PCI Posting. */
+ }
+
+ return 0;
+}
+
+static int ati_free_gatt_table(struct agp_bridge_data *bridge)
+{
+ ati_page_map page_dir;
+
+ page_dir.real = (unsigned long *)agp_bridge->gatt_table_real;
+ page_dir.remapped = (unsigned long __iomem *)agp_bridge->gatt_table;
+
+ ati_free_gatt_pages();
+ ati_free_page_map(&page_dir);
+ return 0;
+}
+
+struct agp_bridge_driver ati_generic_bridge = {
+ .owner = THIS_MODULE,
+ .aperture_sizes = ati_generic_sizes,
+ .size_type = LVL2_APER_SIZE,
+ .num_aperture_sizes = 7,
+ .configure = ati_configure,
+ .fetch_size = ati_fetch_size,
+ .cleanup = ati_cleanup,
+ .tlb_flush = ati_tlbflush,
+ .mask_memory = agp_generic_mask_memory,
+ .masks = ati_generic_masks,
+ .agp_enable = agp_generic_enable,
+ .cache_flush = global_cache_flush,
+ .create_gatt_table = ati_create_gatt_table,
+ .free_gatt_table = ati_free_gatt_table,
+ .insert_memory = ati_insert_memory,
+ .remove_memory = ati_remove_memory,
+ .alloc_by_type = agp_generic_alloc_by_type,
+ .free_by_type = agp_generic_free_by_type,
+ .agp_alloc_page = agp_generic_alloc_page,
+ .agp_destroy_page = agp_generic_destroy_page,
+};
+
+
+static struct agp_device_ids ati_agp_device_ids[] __devinitdata =
+{
+ {
+ .device_id = PCI_DEVICE_ID_ATI_RS100,
+ .chipset_name = "IGP320/M",
+ },
+ {
+ .device_id = PCI_DEVICE_ID_ATI_RS200,
+ .chipset_name = "IGP330/340/345/350/M",
+ },
+ {
+ .device_id = PCI_DEVICE_ID_ATI_RS200_B,
+ .chipset_name = "IGP345M",
+ },
+ {
+ .device_id = PCI_DEVICE_ID_ATI_RS250,
+ .chipset_name = "IGP7000/M",
+ },
+ {
+ .device_id = PCI_DEVICE_ID_ATI_RS300_100,
+ .chipset_name = "IGP9100/M",
+ },
+ {
+ .device_id = PCI_DEVICE_ID_ATI_RS300_133,
+ .chipset_name = "IGP9100/M",
+ },
+ {
+ .device_id = PCI_DEVICE_ID_ATI_RS300_166,
+ .chipset_name = "IGP9100/M",
+ },
+ {
+ .device_id = PCI_DEVICE_ID_ATI_RS300_200,
+ .chipset_name = "IGP9100/M",
+ },
+ { }, /* dummy final entry, always present */
+};
+
+static int __devinit agp_ati_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct agp_device_ids *devs = ati_agp_device_ids;
+ struct agp_bridge_data *bridge;
+ u8 cap_ptr;
+ int j;
+
+ cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP);
+ if (!cap_ptr)
+ return -ENODEV;
+
+ /* probe for known chipsets */
+ for (j = 0; devs[j].chipset_name; j++) {
+ if (pdev->device == devs[j].device_id)
+ goto found;
+ }
+
+ printk(KERN_ERR PFX
+ "Unsupported Ati chipset (device id: %04x)\n", pdev->device);
+ return -ENODEV;
+
+found:
+ bridge = agp_alloc_bridge();
+ if (!bridge)
+ return -ENOMEM;
+
+ bridge->dev = pdev;
+ bridge->capndx = cap_ptr;
+
+ bridge->driver = &ati_generic_bridge;
+
+
+ printk(KERN_INFO PFX "Detected Ati %s chipset\n",
+ devs[j].chipset_name);
+
+ /* Fill in the mode register */
+ pci_read_config_dword(pdev,
+ bridge->capndx+PCI_AGP_STATUS,
+ &bridge->mode);
+
+ pci_set_drvdata(pdev, bridge);
+ return agp_add_bridge(bridge);
+}
+
+static void __devexit agp_ati_remove(struct pci_dev *pdev)
+{
+ struct agp_bridge_data *bridge = pci_get_drvdata(pdev);
+
+ agp_remove_bridge(bridge);
+ agp_put_bridge(bridge);
+}
+
+static struct pci_device_id agp_ati_pci_table[] = {
+ {
+ .class = (PCI_CLASS_BRIDGE_HOST << 8),
+ .class_mask = ~0,
+ .vendor = PCI_VENDOR_ID_ATI,
+ .device = PCI_ANY_ID,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ },
+ { }
+};
+
+MODULE_DEVICE_TABLE(pci, agp_ati_pci_table);
+
+static struct pci_driver agp_ati_pci_driver = {
+ .name = "agpgart-ati",
+ .id_table = agp_ati_pci_table,
+ .probe = agp_ati_probe,
+ .remove = agp_ati_remove,
+};
+
+static int __init agp_ati_init(void)
+{
+ if (agp_off)
+ return -EINVAL;
+ return pci_register_driver(&agp_ati_pci_driver);
+}
+
+static void __exit agp_ati_cleanup(void)
+{
+ pci_unregister_driver(&agp_ati_pci_driver);
+}
+
+module_init(agp_ati_init);
+module_exit(agp_ati_cleanup);
+
+MODULE_AUTHOR("Dave Jones <davej@codemonkey.org.uk>");
+MODULE_LICENSE("GPL and additional rights");
+
diff --git a/drivers/char/agp/backend.c b/drivers/char/agp/backend.c
new file mode 100644
index 00000000000..c3442f3c648
--- /dev/null
+++ b/drivers/char/agp/backend.c
@@ -0,0 +1,348 @@
+/*
+ * AGPGART driver backend routines.
+ * Copyright (C) 2004 Silicon Graphics, Inc.
+ * Copyright (C) 2002-2003 Dave Jones.
+ * Copyright (C) 1999 Jeff Hartmann.
+ * Copyright (C) 1999 Precision Insight, Inc.
+ * Copyright (C) 1999 Xi Graphics, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * JEFF HARTMANN, DAVE JONES, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * TODO:
+ * - Allocate more than order 0 pages to avoid too much linear map splitting.
+ */
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/pagemap.h>
+#include <linux/miscdevice.h>
+#include <linux/pm.h>
+#include <linux/agp_backend.h>
+#include <linux/agpgart.h>
+#include <linux/vmalloc.h>
+#include <asm/io.h>
+#include "agp.h"
+
+/* Due to XFree86 brain-damage, we can't go to 1.0 until they
+ * fix some real stupidity. It's only by chance we can bump
+ * past 0.99 at all due to some boolean logic error. */
+#define AGPGART_VERSION_MAJOR 0
+#define AGPGART_VERSION_MINOR 101
+static struct agp_version agp_current_version =
+{
+ .major = AGPGART_VERSION_MAJOR,
+ .minor = AGPGART_VERSION_MINOR,
+};
+
+struct agp_bridge_data *(*agp_find_bridge)(struct pci_dev *) =
+ &agp_generic_find_bridge;
+
+struct agp_bridge_data *agp_bridge;
+LIST_HEAD(agp_bridges);
+EXPORT_SYMBOL(agp_bridge);
+EXPORT_SYMBOL(agp_bridges);
+EXPORT_SYMBOL(agp_find_bridge);
+
+/**
+ * agp_backend_acquire - attempt to acquire an agp backend.
+ *
+ */
+struct agp_bridge_data *agp_backend_acquire(struct pci_dev *pdev)
+{
+ struct agp_bridge_data *bridge;
+
+ bridge = agp_find_bridge(pdev);
+
+ if (!bridge)
+ return NULL;
+
+ if (atomic_read(&bridge->agp_in_use))
+ return NULL;
+ atomic_inc(&bridge->agp_in_use);
+ return bridge;
+}
+EXPORT_SYMBOL(agp_backend_acquire);
+
+
+/**
+ * agp_backend_release - release the lock on the agp backend.
+ *
+ * The caller must insure that the graphics aperture translation table
+ * is read for use by another entity.
+ *
+ * (Ensure that all memory it bound is unbound.)
+ */
+void agp_backend_release(struct agp_bridge_data *bridge)
+{
+
+ if (bridge)
+ atomic_dec(&bridge->agp_in_use);
+}
+EXPORT_SYMBOL(agp_backend_release);
+
+
+struct { int mem, agp; } maxes_table[] = {
+ {0, 0},
+ {32, 4},
+ {64, 28},
+ {128, 96},
+ {256, 204},
+ {512, 440},
+ {1024, 942},
+ {2048, 1920},
+ {4096, 3932}
+};
+
+static int agp_find_max(void)
+{
+ long memory, index, result;
+
+#if PAGE_SHIFT < 20
+ memory = num_physpages >> (20 - PAGE_SHIFT);
+#else
+ memory = num_physpages << (PAGE_SHIFT - 20);
+#endif
+ index = 1;
+
+ while ((memory > maxes_table[index].mem) && (index < 8))
+ index++;
+
+ result = maxes_table[index - 1].agp +
+ ( (memory - maxes_table[index - 1].mem) *
+ (maxes_table[index].agp - maxes_table[index - 1].agp)) /
+ (maxes_table[index].mem - maxes_table[index - 1].mem);
+
+ result = result << (20 - PAGE_SHIFT);
+ return result;
+}
+
+
+static int agp_backend_initialize(struct agp_bridge_data *bridge)
+{
+ int size_value, rc, got_gatt=0, got_keylist=0;
+
+ bridge->max_memory_agp = agp_find_max();
+ bridge->version = &agp_current_version;
+
+ if (bridge->driver->needs_scratch_page) {
+ void *addr = bridge->driver->agp_alloc_page(bridge);
+
+ if (!addr) {
+ printk(KERN_ERR PFX "unable to get memory for scratch page.\n");
+ return -ENOMEM;
+ }
+
+ bridge->scratch_page_real = virt_to_phys(addr);
+ bridge->scratch_page =
+ bridge->driver->mask_memory(bridge, bridge->scratch_page_real, 0);
+ }
+
+ size_value = bridge->driver->fetch_size();
+ if (size_value == 0) {
+ printk(KERN_ERR PFX "unable to determine aperture size.\n");
+ rc = -EINVAL;
+ goto err_out;
+ }
+ if (bridge->driver->create_gatt_table(bridge)) {
+ printk(KERN_ERR PFX
+ "unable to get memory for graphics translation table.\n");
+ rc = -ENOMEM;
+ goto err_out;
+ }
+ got_gatt = 1;
+
+ bridge->key_list = vmalloc(PAGE_SIZE * 4);
+ if (bridge->key_list == NULL) {
+ printk(KERN_ERR PFX "error allocating memory for key lists.\n");
+ rc = -ENOMEM;
+ goto err_out;
+ }
+ got_keylist = 1;
+
+ /* FIXME vmalloc'd memory not guaranteed contiguous */
+ memset(bridge->key_list, 0, PAGE_SIZE * 4);
+
+ if (bridge->driver->configure()) {
+ printk(KERN_ERR PFX "error configuring host chipset.\n");
+ rc = -EINVAL;
+ goto err_out;
+ }
+
+ return 0;
+
+err_out:
+ if (bridge->driver->needs_scratch_page)
+ bridge->driver->agp_destroy_page(
+ phys_to_virt(bridge->scratch_page_real));
+ if (got_gatt)
+ bridge->driver->free_gatt_table(bridge);
+ if (got_keylist) {
+ vfree(bridge->key_list);
+ bridge->key_list = NULL;
+ }
+ return rc;
+}
+
+/* cannot be __exit b/c as it could be called from __init code */
+static void agp_backend_cleanup(struct agp_bridge_data *bridge)
+{
+ if (bridge->driver->cleanup)
+ bridge->driver->cleanup();
+ if (bridge->driver->free_gatt_table)
+ bridge->driver->free_gatt_table(bridge);
+ if (bridge->key_list) {
+ vfree(bridge->key_list);
+ bridge->key_list = NULL;
+ }
+
+ if (bridge->driver->agp_destroy_page &&
+ bridge->driver->needs_scratch_page)
+ bridge->driver->agp_destroy_page(
+ phys_to_virt(bridge->scratch_page_real));
+}
+
+/* When we remove the global variable agp_bridge from all drivers
+ * then agp_alloc_bridge and agp_generic_find_bridge need to be updated
+ */
+
+struct agp_bridge_data *agp_alloc_bridge(void)
+{
+ struct agp_bridge_data *bridge = kmalloc(sizeof(*bridge), GFP_KERNEL);
+
+ if (!bridge)
+ return NULL;
+
+ memset(bridge, 0, sizeof(*bridge));
+ atomic_set(&bridge->agp_in_use, 0);
+ atomic_set(&bridge->current_memory_agp, 0);
+
+ if (list_empty(&agp_bridges))
+ agp_bridge = bridge;
+
+ return bridge;
+}
+EXPORT_SYMBOL(agp_alloc_bridge);
+
+
+void agp_put_bridge(struct agp_bridge_data *bridge)
+{
+ kfree(bridge);
+
+ if (list_empty(&agp_bridges))
+ agp_bridge = NULL;
+}
+EXPORT_SYMBOL(agp_put_bridge);
+
+
+int agp_add_bridge(struct agp_bridge_data *bridge)
+{
+ int error;
+
+ if (agp_off)
+ return -ENODEV;
+
+ if (!bridge->dev) {
+ printk (KERN_DEBUG PFX "Erk, registering with no pci_dev!\n");
+ return -EINVAL;
+ }
+
+ /* Grab reference on the chipset driver. */
+ if (!try_module_get(bridge->driver->owner)) {
+ printk (KERN_INFO PFX "Couldn't lock chipset driver.\n");
+ return -EINVAL;
+ }
+
+ error = agp_backend_initialize(bridge);
+ if (error) {
+ printk (KERN_INFO PFX "agp_backend_initialize() failed.\n");
+ goto err_out;
+ }
+
+ if (list_empty(&agp_bridges)) {
+ error = agp_frontend_initialize();
+ if (error) {
+ printk (KERN_INFO PFX "agp_frontend_initialize() failed.\n");
+ goto frontend_err;
+ }
+
+ printk(KERN_INFO PFX "AGP aperture is %dM @ 0x%lx\n",
+ bridge->driver->fetch_size(), bridge->gart_bus_addr);
+
+ }
+
+ list_add(&bridge->list, &agp_bridges);
+ return 0;
+
+frontend_err:
+ agp_backend_cleanup(bridge);
+err_out:
+ module_put(bridge->driver->owner);
+ agp_put_bridge(bridge);
+ return error;
+}
+EXPORT_SYMBOL_GPL(agp_add_bridge);
+
+
+void agp_remove_bridge(struct agp_bridge_data *bridge)
+{
+ agp_backend_cleanup(bridge);
+ list_del(&bridge->list);
+ if (list_empty(&agp_bridges))
+ agp_frontend_cleanup();
+ module_put(bridge->driver->owner);
+}
+EXPORT_SYMBOL_GPL(agp_remove_bridge);
+
+int agp_off;
+int agp_try_unsupported_boot;
+EXPORT_SYMBOL(agp_off);
+EXPORT_SYMBOL(agp_try_unsupported_boot);
+
+static int __init agp_init(void)
+{
+ if (!agp_off)
+ printk(KERN_INFO "Linux agpgart interface v%d.%d (c) Dave Jones\n",
+ AGPGART_VERSION_MAJOR, AGPGART_VERSION_MINOR);
+ return 0;
+}
+
+void __exit agp_exit(void)
+{
+}
+
+#ifndef MODULE
+static __init int agp_setup(char *s)
+{
+ if (!strcmp(s,"off"))
+ agp_off = 1;
+ if (!strcmp(s,"try_unsupported"))
+ agp_try_unsupported_boot = 1;
+ return 1;
+}
+__setup("agp=", agp_setup);
+#endif
+
+MODULE_AUTHOR("Dave Jones <davej@codemonkey.org.uk>");
+MODULE_DESCRIPTION("AGP GART driver");
+MODULE_LICENSE("GPL and additional rights");
+MODULE_ALIAS_MISCDEV(AGPGART_MINOR);
+
+module_init(agp_init);
+module_exit(agp_exit);
+
diff --git a/drivers/char/agp/efficeon-agp.c b/drivers/char/agp/efficeon-agp.c
new file mode 100644
index 00000000000..52c0a097118
--- /dev/null
+++ b/drivers/char/agp/efficeon-agp.c
@@ -0,0 +1,463 @@
+/*
+ * Transmeta's Efficeon AGPGART driver.
+ *
+ * Based upon a diff by Linus around November '02.
+ *
+ * Ported to the 2.6 kernel by Carlos Puchol <cpglinux@puchol.com>
+ * and H. Peter Anvin <hpa@transmeta.com>.
+ */
+
+/*
+ * NOTE-cpg-040217:
+ *
+ * - when compiled as a module, after loading the module,
+ * it will refuse to unload, indicating it is in use,
+ * when it is not.
+ * - no s3 (suspend to ram) testing.
+ * - tested on the efficeon integrated nothbridge for tens
+ * of iterations of starting x and glxgears.
+ * - tested with radeon 9000 and radeon mobility m9 cards
+ * - tested with c3/c4 enabled (with the mobility m9 card)
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/agp_backend.h>
+#include <linux/gfp.h>
+#include <linux/page-flags.h>
+#include <linux/mm.h>
+#include "agp.h"
+
+/*
+ * The real differences to the generic AGP code is
+ * in the GART mappings - a two-level setup with the
+ * first level being an on-chip 64-entry table.
+ *
+ * The page array is filled through the ATTPAGE register
+ * (Aperture Translation Table Page Register) at 0xB8. Bits:
+ * 31:20: physical page address
+ * 11:9: Page Attribute Table Index (PATI)
+ * must match the PAT index for the
+ * mapped pages (the 2nd level page table pages
+ * themselves should be just regular WB-cacheable,
+ * so this is normally zero.)
+ * 8: Present
+ * 7:6: reserved, write as zero
+ * 5:0: GATT directory index: which 1st-level entry
+ *
+ * The Efficeon AGP spec requires pages to be WB-cacheable
+ * but to be explicitly CLFLUSH'd after any changes.
+ */
+#define EFFICEON_ATTPAGE 0xb8
+#define EFFICEON_L1_SIZE 64 /* Number of PDE pages */
+
+#define EFFICEON_PATI (0 << 9)
+#define EFFICEON_PRESENT (1 << 8)
+
+static struct _efficeon_private {
+ unsigned long l1_table[EFFICEON_L1_SIZE];
+} efficeon_private;
+
+static struct gatt_mask efficeon_generic_masks[] =
+{
+ {.mask = 0x00000001, .type = 0}
+};
+
+static struct aper_size_info_lvl2 efficeon_generic_sizes[4] =
+{
+ {256, 65536, 0},
+ {128, 32768, 32},
+ {64, 16384, 48},
+ {32, 8192, 56}
+};
+
+/*
+ * Control interfaces are largely identical to
+ * the legacy Intel 440BX..
+ */
+
+static int efficeon_fetch_size(void)
+{
+ int i;
+ u16 temp;
+ struct aper_size_info_lvl2 *values;
+
+ pci_read_config_word(agp_bridge->dev, INTEL_APSIZE, &temp);
+ values = A_SIZE_LVL2(agp_bridge->driver->aperture_sizes);
+
+ for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++) {
+ if (temp == values[i].size_value) {
+ agp_bridge->previous_size =
+ agp_bridge->current_size = (void *) (values + i);
+ agp_bridge->aperture_size_idx = i;
+ return values[i].size;
+ }
+ }
+
+ return 0;
+}
+
+static void efficeon_tlbflush(struct agp_memory * mem)
+{
+ printk(KERN_DEBUG PFX "efficeon_tlbflush()\n");
+ pci_write_config_dword(agp_bridge->dev, INTEL_AGPCTRL, 0x2200);
+ pci_write_config_dword(agp_bridge->dev, INTEL_AGPCTRL, 0x2280);
+}
+
+static void efficeon_cleanup(void)
+{
+ u16 temp;
+ struct aper_size_info_lvl2 *previous_size;
+
+ printk(KERN_DEBUG PFX "efficeon_cleanup()\n");
+ previous_size = A_SIZE_LVL2(agp_bridge->previous_size);
+ pci_read_config_word(agp_bridge->dev, INTEL_NBXCFG, &temp);
+ pci_write_config_word(agp_bridge->dev, INTEL_NBXCFG, temp & ~(1 << 9));
+ pci_write_config_word(agp_bridge->dev, INTEL_APSIZE,
+ previous_size->size_value);
+}
+
+static int efficeon_configure(void)
+{
+ u32 temp;
+ u16 temp2;
+ struct aper_size_info_lvl2 *current_size;
+
+ printk(KERN_DEBUG PFX "efficeon_configure()\n");
+
+ current_size = A_SIZE_LVL2(agp_bridge->current_size);
+
+ /* aperture size */
+ pci_write_config_word(agp_bridge->dev, INTEL_APSIZE,
+ current_size->size_value);
+
+ /* address to map to */
+ pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp);
+ agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+
+ /* agpctrl */
+ pci_write_config_dword(agp_bridge->dev, INTEL_AGPCTRL, 0x2280);
+
+ /* paccfg/nbxcfg */
+ pci_read_config_word(agp_bridge->dev, INTEL_NBXCFG, &temp2);
+ pci_write_config_word(agp_bridge->dev, INTEL_NBXCFG,
+ (temp2 & ~(1 << 10)) | (1 << 9) | (1 << 11));
+ /* clear any possible error conditions */
+ pci_write_config_byte(agp_bridge->dev, INTEL_ERRSTS + 1, 7);
+ return 0;
+}
+
+static int efficeon_free_gatt_table(struct agp_bridge_data *bridge)
+{
+ int index, freed = 0;
+
+ for (index = 0; index < EFFICEON_L1_SIZE; index++) {
+ unsigned long page = efficeon_private.l1_table[index];
+ if (page) {
+ efficeon_private.l1_table[index] = 0;
+ ClearPageReserved(virt_to_page((char *)page));
+ free_page(page);
+ freed++;
+ }
+ printk(KERN_DEBUG PFX "efficeon_free_gatt_table(%p, %02x, %08x)\n",
+ agp_bridge->dev, EFFICEON_ATTPAGE, index);
+ pci_write_config_dword(agp_bridge->dev,
+ EFFICEON_ATTPAGE, index);
+ }
+ printk(KERN_DEBUG PFX "efficeon_free_gatt_table() freed %d pages\n", freed);
+ return 0;
+}
+
+
+/*
+ * Since we don't need contigious memory we just try
+ * to get the gatt table once
+ */
+
+#define GET_PAGE_DIR_OFF(addr) (addr >> 22)
+#define GET_PAGE_DIR_IDX(addr) (GET_PAGE_DIR_OFF(addr) - \
+ GET_PAGE_DIR_OFF(agp_bridge->gart_bus_addr))
+#define GET_GATT_OFF(addr) ((addr & 0x003ff000) >> 12)
+#undef GET_GATT
+#define GET_GATT(addr) (efficeon_private.gatt_pages[\
+ GET_PAGE_DIR_IDX(addr)]->remapped)
+
+static int efficeon_create_gatt_table(struct agp_bridge_data *bridge)
+{
+ int index;
+ const int pati = EFFICEON_PATI;
+ const int present = EFFICEON_PRESENT;
+ const int clflush_chunk = ((cpuid_ebx(1) >> 8) & 0xff) << 3;
+ int num_entries, l1_pages;
+
+ num_entries = A_SIZE_LVL2(agp_bridge->current_size)->num_entries;
+
+ printk(KERN_DEBUG PFX "efficeon_create_gatt_table(%d)\n", num_entries);
+
+ /* There are 2^10 PTE pages per PDE page */
+ BUG_ON(num_entries & 0x3ff);
+ l1_pages = num_entries >> 10;
+
+ for (index = 0 ; index < l1_pages ; index++) {
+ int offset;
+ unsigned long page;
+ unsigned long value;
+
+ page = efficeon_private.l1_table[index];
+ BUG_ON(page);
+
+ page = get_zeroed_page(GFP_KERNEL);
+ if (!page) {
+ efficeon_free_gatt_table(agp_bridge);
+ return -ENOMEM;
+ }
+ SetPageReserved(virt_to_page((char *)page));
+
+ for (offset = 0; offset < PAGE_SIZE; offset += clflush_chunk)
+ asm volatile("clflush %0" : : "m" (*(char *)(page+offset)));
+
+ efficeon_private.l1_table[index] = page;
+
+ value = __pa(page) | pati | present | index;
+
+ pci_write_config_dword(agp_bridge->dev,
+ EFFICEON_ATTPAGE, value);
+ }
+
+ return 0;
+}
+
+static int efficeon_insert_memory(struct agp_memory * mem, off_t pg_start, int type)
+{
+ int i, count = mem->page_count, num_entries;
+ unsigned int *page, *last_page;
+ const int clflush_chunk = ((cpuid_ebx(1) >> 8) & 0xff) << 3;
+ const unsigned long clflush_mask = ~(clflush_chunk-1);
+
+ printk(KERN_DEBUG PFX "efficeon_insert_memory(%lx, %d)\n", pg_start, count);
+
+ num_entries = A_SIZE_LVL2(agp_bridge->current_size)->num_entries;
+ if ((pg_start + mem->page_count) > num_entries)
+ return -EINVAL;
+ if (type != 0 || mem->type != 0)
+ return -EINVAL;
+
+ if (mem->is_flushed == FALSE) {
+ global_cache_flush();
+ mem->is_flushed = TRUE;
+ }
+
+ last_page = NULL;
+ for (i = 0; i < count; i++) {
+ int index = pg_start + i;
+ unsigned long insert = mem->memory[i];
+
+ page = (unsigned int *) efficeon_private.l1_table[index >> 10];
+
+ if (!page)
+ continue;
+
+ page += (index & 0x3ff);
+ *page = insert;
+
+ /* clflush is slow, so don't clflush until we have to */
+ if ( last_page &&
+ ((unsigned long)page^(unsigned long)last_page) & clflush_mask )
+ asm volatile("clflush %0" : : "m" (*last_page));
+
+ last_page = page;
+ }
+
+ if ( last_page )
+ asm volatile("clflush %0" : : "m" (*last_page));
+
+ agp_bridge->driver->tlb_flush(mem);
+ return 0;
+}
+
+static int efficeon_remove_memory(struct agp_memory * mem, off_t pg_start, int type)
+{
+ int i, count = mem->page_count, num_entries;
+
+ printk(KERN_DEBUG PFX "efficeon_remove_memory(%lx, %d)\n", pg_start, count);
+
+ num_entries = A_SIZE_LVL2(agp_bridge->current_size)->num_entries;
+
+ if ((pg_start + mem->page_count) > num_entries)
+ return -EINVAL;
+ if (type != 0 || mem->type != 0)
+ return -EINVAL;
+
+ for (i = 0; i < count; i++) {
+ int index = pg_start + i;
+ unsigned int *page = (unsigned int *) efficeon_private.l1_table[index >> 10];
+
+ if (!page)
+ continue;
+ page += (index & 0x3ff);
+ *page = 0;
+ }
+ agp_bridge->driver->tlb_flush(mem);
+ return 0;
+}
+
+
+struct agp_bridge_driver efficeon_driver = {
+ .owner = THIS_MODULE,
+ .aperture_sizes = efficeon_generic_sizes,
+ .size_type = LVL2_APER_SIZE,
+ .num_aperture_sizes = 4,
+ .configure = efficeon_configure,
+ .fetch_size = efficeon_fetch_size,
+ .cleanup = efficeon_cleanup,
+ .tlb_flush = efficeon_tlbflush,
+ .mask_memory = agp_generic_mask_memory,
+ .masks = efficeon_generic_masks,
+ .agp_enable = agp_generic_enable,
+ .cache_flush = global_cache_flush,
+
+ // Efficeon-specific GATT table setup / populate / teardown
+ .create_gatt_table = efficeon_create_gatt_table,
+ .free_gatt_table = efficeon_free_gatt_table,
+ .insert_memory = efficeon_insert_memory,
+ .remove_memory = efficeon_remove_memory,
+ .cant_use_aperture = 0, // 1 might be faster?
+
+ // Generic
+ .alloc_by_type = agp_generic_alloc_by_type,
+ .free_by_type = agp_generic_free_by_type,
+ .agp_alloc_page = agp_generic_alloc_page,
+ .agp_destroy_page = agp_generic_destroy_page,
+};
+
+
+static int agp_efficeon_resume(struct pci_dev *pdev)
+{
+ printk(KERN_DEBUG PFX "agp_efficeon_resume()\n");
+ return efficeon_configure();
+}
+
+static int __devinit agp_efficeon_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct agp_bridge_data *bridge;
+ u8 cap_ptr;
+ struct resource *r;
+
+ cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP);
+ if (!cap_ptr)
+ return -ENODEV;
+
+ /* Probe for Efficeon controller */
+ if (pdev->device != PCI_DEVICE_ID_EFFICEON) {
+ printk(KERN_ERR PFX "Unsupported Efficeon chipset (device id: %04x)\n",
+ pdev->device);
+ return -ENODEV;
+ }
+
+ printk(KERN_INFO PFX "Detected Transmeta Efficeon TM8000 series chipset\n");
+
+ bridge = agp_alloc_bridge();
+ if (!bridge)
+ return -ENOMEM;
+
+ bridge->driver = &efficeon_driver;
+ bridge->dev = pdev;
+ bridge->capndx = cap_ptr;
+
+ /*
+ * The following fixes the case where the BIOS has "forgotten" to
+ * provide an address range for the GART.
+ * 20030610 - hamish@zot.org
+ */
+ r = &pdev->resource[0];
+ if (!r->start && r->end) {
+ if(pci_assign_resource(pdev, 0)) {
+ printk(KERN_ERR PFX "could not assign resource 0\n");
+ return -ENODEV;
+ }
+ }
+
+ /*
+ * If the device has not been properly setup, the following will catch
+ * the problem and should stop the system from crashing.
+ * 20030610 - hamish@zot.org
+ */
+ if (pci_enable_device(pdev)) {
+ printk(KERN_ERR PFX "Unable to Enable PCI device\n");
+ return -ENODEV;
+ }
+
+ /* Fill in the mode register */
+ if (cap_ptr) {
+ pci_read_config_dword(pdev,
+ bridge->capndx+PCI_AGP_STATUS,
+ &bridge->mode);
+ }
+
+ pci_set_drvdata(pdev, bridge);
+ return agp_add_bridge(bridge);
+}
+
+static void __devexit agp_efficeon_remove(struct pci_dev *pdev)
+{
+ struct agp_bridge_data *bridge = pci_get_drvdata(pdev);
+
+ agp_remove_bridge(bridge);
+ agp_put_bridge(bridge);
+}
+
+static int agp_efficeon_suspend(struct pci_dev *dev, u32 state)
+{
+ return 0;
+}
+
+
+static struct pci_device_id agp_efficeon_pci_table[] = {
+ {
+ .class = (PCI_CLASS_BRIDGE_HOST << 8),
+ .class_mask = ~0,
+ .vendor = PCI_VENDOR_ID_TRANSMETA,
+ .device = PCI_ANY_ID,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ },
+ { }
+};
+
+MODULE_DEVICE_TABLE(pci, agp_efficeon_pci_table);
+
+static struct pci_driver agp_efficeon_pci_driver = {
+ .name = "agpgart-efficeon",
+ .id_table = agp_efficeon_pci_table,
+ .probe = agp_efficeon_probe,
+ .remove = agp_efficeon_remove,
+ .suspend = agp_efficeon_suspend,
+ .resume = agp_efficeon_resume,
+};
+
+static int __init agp_efficeon_init(void)
+{
+ static int agp_initialised=0;
+
+ if (agp_off)
+ return -EINVAL;
+
+ if (agp_initialised == 1)
+ return 0;
+ agp_initialised=1;
+
+ return pci_register_driver(&agp_efficeon_pci_driver);
+}
+
+static void __exit agp_efficeon_cleanup(void)
+{
+ pci_unregister_driver(&agp_efficeon_pci_driver);
+}
+
+module_init(agp_efficeon_init);
+module_exit(agp_efficeon_cleanup);
+
+MODULE_AUTHOR("Carlos Puchol <cpglinux@puchol.com>");
+MODULE_LICENSE("GPL and additional rights");
diff --git a/drivers/char/agp/frontend.c b/drivers/char/agp/frontend.c
new file mode 100644
index 00000000000..f633623ac80
--- /dev/null
+++ b/drivers/char/agp/frontend.c
@@ -0,0 +1,1103 @@
+/*
+ * AGPGART driver frontend
+ * Copyright (C) 2004 Silicon Graphics, Inc.
+ * Copyright (C) 2002-2003 Dave Jones
+ * Copyright (C) 1999 Jeff Hartmann
+ * Copyright (C) 1999 Precision Insight, Inc.
+ * Copyright (C) 1999 Xi Graphics, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * JEFF HARTMANN, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mman.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/miscdevice.h>
+#include <linux/agp_backend.h>
+#include <linux/agpgart.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <asm/uaccess.h>
+#include <asm/pgtable.h>
+#include "agp.h"
+
+static struct agp_front_data agp_fe;
+
+static struct agp_memory *agp_find_mem_by_key(int key)
+{
+ struct agp_memory *curr;
+
+ if (agp_fe.current_controller == NULL)
+ return NULL;
+
+ curr = agp_fe.current_controller->pool;
+
+ while (curr != NULL) {
+ if (curr->key == key)
+ break;
+ curr = curr->next;
+ }
+
+ DBG("key=%d -> mem=%p", key, curr);
+ return curr;
+}
+
+static void agp_remove_from_pool(struct agp_memory *temp)
+{
+ struct agp_memory *prev;
+ struct agp_memory *next;
+
+ /* Check to see if this is even in the memory pool */
+
+ DBG("mem=%p", temp);
+ if (agp_find_mem_by_key(temp->key) != NULL) {
+ next = temp->next;
+ prev = temp->prev;
+
+ if (prev != NULL) {
+ prev->next = next;
+ if (next != NULL)
+ next->prev = prev;
+
+ } else {
+ /* This is the first item on the list */
+ if (next != NULL)
+ next->prev = NULL;
+
+ agp_fe.current_controller->pool = next;
+ }
+ }
+}
+
+/*
+ * Routines for managing each client's segment list -
+ * These routines handle adding and removing segments
+ * to each auth'ed client.
+ */
+
+static struct
+agp_segment_priv *agp_find_seg_in_client(const struct agp_client *client,
+ unsigned long offset,
+ int size, pgprot_t page_prot)
+{
+ struct agp_segment_priv *seg;
+ int num_segments, i;
+ off_t pg_start;
+ size_t pg_count;
+
+ pg_start = offset / 4096;
+ pg_count = size / 4096;
+ seg = *(client->segments);
+ num_segments = client->num_segments;
+
+ for (i = 0; i < client->num_segments; i++) {
+ if ((seg[i].pg_start == pg_start) &&
+ (seg[i].pg_count == pg_count) &&
+ (pgprot_val(seg[i].prot) == pgprot_val(page_prot))) {
+ return seg + i;
+ }
+ }
+
+ return NULL;
+}
+
+static void agp_remove_seg_from_client(struct agp_client *client)
+{
+ DBG("client=%p", client);
+
+ if (client->segments != NULL) {
+ if (*(client->segments) != NULL) {
+ DBG("Freeing %p from client %p", *(client->segments), client);
+ kfree(*(client->segments));
+ }
+ DBG("Freeing %p from client %p", client->segments, client);
+ kfree(client->segments);
+ client->segments = NULL;
+ }
+}
+
+static void agp_add_seg_to_client(struct agp_client *client,
+ struct agp_segment_priv ** seg, int num_segments)
+{
+ struct agp_segment_priv **prev_seg;
+
+ prev_seg = client->segments;
+
+ if (prev_seg != NULL)
+ agp_remove_seg_from_client(client);
+
+ DBG("Adding seg %p (%d segments) to client %p", seg, num_segments, client);
+ client->num_segments = num_segments;
+ client->segments = seg;
+}
+
+/* Originally taken from linux/mm/mmap.c from the array
+ * protection_map.
+ * The original really should be exported to modules, or
+ * some routine which does the conversion for you
+ */
+
+static const pgprot_t my_protect_map[16] =
+{
+ __P000, __P001, __P010, __P011, __P100, __P101, __P110, __P111,
+ __S000, __S001, __S010, __S011, __S100, __S101, __S110, __S111
+};
+
+static pgprot_t agp_convert_mmap_flags(int prot)
+{
+#define _trans(x,bit1,bit2) \
+((bit1==bit2)?(x&bit1):(x&bit1)?bit2:0)
+
+ unsigned long prot_bits;
+ pgprot_t temp;
+
+ prot_bits = _trans(prot, PROT_READ, VM_READ) |
+ _trans(prot, PROT_WRITE, VM_WRITE) |
+ _trans(prot, PROT_EXEC, VM_EXEC);
+
+ prot_bits |= VM_SHARED;
+
+ temp = my_protect_map[prot_bits & 0x0000000f];
+
+ return temp;
+}
+
+static int agp_create_segment(struct agp_client *client, struct agp_region *region)
+{
+ struct agp_segment_priv **ret_seg;
+ struct agp_segment_priv *seg;
+ struct agp_segment *user_seg;
+ size_t i;
+
+ seg = kmalloc((sizeof(struct agp_segment_priv) * region->seg_count), GFP_KERNEL);
+ if (seg == NULL) {
+ kfree(region->seg_list);
+ region->seg_list = NULL;
+ return -ENOMEM;
+ }
+ memset(seg, 0, (sizeof(struct agp_segment_priv) * region->seg_count));
+ user_seg = region->seg_list;
+
+ for (i = 0; i < region->seg_count; i++) {
+ seg[i].pg_start = user_seg[i].pg_start;
+ seg[i].pg_count = user_seg[i].pg_count;
+ seg[i].prot = agp_convert_mmap_flags(user_seg[i].prot);
+ }
+ kfree(region->seg_list);
+ region->seg_list = NULL;
+
+ ret_seg = kmalloc(sizeof(void *), GFP_KERNEL);
+ if (ret_seg == NULL) {
+ kfree(seg);
+ return -ENOMEM;
+ }
+ *ret_seg = seg;
+ agp_add_seg_to_client(client, ret_seg, region->seg_count);
+ return 0;
+}
+
+/* End - Routines for managing each client's segment list */
+
+/* This function must only be called when current_controller != NULL */
+static void agp_insert_into_pool(struct agp_memory * temp)
+{
+ struct agp_memory *prev;
+
+ prev = agp_fe.current_controller->pool;
+
+ if (prev != NULL) {
+ prev->prev = temp;
+ temp->next = prev;
+ }
+ agp_fe.current_controller->pool = temp;
+}
+
+
+/* File private list routines */
+
+struct agp_file_private *agp_find_private(pid_t pid)
+{
+ struct agp_file_private *curr;
+
+ curr = agp_fe.file_priv_list;
+
+ while (curr != NULL) {
+ if (curr->my_pid == pid)
+ return curr;
+ curr = curr->next;
+ }
+
+ return NULL;
+}
+
+void agp_insert_file_private(struct agp_file_private * priv)
+{
+ struct agp_file_private *prev;
+
+ prev = agp_fe.file_priv_list;
+
+ if (prev != NULL)
+ prev->prev = priv;
+ priv->next = prev;
+ agp_fe.file_priv_list = priv;
+}
+
+void agp_remove_file_private(struct agp_file_private * priv)
+{
+ struct agp_file_private *next;
+ struct agp_file_private *prev;
+
+ next = priv->next;
+ prev = priv->prev;
+
+ if (prev != NULL) {
+ prev->next = next;
+
+ if (next != NULL)
+ next->prev = prev;
+
+ } else {
+ if (next != NULL)
+ next->prev = NULL;
+
+ agp_fe.file_priv_list = next;
+ }
+}
+
+/* End - File flag list routines */
+
+/*
+ * Wrappers for agp_free_memory & agp_allocate_memory
+ * These make sure that internal lists are kept updated.
+ */
+static void agp_free_memory_wrap(struct agp_memory *memory)
+{
+ agp_remove_from_pool(memory);
+ agp_free_memory(memory);
+}
+
+static struct agp_memory *agp_allocate_memory_wrap(size_t pg_count, u32 type)
+{
+ struct agp_memory *memory;
+
+ memory = agp_allocate_memory(agp_bridge, pg_count, type);
+ if (memory == NULL)
+ return NULL;
+
+ agp_insert_into_pool(memory);
+ return memory;
+}
+
+/* Routines for managing the list of controllers -
+ * These routines manage the current controller, and the list of
+ * controllers
+ */
+
+static struct agp_controller *agp_find_controller_by_pid(pid_t id)
+{
+ struct agp_controller *controller;
+
+ controller = agp_fe.controllers;
+
+ while (controller != NULL) {
+ if (controller->pid == id)
+ return controller;
+ controller = controller->next;
+ }
+
+ return NULL;
+}
+
+static struct agp_controller *agp_create_controller(pid_t id)
+{
+ struct agp_controller *controller;
+
+ controller = kmalloc(sizeof(struct agp_controller), GFP_KERNEL);
+
+ if (controller == NULL)
+ return NULL;
+
+ memset(controller, 0, sizeof(struct agp_controller));
+ controller->pid = id;
+
+ return controller;
+}
+
+static int agp_insert_controller(struct agp_controller *controller)
+{
+ struct agp_controller *prev_controller;
+
+ prev_controller = agp_fe.controllers;
+ controller->next = prev_controller;
+
+ if (prev_controller != NULL)
+ prev_controller->prev = controller;
+
+ agp_fe.controllers = controller;
+
+ return 0;
+}
+
+static void agp_remove_all_clients(struct agp_controller *controller)
+{
+ struct agp_client *client;
+ struct agp_client *temp;
+
+ client = controller->clients;
+
+ while (client) {
+ struct agp_file_private *priv;
+
+ temp = client;
+ agp_remove_seg_from_client(temp);
+ priv = agp_find_private(temp->pid);
+
+ if (priv != NULL) {
+ clear_bit(AGP_FF_IS_VALID, &priv->access_flags);
+ clear_bit(AGP_FF_IS_CLIENT, &priv->access_flags);
+ }
+ client = client->next;
+ kfree(temp);
+ }
+}
+
+static void agp_remove_all_memory(struct agp_controller *controller)
+{
+ struct agp_memory *memory;
+ struct agp_memory *temp;
+
+ memory = controller->pool;
+
+ while (memory) {
+ temp = memory;
+ memory = memory->next;
+ agp_free_memory_wrap(temp);
+ }
+}
+
+static int agp_remove_controller(struct agp_controller *controller)
+{
+ struct agp_controller *prev_controller;
+ struct agp_controller *next_controller;
+
+ prev_controller = controller->prev;
+ next_controller = controller->next;
+
+ if (prev_controller != NULL) {
+ prev_controller->next = next_controller;
+ if (next_controller != NULL)
+ next_controller->prev = prev_controller;
+
+ } else {
+ if (next_controller != NULL)
+ next_controller->prev = NULL;
+
+ agp_fe.controllers = next_controller;
+ }
+
+ agp_remove_all_memory(controller);
+ agp_remove_all_clients(controller);
+
+ if (agp_fe.current_controller == controller) {
+ agp_fe.current_controller = NULL;
+ agp_fe.backend_acquired = FALSE;
+ agp_backend_release(agp_bridge);
+ }
+ kfree(controller);
+ return 0;
+}
+
+static void agp_controller_make_current(struct agp_controller *controller)
+{
+ struct agp_client *clients;
+
+ clients = controller->clients;
+
+ while (clients != NULL) {
+ struct agp_file_private *priv;
+
+ priv = agp_find_private(clients->pid);
+
+ if (priv != NULL) {
+ set_bit(AGP_FF_IS_VALID, &priv->access_flags);
+ set_bit(AGP_FF_IS_CLIENT, &priv->access_flags);
+ }
+ clients = clients->next;
+ }
+
+ agp_fe.current_controller = controller;
+}
+
+static void agp_controller_release_current(struct agp_controller *controller,
+ struct agp_file_private *controller_priv)
+{
+ struct agp_client *clients;
+
+ clear_bit(AGP_FF_IS_VALID, &controller_priv->access_flags);
+ clients = controller->clients;
+
+ while (clients != NULL) {
+ struct agp_file_private *priv;
+
+ priv = agp_find_private(clients->pid);
+
+ if (priv != NULL)
+ clear_bit(AGP_FF_IS_VALID, &priv->access_flags);
+
+ clients = clients->next;
+ }
+
+ agp_fe.current_controller = NULL;
+ agp_fe.used_by_controller = FALSE;
+ agp_backend_release(agp_bridge);
+}
+
+/*
+ * Routines for managing client lists -
+ * These routines are for managing the list of auth'ed clients.
+ */
+
+static struct agp_client
+*agp_find_client_in_controller(struct agp_controller *controller, pid_t id)
+{
+ struct agp_client *client;
+
+ if (controller == NULL)
+ return NULL;
+
+ client = controller->clients;
+
+ while (client != NULL) {
+ if (client->pid == id)
+ return client;
+ client = client->next;
+ }
+
+ return NULL;
+}
+
+static struct agp_controller *agp_find_controller_for_client(pid_t id)
+{
+ struct agp_controller *controller;
+
+ controller = agp_fe.controllers;
+
+ while (controller != NULL) {
+ if ((agp_find_client_in_controller(controller, id)) != NULL)
+ return controller;
+ controller = controller->next;
+ }
+
+ return NULL;
+}
+
+static struct agp_client *agp_find_client_by_pid(pid_t id)
+{
+ struct agp_client *temp;
+
+ if (agp_fe.current_controller == NULL)
+ return NULL;
+
+ temp = agp_find_client_in_controller(agp_fe.current_controller, id);
+ return temp;
+}
+
+static void agp_insert_client(struct agp_client *client)
+{
+ struct agp_client *prev_client;
+
+ prev_client = agp_fe.current_controller->clients;
+ client->next = prev_client;
+
+ if (prev_client != NULL)
+ prev_client->prev = client;
+
+ agp_fe.current_controller->clients = client;
+ agp_fe.current_controller->num_clients++;
+}
+
+static struct agp_client *agp_create_client(pid_t id)
+{
+ struct agp_client *new_client;
+
+ new_client = kmalloc(sizeof(struct agp_client), GFP_KERNEL);
+
+ if (new_client == NULL)
+ return NULL;
+
+ memset(new_client, 0, sizeof(struct agp_client));
+ new_client->pid = id;
+ agp_insert_client(new_client);
+ return new_client;
+}
+
+static int agp_remove_client(pid_t id)
+{
+ struct agp_client *client;
+ struct agp_client *prev_client;
+ struct agp_client *next_client;
+ struct agp_controller *controller;
+
+ controller = agp_find_controller_for_client(id);
+ if (controller == NULL)
+ return -EINVAL;
+
+ client = agp_find_client_in_controller(controller, id);
+ if (client == NULL)
+ return -EINVAL;
+
+ prev_client = client->prev;
+ next_client = client->next;
+
+ if (prev_client != NULL) {
+ prev_client->next = next_client;
+ if (next_client != NULL)
+ next_client->prev = prev_client;
+
+ } else {
+ if (next_client != NULL)
+ next_client->prev = NULL;
+ controller->clients = next_client;
+ }
+
+ controller->num_clients--;
+ agp_remove_seg_from_client(client);
+ kfree(client);
+ return 0;
+}
+
+/* End - Routines for managing client lists */
+
+/* File Operations */
+
+static int agp_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ unsigned int size, current_size;
+ unsigned long offset;
+ struct agp_client *client;
+ struct agp_file_private *priv = file->private_data;
+ struct agp_kern_info kerninfo;
+
+ down(&(agp_fe.agp_mutex));
+
+ if (agp_fe.backend_acquired != TRUE)
+ goto out_eperm;
+
+ if (!(test_bit(AGP_FF_IS_VALID, &priv->access_flags)))
+ goto out_eperm;
+
+ agp_copy_info(agp_bridge, &kerninfo);
+ size = vma->vm_end - vma->vm_start;
+ current_size = kerninfo.aper_size;
+ current_size = current_size * 0x100000;
+ offset = vma->vm_pgoff << PAGE_SHIFT;
+ DBG("%lx:%lx", offset, offset+size);
+
+ if (test_bit(AGP_FF_IS_CLIENT, &priv->access_flags)) {
+ if ((size + offset) > current_size)
+ goto out_inval;
+
+ client = agp_find_client_by_pid(current->pid);
+
+ if (client == NULL)
+ goto out_eperm;
+
+ if (!agp_find_seg_in_client(client, offset, size, vma->vm_page_prot))
+ goto out_inval;
+
+ DBG("client vm_ops=%p", kerninfo.vm_ops);
+ if (kerninfo.vm_ops) {
+ vma->vm_ops = kerninfo.vm_ops;
+ } else if (io_remap_pfn_range(vma, vma->vm_start,
+ (kerninfo.aper_base + offset) >> PAGE_SHIFT,
+ size, vma->vm_page_prot)) {
+ goto out_again;
+ }
+ up(&(agp_fe.agp_mutex));
+ return 0;
+ }
+
+ if (test_bit(AGP_FF_IS_CONTROLLER, &priv->access_flags)) {
+ if (size != current_size)
+ goto out_inval;
+
+ DBG("controller vm_ops=%p", kerninfo.vm_ops);
+ if (kerninfo.vm_ops) {
+ vma->vm_ops = kerninfo.vm_ops;
+ } else if (io_remap_pfn_range(vma, vma->vm_start,
+ kerninfo.aper_base >> PAGE_SHIFT,
+ size, vma->vm_page_prot)) {
+ goto out_again;
+ }
+ up(&(agp_fe.agp_mutex));
+ return 0;
+ }
+
+out_eperm:
+ up(&(agp_fe.agp_mutex));
+ return -EPERM;
+
+out_inval:
+ up(&(agp_fe.agp_mutex));
+ return -EINVAL;
+
+out_again:
+ up(&(agp_fe.agp_mutex));
+ return -EAGAIN;
+}
+
+static int agp_release(struct inode *inode, struct file *file)
+{
+ struct agp_file_private *priv = file->private_data;
+
+ down(&(agp_fe.agp_mutex));
+
+ DBG("priv=%p", priv);
+
+ if (test_bit(AGP_FF_IS_CONTROLLER, &priv->access_flags)) {
+ struct agp_controller *controller;
+
+ controller = agp_find_controller_by_pid(priv->my_pid);
+
+ if (controller != NULL) {
+ if (controller == agp_fe.current_controller)
+ agp_controller_release_current(controller, priv);
+ agp_remove_controller(controller);
+ controller = NULL;
+ }
+ }
+
+ if (test_bit(AGP_FF_IS_CLIENT, &priv->access_flags))
+ agp_remove_client(priv->my_pid);
+
+ agp_remove_file_private(priv);
+ kfree(priv);
+ file->private_data = NULL;
+ up(&(agp_fe.agp_mutex));
+ return 0;
+}
+
+static int agp_open(struct inode *inode, struct file *file)
+{
+ int minor = iminor(inode);
+ struct agp_file_private *priv;
+ struct agp_client *client;
+ int rc = -ENXIO;
+
+ down(&(agp_fe.agp_mutex));
+
+ if (minor != AGPGART_MINOR)
+ goto err_out;
+
+ priv = kmalloc(sizeof(struct agp_file_private), GFP_KERNEL);
+ if (priv == NULL)
+ goto err_out_nomem;
+
+ memset(priv, 0, sizeof(struct agp_file_private));
+ set_bit(AGP_FF_ALLOW_CLIENT, &priv->access_flags);
+ priv->my_pid = current->pid;
+
+ if ((current->uid == 0) || (current->suid == 0)) {
+ /* Root priv, can be controller */
+ set_bit(AGP_FF_ALLOW_CONTROLLER, &priv->access_flags);
+ }
+ client = agp_find_client_by_pid(current->pid);
+
+ if (client != NULL) {
+ set_bit(AGP_FF_IS_CLIENT, &priv->access_flags);
+ set_bit(AGP_FF_IS_VALID, &priv->access_flags);
+ }
+ file->private_data = (void *) priv;
+ agp_insert_file_private(priv);
+ DBG("private=%p, client=%p", priv, client);
+ up(&(agp_fe.agp_mutex));
+ return 0;
+
+err_out_nomem:
+ rc = -ENOMEM;
+err_out:
+ up(&(agp_fe.agp_mutex));
+ return rc;
+}
+
+
+static ssize_t agp_read(struct file *file, char __user *buf,
+ size_t count, loff_t * ppos)
+{
+ return -EINVAL;
+}
+
+static ssize_t agp_write(struct file *file, const char __user *buf,
+ size_t count, loff_t * ppos)
+{
+ return -EINVAL;
+}
+
+static int agpioc_info_wrap(struct agp_file_private *priv, void __user *arg)
+{
+ struct agp_info userinfo;
+ struct agp_kern_info kerninfo;
+
+ agp_copy_info(agp_bridge, &kerninfo);
+
+ userinfo.version.major = kerninfo.version.major;
+ userinfo.version.minor = kerninfo.version.minor;
+ userinfo.bridge_id = kerninfo.device->vendor |
+ (kerninfo.device->device << 16);
+ userinfo.agp_mode = kerninfo.mode;
+ userinfo.aper_base = kerninfo.aper_base;
+ userinfo.aper_size = kerninfo.aper_size;
+ userinfo.pg_total = userinfo.pg_system = kerninfo.max_memory;
+ userinfo.pg_used = kerninfo.current_memory;
+
+ if (copy_to_user(arg, &userinfo, sizeof(struct agp_info)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int agpioc_acquire_wrap(struct agp_file_private *priv)
+{
+ struct agp_controller *controller;
+
+ DBG("");
+
+ if (!(test_bit(AGP_FF_ALLOW_CONTROLLER, &priv->access_flags)))
+ return -EPERM;
+
+ if (agp_fe.current_controller != NULL)
+ return -EBUSY;
+
+ if(!agp_bridge)
+ return -ENODEV;
+
+ if (atomic_read(&agp_bridge->agp_in_use))
+ return -EBUSY;
+
+ atomic_inc(&agp_bridge->agp_in_use);
+
+ agp_fe.backend_acquired = TRUE;
+
+ controller = agp_find_controller_by_pid(priv->my_pid);
+
+ if (controller != NULL) {
+ agp_controller_make_current(controller);
+ } else {
+ controller = agp_create_controller(priv->my_pid);
+
+ if (controller == NULL) {
+ agp_fe.backend_acquired = FALSE;
+ agp_backend_release(agp_bridge);
+ return -ENOMEM;
+ }
+ agp_insert_controller(controller);
+ agp_controller_make_current(controller);
+ }
+
+ set_bit(AGP_FF_IS_CONTROLLER, &priv->access_flags);
+ set_bit(AGP_FF_IS_VALID, &priv->access_flags);
+ return 0;
+}
+
+static int agpioc_release_wrap(struct agp_file_private *priv)
+{
+ DBG("");
+ agp_controller_release_current(agp_fe.current_controller, priv);
+ return 0;
+}
+
+static int agpioc_setup_wrap(struct agp_file_private *priv, void __user *arg)
+{
+ struct agp_setup mode;
+
+ DBG("");
+ if (copy_from_user(&mode, arg, sizeof(struct agp_setup)))
+ return -EFAULT;
+
+ agp_enable(agp_bridge, mode.agp_mode);
+ return 0;
+}
+
+static int agpioc_reserve_wrap(struct agp_file_private *priv, void __user *arg)
+{
+ struct agp_region reserve;
+ struct agp_client *client;
+ struct agp_file_private *client_priv;
+
+ DBG("");
+ if (copy_from_user(&reserve, arg, sizeof(struct agp_region)))
+ return -EFAULT;
+
+ if ((unsigned) reserve.seg_count >= ~0U/sizeof(struct agp_segment))
+ return -EFAULT;
+
+ client = agp_find_client_by_pid(reserve.pid);
+
+ if (reserve.seg_count == 0) {
+ /* remove a client */
+ client_priv = agp_find_private(reserve.pid);
+
+ if (client_priv != NULL) {
+ set_bit(AGP_FF_IS_CLIENT, &client_priv->access_flags);
+ set_bit(AGP_FF_IS_VALID, &client_priv->access_flags);
+ }
+ if (client == NULL) {
+ /* client is already removed */
+ return 0;
+ }
+ return agp_remove_client(reserve.pid);
+ } else {
+ struct agp_segment *segment;
+
+ if (reserve.seg_count >= 16384)
+ return -EINVAL;
+
+ segment = kmalloc((sizeof(struct agp_segment) * reserve.seg_count),
+ GFP_KERNEL);
+
+ if (segment == NULL)
+ return -ENOMEM;
+
+ if (copy_from_user(segment, (void __user *) reserve.seg_list,
+ sizeof(struct agp_segment) * reserve.seg_count)) {
+ kfree(segment);
+ return -EFAULT;
+ }
+ reserve.seg_list = segment;
+
+ if (client == NULL) {
+ /* Create the client and add the segment */
+ client = agp_create_client(reserve.pid);
+
+ if (client == NULL) {
+ kfree(segment);
+ return -ENOMEM;
+ }
+ client_priv = agp_find_private(reserve.pid);
+
+ if (client_priv != NULL) {
+ set_bit(AGP_FF_IS_CLIENT, &client_priv->access_flags);
+ set_bit(AGP_FF_IS_VALID, &client_priv->access_flags);
+ }
+ }
+ return agp_create_segment(client, &reserve);
+ }
+ /* Will never really happen */
+ return -EINVAL;
+}
+
+static int agpioc_protect_wrap(struct agp_file_private *priv)
+{
+ DBG("");
+ /* This function is not currently implemented */
+ return -EINVAL;
+}
+
+static int agpioc_allocate_wrap(struct agp_file_private *priv, void __user *arg)
+{
+ struct agp_memory *memory;
+ struct agp_allocate alloc;
+
+ DBG("");
+ if (copy_from_user(&alloc, arg, sizeof(struct agp_allocate)))
+ return -EFAULT;
+
+ memory = agp_allocate_memory_wrap(alloc.pg_count, alloc.type);
+
+ if (memory == NULL)
+ return -ENOMEM;
+
+ alloc.key = memory->key;
+ alloc.physical = memory->physical;
+
+ if (copy_to_user(arg, &alloc, sizeof(struct agp_allocate))) {
+ agp_free_memory_wrap(memory);
+ return -EFAULT;
+ }
+ return 0;
+}
+
+static int agpioc_deallocate_wrap(struct agp_file_private *priv, int arg)
+{
+ struct agp_memory *memory;
+
+ DBG("");
+ memory = agp_find_mem_by_key(arg);
+
+ if (memory == NULL)
+ return -EINVAL;
+
+ agp_free_memory_wrap(memory);
+ return 0;
+}
+
+static int agpioc_bind_wrap(struct agp_file_private *priv, void __user *arg)
+{
+ struct agp_bind bind_info;
+ struct agp_memory *memory;
+
+ DBG("");
+ if (copy_from_user(&bind_info, arg, sizeof(struct agp_bind)))
+ return -EFAULT;
+
+ memory = agp_find_mem_by_key(bind_info.key);
+
+ if (memory == NULL)
+ return -EINVAL;
+
+ return agp_bind_memory(memory, bind_info.pg_start);
+}
+
+static int agpioc_unbind_wrap(struct agp_file_private *priv, void __user *arg)
+{
+ struct agp_memory *memory;
+ struct agp_unbind unbind;
+
+ DBG("");
+ if (copy_from_user(&unbind, arg, sizeof(struct agp_unbind)))
+ return -EFAULT;
+
+ memory = agp_find_mem_by_key(unbind.key);
+
+ if (memory == NULL)
+ return -EINVAL;
+
+ return agp_unbind_memory(memory);
+}
+
+static int agp_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct agp_file_private *curr_priv = file->private_data;
+ int ret_val = -ENOTTY;
+
+ DBG("priv=%p, cmd=%x", curr_priv, cmd);
+ down(&(agp_fe.agp_mutex));
+
+ if ((agp_fe.current_controller == NULL) &&
+ (cmd != AGPIOC_ACQUIRE)) {
+ ret_val = -EINVAL;
+ goto ioctl_out;
+ }
+ if ((agp_fe.backend_acquired != TRUE) &&
+ (cmd != AGPIOC_ACQUIRE)) {
+ ret_val = -EBUSY;
+ goto ioctl_out;
+ }
+ if (cmd != AGPIOC_ACQUIRE) {
+ if (!(test_bit(AGP_FF_IS_CONTROLLER, &curr_priv->access_flags))) {
+ ret_val = -EPERM;
+ goto ioctl_out;
+ }
+ /* Use the original pid of the controller,
+ * in case it's threaded */
+
+ if (agp_fe.current_controller->pid != curr_priv->my_pid) {
+ ret_val = -EBUSY;
+ goto ioctl_out;
+ }
+ }
+
+ switch (cmd) {
+ case AGPIOC_INFO:
+ ret_val = agpioc_info_wrap(curr_priv, (void __user *) arg);
+ break;
+
+ case AGPIOC_ACQUIRE:
+ ret_val = agpioc_acquire_wrap(curr_priv);
+ break;
+
+ case AGPIOC_RELEASE:
+ ret_val = agpioc_release_wrap(curr_priv);
+ break;
+
+ case AGPIOC_SETUP:
+ ret_val = agpioc_setup_wrap(curr_priv, (void __user *) arg);
+ break;
+
+ case AGPIOC_RESERVE:
+ ret_val = agpioc_reserve_wrap(curr_priv, (void __user *) arg);
+ break;
+
+ case AGPIOC_PROTECT:
+ ret_val = agpioc_protect_wrap(curr_priv);
+ break;
+
+ case AGPIOC_ALLOCATE:
+ ret_val = agpioc_allocate_wrap(curr_priv, (void __user *) arg);
+ break;
+
+ case AGPIOC_DEALLOCATE:
+ ret_val = agpioc_deallocate_wrap(curr_priv, (int) arg);
+ break;
+
+ case AGPIOC_BIND:
+ ret_val = agpioc_bind_wrap(curr_priv, (void __user *) arg);
+ break;
+
+ case AGPIOC_UNBIND:
+ ret_val = agpioc_unbind_wrap(curr_priv, (void __user *) arg);
+ break;
+ }
+
+ioctl_out:
+ DBG("ioctl returns %d\n", ret_val);
+ up(&(agp_fe.agp_mutex));
+ return ret_val;
+}
+
+static struct file_operations agp_fops =
+{
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .read = agp_read,
+ .write = agp_write,
+ .ioctl = agp_ioctl,
+ .mmap = agp_mmap,
+ .open = agp_open,
+ .release = agp_release,
+};
+
+static struct miscdevice agp_miscdev =
+{
+ .minor = AGPGART_MINOR,
+ .name = "agpgart",
+ .fops = &agp_fops
+};
+
+int agp_frontend_initialize(void)
+{
+ memset(&agp_fe, 0, sizeof(struct agp_front_data));
+ sema_init(&(agp_fe.agp_mutex), 1);
+
+ if (misc_register(&agp_miscdev)) {
+ printk(KERN_ERR PFX "unable to get minor: %d\n", AGPGART_MINOR);
+ return -EIO;
+ }
+ return 0;
+}
+
+void agp_frontend_cleanup(void)
+{
+ misc_deregister(&agp_miscdev);
+}
diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c
new file mode 100644
index 00000000000..c321a924e38
--- /dev/null
+++ b/drivers/char/agp/generic.c
@@ -0,0 +1,1222 @@
+/*
+ * AGPGART driver.
+ * Copyright (C) 2004 Silicon Graphics, Inc.
+ * Copyright (C) 2002-2005 Dave Jones.
+ * Copyright (C) 1999 Jeff Hartmann.
+ * Copyright (C) 1999 Precision Insight, Inc.
+ * Copyright (C) 1999 Xi Graphics, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * JEFF HARTMANN, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * TODO:
+ * - Allocate more than order 0 pages to avoid too much linear map splitting.
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/pagemap.h>
+#include <linux/miscdevice.h>
+#include <linux/pm.h>
+#include <linux/agp_backend.h>
+#include <linux/vmalloc.h>
+#include <linux/dma-mapping.h>
+#include <linux/mm.h>
+#include <asm/io.h>
+#include <asm/cacheflush.h>
+#include <asm/pgtable.h>
+#include "agp.h"
+
+__u32 *agp_gatt_table;
+int agp_memory_reserved;
+
+/*
+ * Needed by the Nforce GART driver for the time being. Would be
+ * nice to do this some other way instead of needing this export.
+ */
+EXPORT_SYMBOL_GPL(agp_memory_reserved);
+
+#if defined(CONFIG_X86)
+int map_page_into_agp(struct page *page)
+{
+ int i;
+ i = change_page_attr(page, 1, PAGE_KERNEL_NOCACHE);
+ global_flush_tlb();
+ return i;
+}
+EXPORT_SYMBOL_GPL(map_page_into_agp);
+
+int unmap_page_from_agp(struct page *page)
+{
+ int i;
+ i = change_page_attr(page, 1, PAGE_KERNEL);
+ global_flush_tlb();
+ return i;
+}
+EXPORT_SYMBOL_GPL(unmap_page_from_agp);
+#endif
+
+/*
+ * Generic routines for handling agp_memory structures -
+ * They use the basic page allocation routines to do the brunt of the work.
+ */
+
+void agp_free_key(int key)
+{
+ if (key < 0)
+ return;
+
+ if (key < MAXKEY)
+ clear_bit(key, agp_bridge->key_list);
+}
+EXPORT_SYMBOL(agp_free_key);
+
+
+static int agp_get_key(void)
+{
+ int bit;
+
+ bit = find_first_zero_bit(agp_bridge->key_list, MAXKEY);
+ if (bit < MAXKEY) {
+ set_bit(bit, agp_bridge->key_list);
+ return bit;
+ }
+ return -1;
+}
+
+
+struct agp_memory *agp_create_memory(int scratch_pages)
+{
+ struct agp_memory *new;
+
+ new = kmalloc(sizeof(struct agp_memory), GFP_KERNEL);
+
+ if (new == NULL)
+ return NULL;
+
+ memset(new, 0, sizeof(struct agp_memory));
+ new->key = agp_get_key();
+
+ if (new->key < 0) {
+ kfree(new);
+ return NULL;
+ }
+ new->memory = vmalloc(PAGE_SIZE * scratch_pages);
+
+ if (new->memory == NULL) {
+ agp_free_key(new->key);
+ kfree(new);
+ return NULL;
+ }
+ new->num_scratch_pages = scratch_pages;
+ return new;
+}
+EXPORT_SYMBOL(agp_create_memory);
+
+/**
+ * agp_free_memory - free memory associated with an agp_memory pointer.
+ *
+ * @curr: agp_memory pointer to be freed.
+ *
+ * It is the only function that can be called when the backend is not owned
+ * by the caller. (So it can free memory on client death.)
+ */
+void agp_free_memory(struct agp_memory *curr)
+{
+ size_t i;
+
+ if (curr == NULL)
+ return;
+
+ if (curr->is_bound == TRUE)
+ agp_unbind_memory(curr);
+
+ if (curr->type != 0) {
+ curr->bridge->driver->free_by_type(curr);
+ return;
+ }
+ if (curr->page_count != 0) {
+ for (i = 0; i < curr->page_count; i++) {
+ curr->bridge->driver->agp_destroy_page(phys_to_virt(curr->memory[i]));
+ }
+ }
+ agp_free_key(curr->key);
+ vfree(curr->memory);
+ kfree(curr);
+}
+EXPORT_SYMBOL(agp_free_memory);
+
+#define ENTRIES_PER_PAGE (PAGE_SIZE / sizeof(unsigned long))
+
+/**
+ * agp_allocate_memory - allocate a group of pages of a certain type.
+ *
+ * @page_count: size_t argument of the number of pages
+ * @type: u32 argument of the type of memory to be allocated.
+ *
+ * Every agp bridge device will allow you to allocate AGP_NORMAL_MEMORY which
+ * maps to physical ram. Any other type is device dependent.
+ *
+ * It returns NULL whenever memory is unavailable.
+ */
+struct agp_memory *agp_allocate_memory(struct agp_bridge_data *bridge,
+ size_t page_count, u32 type)
+{
+ int scratch_pages;
+ struct agp_memory *new;
+ size_t i;
+
+ if (!bridge)
+ return NULL;
+
+ if ((atomic_read(&bridge->current_memory_agp) + page_count) > bridge->max_memory_agp)
+ return NULL;
+
+ if (type != 0) {
+ new = bridge->driver->alloc_by_type(page_count, type);
+ if (new)
+ new->bridge = bridge;
+ return new;
+ }
+
+ scratch_pages = (page_count + ENTRIES_PER_PAGE - 1) / ENTRIES_PER_PAGE;
+
+ new = agp_create_memory(scratch_pages);
+
+ if (new == NULL)
+ return NULL;
+
+ for (i = 0; i < page_count; i++) {
+ void *addr = bridge->driver->agp_alloc_page(bridge);
+
+ if (addr == NULL) {
+ agp_free_memory(new);
+ return NULL;
+ }
+ new->memory[i] = virt_to_phys(addr);
+ new->page_count++;
+ }
+ new->bridge = bridge;
+
+ flush_agp_mappings();
+
+ return new;
+}
+EXPORT_SYMBOL(agp_allocate_memory);
+
+
+/* End - Generic routines for handling agp_memory structures */
+
+
+static int agp_return_size(void)
+{
+ int current_size;
+ void *temp;
+
+ temp = agp_bridge->current_size;
+
+ switch (agp_bridge->driver->size_type) {
+ case U8_APER_SIZE:
+ current_size = A_SIZE_8(temp)->size;
+ break;
+ case U16_APER_SIZE:
+ current_size = A_SIZE_16(temp)->size;
+ break;
+ case U32_APER_SIZE:
+ current_size = A_SIZE_32(temp)->size;
+ break;
+ case LVL2_APER_SIZE:
+ current_size = A_SIZE_LVL2(temp)->size;
+ break;
+ case FIXED_APER_SIZE:
+ current_size = A_SIZE_FIX(temp)->size;
+ break;
+ default:
+ current_size = 0;
+ break;
+ }
+
+ current_size -= (agp_memory_reserved / (1024*1024));
+ if (current_size <0)
+ current_size = 0;
+ return current_size;
+}
+
+
+int agp_num_entries(void)
+{
+ int num_entries;
+ void *temp;
+
+ temp = agp_bridge->current_size;
+
+ switch (agp_bridge->driver->size_type) {
+ case U8_APER_SIZE:
+ num_entries = A_SIZE_8(temp)->num_entries;
+ break;
+ case U16_APER_SIZE:
+ num_entries = A_SIZE_16(temp)->num_entries;
+ break;
+ case U32_APER_SIZE:
+ num_entries = A_SIZE_32(temp)->num_entries;
+ break;
+ case LVL2_APER_SIZE:
+ num_entries = A_SIZE_LVL2(temp)->num_entries;
+ break;
+ case FIXED_APER_SIZE:
+ num_entries = A_SIZE_FIX(temp)->num_entries;
+ break;
+ default:
+ num_entries = 0;
+ break;
+ }
+
+ num_entries -= agp_memory_reserved>>PAGE_SHIFT;
+ if (num_entries<0)
+ num_entries = 0;
+ return num_entries;
+}
+EXPORT_SYMBOL_GPL(agp_num_entries);
+
+
+static int check_bridge_mode(struct pci_dev *dev)
+{
+ u32 agp3;
+ u8 cap_ptr;
+
+ cap_ptr = pci_find_capability(dev, PCI_CAP_ID_AGP);
+ pci_read_config_dword(dev, cap_ptr+AGPSTAT, &agp3);
+ if (agp3 & AGPSTAT_MODE_3_0)
+ return 1;
+ return 0;
+}
+
+
+/**
+ * agp_copy_info - copy bridge state information
+ *
+ * @info: agp_kern_info pointer. The caller should insure that this pointer is valid.
+ *
+ * This function copies information about the agp bridge device and the state of
+ * the agp backend into an agp_kern_info pointer.
+ */
+int agp_copy_info(struct agp_bridge_data *bridge, struct agp_kern_info *info)
+{
+ memset(info, 0, sizeof(struct agp_kern_info));
+ if (!bridge) {
+ info->chipset = NOT_SUPPORTED;
+ return -EIO;
+ }
+
+ info->version.major = bridge->version->major;
+ info->version.minor = bridge->version->minor;
+ info->chipset = SUPPORTED;
+ info->device = bridge->dev;
+ if (check_bridge_mode(bridge->dev))
+ info->mode = bridge->mode & ~AGP3_RESERVED_MASK;
+ else
+ info->mode = bridge->mode & ~AGP2_RESERVED_MASK;
+ info->mode = bridge->mode;
+ info->aper_base = bridge->gart_bus_addr;
+ info->aper_size = agp_return_size();
+ info->max_memory = bridge->max_memory_agp;
+ info->current_memory = atomic_read(&bridge->current_memory_agp);
+ info->cant_use_aperture = bridge->driver->cant_use_aperture;
+ info->vm_ops = bridge->vm_ops;
+ info->page_mask = ~0UL;
+ return 0;
+}
+EXPORT_SYMBOL(agp_copy_info);
+
+/* End - Routine to copy over information structure */
+
+/*
+ * Routines for handling swapping of agp_memory into the GATT -
+ * These routines take agp_memory and insert them into the GATT.
+ * They call device specific routines to actually write to the GATT.
+ */
+
+/**
+ * agp_bind_memory - Bind an agp_memory structure into the GATT.
+ *
+ * @curr: agp_memory pointer
+ * @pg_start: an offset into the graphics aperture translation table
+ *
+ * It returns -EINVAL if the pointer == NULL.
+ * It returns -EBUSY if the area of the table requested is already in use.
+ */
+int agp_bind_memory(struct agp_memory *curr, off_t pg_start)
+{
+ int ret_val;
+
+ if (curr == NULL)
+ return -EINVAL;
+
+ if (curr->is_bound == TRUE) {
+ printk (KERN_INFO PFX "memory %p is already bound!\n", curr);
+ return -EINVAL;
+ }
+ if (curr->is_flushed == FALSE) {
+ curr->bridge->driver->cache_flush();
+ curr->is_flushed = TRUE;
+ }
+ ret_val = curr->bridge->driver->insert_memory(curr, pg_start, curr->type);
+
+ if (ret_val != 0)
+ return ret_val;
+
+ curr->is_bound = TRUE;
+ curr->pg_start = pg_start;
+ return 0;
+}
+EXPORT_SYMBOL(agp_bind_memory);
+
+
+/**
+ * agp_unbind_memory - Removes an agp_memory structure from the GATT
+ *
+ * @curr: agp_memory pointer to be removed from the GATT.
+ *
+ * It returns -EINVAL if this piece of agp_memory is not currently bound to
+ * the graphics aperture translation table or if the agp_memory pointer == NULL
+ */
+int agp_unbind_memory(struct agp_memory *curr)
+{
+ int ret_val;
+
+ if (curr == NULL)
+ return -EINVAL;
+
+ if (curr->is_bound != TRUE) {
+ printk (KERN_INFO PFX "memory %p was not bound!\n", curr);
+ return -EINVAL;
+ }
+
+ ret_val = curr->bridge->driver->remove_memory(curr, curr->pg_start, curr->type);
+
+ if (ret_val != 0)
+ return ret_val;
+
+ curr->is_bound = FALSE;
+ curr->pg_start = 0;
+ return 0;
+}
+EXPORT_SYMBOL(agp_unbind_memory);
+
+/* End - Routines for handling swapping of agp_memory into the GATT */
+
+
+/* Generic Agp routines - Start */
+static void agp_v2_parse_one(u32 *requested_mode, u32 *bridge_agpstat, u32 *vga_agpstat)
+{
+ u32 tmp;
+
+ if (*requested_mode & AGP2_RESERVED_MASK) {
+ printk (KERN_INFO PFX "reserved bits set in mode 0x%x. Fixed.\n", *requested_mode);
+ *requested_mode &= ~AGP2_RESERVED_MASK;
+ }
+
+ /* Check the speed bits make sense. Only one should be set. */
+ tmp = *requested_mode & 7;
+ switch (tmp) {
+ case 0:
+ printk (KERN_INFO PFX "%s tried to set rate=x0. Setting to x1 mode.\n", current->comm);
+ *requested_mode |= AGPSTAT2_1X;
+ break;
+ case 1:
+ case 2:
+ break;
+ case 3:
+ *requested_mode &= ~(AGPSTAT2_1X); /* rate=2 */
+ break;
+ case 4:
+ break;
+ case 5:
+ case 6:
+ case 7:
+ *requested_mode &= ~(AGPSTAT2_1X|AGPSTAT2_2X); /* rate=4*/
+ break;
+ }
+
+ /* disable SBA if it's not supported */
+ if (!((*bridge_agpstat & AGPSTAT_SBA) && (*vga_agpstat & AGPSTAT_SBA) && (*requested_mode & AGPSTAT_SBA)))
+ *bridge_agpstat &= ~AGPSTAT_SBA;
+
+ /* Set rate */
+ if (!((*bridge_agpstat & AGPSTAT2_4X) && (*vga_agpstat & AGPSTAT2_4X) && (*requested_mode & AGPSTAT2_4X)))
+ *bridge_agpstat &= ~AGPSTAT2_4X;
+
+ if (!((*bridge_agpstat & AGPSTAT2_2X) && (*vga_agpstat & AGPSTAT2_2X) && (*requested_mode & AGPSTAT2_2X)))
+ *bridge_agpstat &= ~AGPSTAT2_2X;
+
+ if (!((*bridge_agpstat & AGPSTAT2_1X) && (*vga_agpstat & AGPSTAT2_1X) && (*requested_mode & AGPSTAT2_1X)))
+ *bridge_agpstat &= ~AGPSTAT2_1X;
+
+ /* Now we know what mode it should be, clear out the unwanted bits. */
+ if (*bridge_agpstat & AGPSTAT2_4X)
+ *bridge_agpstat &= ~(AGPSTAT2_1X | AGPSTAT2_2X); /* 4X */
+
+ if (*bridge_agpstat & AGPSTAT2_2X)
+ *bridge_agpstat &= ~(AGPSTAT2_1X | AGPSTAT2_4X); /* 2X */
+
+ if (*bridge_agpstat & AGPSTAT2_1X)
+ *bridge_agpstat &= ~(AGPSTAT2_2X | AGPSTAT2_4X); /* 1X */
+
+ /* Apply any errata. */
+ if (agp_bridge->flags & AGP_ERRATA_FASTWRITES)
+ *bridge_agpstat &= ~AGPSTAT_FW;
+
+ if (agp_bridge->flags & AGP_ERRATA_SBA)
+ *bridge_agpstat &= ~AGPSTAT_SBA;
+
+ if (agp_bridge->flags & AGP_ERRATA_1X) {
+ *bridge_agpstat &= ~(AGPSTAT2_2X | AGPSTAT2_4X);
+ *bridge_agpstat |= AGPSTAT2_1X;
+ }
+
+ /* If we've dropped down to 1X, disable fast writes. */
+ if (*bridge_agpstat & AGPSTAT2_1X)
+ *bridge_agpstat &= ~AGPSTAT_FW;
+}
+
+/*
+ * requested_mode = Mode requested by (typically) X.
+ * bridge_agpstat = PCI_AGP_STATUS from agp bridge.
+ * vga_agpstat = PCI_AGP_STATUS from graphic card.
+ */
+static void agp_v3_parse_one(u32 *requested_mode, u32 *bridge_agpstat, u32 *vga_agpstat)
+{
+ u32 origbridge=*bridge_agpstat, origvga=*vga_agpstat;
+ u32 tmp;
+
+ if (*requested_mode & AGP3_RESERVED_MASK) {
+ printk (KERN_INFO PFX "reserved bits set in mode 0x%x. Fixed.\n", *requested_mode);
+ *requested_mode &= ~AGP3_RESERVED_MASK;
+ }
+
+ /* Check the speed bits make sense. */
+ tmp = *requested_mode & 7;
+ if (tmp == 0) {
+ printk (KERN_INFO PFX "%s tried to set rate=x0. Setting to AGP3 x4 mode.\n", current->comm);
+ *requested_mode |= AGPSTAT3_4X;
+ }
+ if (tmp >= 3) {
+ printk (KERN_INFO PFX "%s tried to set rate=x%d. Setting to AGP3 x8 mode.\n", current->comm, tmp * 4);
+ *requested_mode = (*requested_mode & ~7) | AGPSTAT3_8X;
+ }
+
+ /* ARQSZ - Set the value to the maximum one.
+ * Don't allow the mode register to override values. */
+ *bridge_agpstat = ((*bridge_agpstat & ~AGPSTAT_ARQSZ) |
+ max_t(u32,(*bridge_agpstat & AGPSTAT_ARQSZ),(*vga_agpstat & AGPSTAT_ARQSZ)));
+
+ /* Calibration cycle.
+ * Don't allow the mode register to override values. */
+ *bridge_agpstat = ((*bridge_agpstat & ~AGPSTAT_CAL_MASK) |
+ min_t(u32,(*bridge_agpstat & AGPSTAT_CAL_MASK),(*vga_agpstat & AGPSTAT_CAL_MASK)));
+
+ /* SBA *must* be supported for AGP v3 */
+ *bridge_agpstat |= AGPSTAT_SBA;
+
+ /*
+ * Set speed.
+ * Check for invalid speeds. This can happen when applications
+ * written before the AGP 3.0 standard pass AGP2.x modes to AGP3 hardware
+ */
+ if (*requested_mode & AGPSTAT_MODE_3_0) {
+ /*
+ * Caller hasn't a clue what it is doing. Bridge is in 3.0 mode,
+ * have been passed a 3.0 mode, but with 2.x speed bits set.
+ * AGP2.x 4x -> AGP3.0 4x.
+ */
+ if (*requested_mode & AGPSTAT2_4X) {
+ printk (KERN_INFO PFX "%s passes broken AGP3 flags (%x). Fixed.\n",
+ current->comm, *requested_mode);
+ *requested_mode &= ~AGPSTAT2_4X;
+ *requested_mode |= AGPSTAT3_4X;
+ }
+ } else {
+ /*
+ * The caller doesn't know what they are doing. We are in 3.0 mode,
+ * but have been passed an AGP 2.x mode.
+ * Convert AGP 1x,2x,4x -> AGP 3.0 4x.
+ */
+ printk (KERN_INFO PFX "%s passes broken AGP2 flags (%x) in AGP3 mode. Fixed.\n",
+ current->comm, *requested_mode);
+ *requested_mode &= ~(AGPSTAT2_4X | AGPSTAT2_2X | AGPSTAT2_1X);
+ *requested_mode |= AGPSTAT3_4X;
+ }
+
+ if (*requested_mode & AGPSTAT3_8X) {
+ if (!(*bridge_agpstat & AGPSTAT3_8X)) {
+ *bridge_agpstat &= ~(AGPSTAT3_8X | AGPSTAT3_RSVD);
+ *bridge_agpstat |= AGPSTAT3_4X;
+ printk ("%s requested AGPx8 but bridge not capable.\n", current->comm);
+ return;
+ }
+ if (!(*vga_agpstat & AGPSTAT3_8X)) {
+ *bridge_agpstat &= ~(AGPSTAT3_8X | AGPSTAT3_RSVD);
+ *bridge_agpstat |= AGPSTAT3_4X;
+ printk ("%s requested AGPx8 but graphic card not capable.\n", current->comm);
+ return;
+ }
+ /* All set, bridge & device can do AGP x8*/
+ *bridge_agpstat &= ~(AGPSTAT3_4X | AGPSTAT3_RSVD);
+ goto done;
+
+ } else {
+
+ /*
+ * If we didn't specify AGPx8, we can only do x4.
+ * If the hardware can't do x4, we're up shit creek, and never
+ * should have got this far.
+ */
+ *bridge_agpstat &= ~(AGPSTAT3_8X | AGPSTAT3_RSVD);
+ if ((*bridge_agpstat & AGPSTAT3_4X) && (*vga_agpstat & AGPSTAT3_4X))
+ *bridge_agpstat |= AGPSTAT3_4X;
+ else {
+ printk (KERN_INFO PFX "Badness. Don't know which AGP mode to set. "
+ "[bridge_agpstat:%x vga_agpstat:%x fell back to:- bridge_agpstat:%x vga_agpstat:%x]\n",
+ origbridge, origvga, *bridge_agpstat, *vga_agpstat);
+ if (!(*bridge_agpstat & AGPSTAT3_4X))
+ printk (KERN_INFO PFX "Bridge couldn't do AGP x4.\n");
+ if (!(*vga_agpstat & AGPSTAT3_4X))
+ printk (KERN_INFO PFX "Graphic card couldn't do AGP x4.\n");
+ return;
+ }
+ }
+
+done:
+ /* Apply any errata. */
+ if (agp_bridge->flags & AGP_ERRATA_FASTWRITES)
+ *bridge_agpstat &= ~AGPSTAT_FW;
+
+ if (agp_bridge->flags & AGP_ERRATA_SBA)
+ *bridge_agpstat &= ~AGPSTAT_SBA;
+
+ if (agp_bridge->flags & AGP_ERRATA_1X) {
+ *bridge_agpstat &= ~(AGPSTAT2_2X | AGPSTAT2_4X);
+ *bridge_agpstat |= AGPSTAT2_1X;
+ }
+}
+
+
+/**
+ * agp_collect_device_status - determine correct agp_cmd from various agp_stat's
+ * @bridge: an agp_bridge_data struct allocated for the AGP host bridge.
+ * @requested_mode: requested agp_stat from userspace (Typically from X)
+ * @bridge_agpstat: current agp_stat from AGP bridge.
+ *
+ * This function will hunt for an AGP graphics card, and try to match
+ * the requested mode to the capabilities of both the bridge and the card.
+ */
+u32 agp_collect_device_status(struct agp_bridge_data *bridge, u32 requested_mode, u32 bridge_agpstat)
+{
+ struct pci_dev *device = NULL;
+ u32 vga_agpstat;
+ u8 cap_ptr;
+
+ for (;;) {
+ device = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, device);
+ if (!device) {
+ printk (KERN_INFO PFX "Couldn't find an AGP VGA controller.\n");
+ return 0;
+ }
+ cap_ptr = pci_find_capability(device, PCI_CAP_ID_AGP);
+ if (cap_ptr)
+ break;
+ }
+
+ /*
+ * Ok, here we have a AGP device. Disable impossible
+ * settings, and adjust the readqueue to the minimum.
+ */
+ pci_read_config_dword(device, cap_ptr+PCI_AGP_STATUS, &vga_agpstat);
+
+ /* adjust RQ depth */
+ bridge_agpstat = ((bridge_agpstat & ~AGPSTAT_RQ_DEPTH) |
+ min_t(u32, (requested_mode & AGPSTAT_RQ_DEPTH),
+ min_t(u32, (bridge_agpstat & AGPSTAT_RQ_DEPTH), (vga_agpstat & AGPSTAT_RQ_DEPTH))));
+
+ /* disable FW if it's not supported */
+ if (!((bridge_agpstat & AGPSTAT_FW) &&
+ (vga_agpstat & AGPSTAT_FW) &&
+ (requested_mode & AGPSTAT_FW)))
+ bridge_agpstat &= ~AGPSTAT_FW;
+
+ /* Check to see if we are operating in 3.0 mode */
+ if (check_bridge_mode(agp_bridge->dev))
+ agp_v3_parse_one(&requested_mode, &bridge_agpstat, &vga_agpstat);
+ else
+ agp_v2_parse_one(&requested_mode, &bridge_agpstat, &vga_agpstat);
+
+ pci_dev_put(device);
+ return bridge_agpstat;
+}
+EXPORT_SYMBOL(agp_collect_device_status);
+
+
+void agp_device_command(u32 bridge_agpstat, int agp_v3)
+{
+ struct pci_dev *device = NULL;
+ int mode;
+
+ mode = bridge_agpstat & 0x7;
+ if (agp_v3)
+ mode *= 4;
+
+ for_each_pci_dev(device) {
+ u8 agp = pci_find_capability(device, PCI_CAP_ID_AGP);
+ if (!agp)
+ continue;
+
+ printk(KERN_INFO PFX "Putting AGP V%d device at %s into %dx mode\n",
+ agp_v3 ? 3 : 2, pci_name(device), mode);
+ pci_write_config_dword(device, agp + PCI_AGP_COMMAND, bridge_agpstat);
+ }
+}
+EXPORT_SYMBOL(agp_device_command);
+
+
+void get_agp_version(struct agp_bridge_data *bridge)
+{
+ u32 ncapid;
+
+ /* Exit early if already set by errata workarounds. */
+ if (bridge->major_version != 0)
+ return;
+
+ pci_read_config_dword(bridge->dev, bridge->capndx, &ncapid);
+ bridge->major_version = (ncapid >> AGP_MAJOR_VERSION_SHIFT) & 0xf;
+ bridge->minor_version = (ncapid >> AGP_MINOR_VERSION_SHIFT) & 0xf;
+}
+EXPORT_SYMBOL(get_agp_version);
+
+
+void agp_generic_enable(struct agp_bridge_data *bridge, u32 requested_mode)
+{
+ u32 bridge_agpstat, temp;
+
+ get_agp_version(agp_bridge);
+
+ printk(KERN_INFO PFX "Found an AGP %d.%d compliant device at %s.\n",
+ agp_bridge->major_version,
+ agp_bridge->minor_version,
+ pci_name(agp_bridge->dev));
+
+ pci_read_config_dword(agp_bridge->dev,
+ agp_bridge->capndx + PCI_AGP_STATUS, &bridge_agpstat);
+
+ bridge_agpstat = agp_collect_device_status(agp_bridge, requested_mode, bridge_agpstat);
+ if (bridge_agpstat == 0)
+ /* Something bad happened. FIXME: Return error code? */
+ return;
+
+ bridge_agpstat |= AGPSTAT_AGP_ENABLE;
+
+ /* Do AGP version specific frobbing. */
+ if (bridge->major_version >= 3) {
+ if (check_bridge_mode(bridge->dev)) {
+ /* If we have 3.5, we can do the isoch stuff. */
+ if (bridge->minor_version >= 5)
+ agp_3_5_enable(bridge);
+ agp_device_command(bridge_agpstat, TRUE);
+ return;
+ } else {
+ /* Disable calibration cycle in RX91<1> when not in AGP3.0 mode of operation.*/
+ bridge_agpstat &= ~(7<<10) ;
+ pci_read_config_dword(bridge->dev,
+ bridge->capndx+AGPCTRL, &temp);
+ temp |= (1<<9);
+ pci_write_config_dword(bridge->dev,
+ bridge->capndx+AGPCTRL, temp);
+
+ printk (KERN_INFO PFX "Device is in legacy mode,"
+ " falling back to 2.x\n");
+ }
+ }
+
+ /* AGP v<3 */
+ agp_device_command(bridge_agpstat, FALSE);
+}
+EXPORT_SYMBOL(agp_generic_enable);
+
+
+int agp_generic_create_gatt_table(struct agp_bridge_data *bridge)
+{
+ char *table;
+ char *table_end;
+ int size;
+ int page_order;
+ int num_entries;
+ int i;
+ void *temp;
+ struct page *page;
+
+ /* The generic routines can't handle 2 level gatt's */
+ if (bridge->driver->size_type == LVL2_APER_SIZE)
+ return -EINVAL;
+
+ table = NULL;
+ i = bridge->aperture_size_idx;
+ temp = bridge->current_size;
+ size = page_order = num_entries = 0;
+
+ if (bridge->driver->size_type != FIXED_APER_SIZE) {
+ do {
+ switch (bridge->driver->size_type) {
+ case U8_APER_SIZE:
+ size = A_SIZE_8(temp)->size;
+ page_order =
+ A_SIZE_8(temp)->page_order;
+ num_entries =
+ A_SIZE_8(temp)->num_entries;
+ break;
+ case U16_APER_SIZE:
+ size = A_SIZE_16(temp)->size;
+ page_order = A_SIZE_16(temp)->page_order;
+ num_entries = A_SIZE_16(temp)->num_entries;
+ break;
+ case U32_APER_SIZE:
+ size = A_SIZE_32(temp)->size;
+ page_order = A_SIZE_32(temp)->page_order;
+ num_entries = A_SIZE_32(temp)->num_entries;
+ break;
+ /* This case will never really happen. */
+ case FIXED_APER_SIZE:
+ case LVL2_APER_SIZE:
+ default:
+ size = page_order = num_entries = 0;
+ break;
+ }
+
+ table = (char *) __get_free_pages(GFP_KERNEL,
+ page_order);
+
+ if (table == NULL) {
+ i++;
+ switch (bridge->driver->size_type) {
+ case U8_APER_SIZE:
+ bridge->current_size = A_IDX8(bridge);
+ break;
+ case U16_APER_SIZE:
+ bridge->current_size = A_IDX16(bridge);
+ break;
+ case U32_APER_SIZE:
+ bridge->current_size = A_IDX32(bridge);
+ break;
+ /* This case will never really happen. */
+ case FIXED_APER_SIZE:
+ case LVL2_APER_SIZE:
+ default:
+ bridge->current_size =
+ bridge->current_size;
+ break;
+ }
+ temp = bridge->current_size;
+ } else {
+ bridge->aperture_size_idx = i;
+ }
+ } while (!table && (i < bridge->driver->num_aperture_sizes));
+ } else {
+ size = ((struct aper_size_info_fixed *) temp)->size;
+ page_order = ((struct aper_size_info_fixed *) temp)->page_order;
+ num_entries = ((struct aper_size_info_fixed *) temp)->num_entries;
+ table = (char *) __get_free_pages(GFP_KERNEL, page_order);
+ }
+
+ if (table == NULL)
+ return -ENOMEM;
+
+ table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1);
+
+ for (page = virt_to_page(table); page <= virt_to_page(table_end); page++)
+ SetPageReserved(page);
+
+ bridge->gatt_table_real = (u32 *) table;
+ agp_gatt_table = (void *)table;
+
+ bridge->driver->cache_flush();
+ bridge->gatt_table = ioremap_nocache(virt_to_phys(table),
+ (PAGE_SIZE * (1 << page_order)));
+ bridge->driver->cache_flush();
+
+ if (bridge->gatt_table == NULL) {
+ for (page = virt_to_page(table); page <= virt_to_page(table_end); page++)
+ ClearPageReserved(page);
+
+ free_pages((unsigned long) table, page_order);
+
+ return -ENOMEM;
+ }
+ bridge->gatt_bus_addr = virt_to_phys(bridge->gatt_table_real);
+
+ /* AK: bogus, should encode addresses > 4GB */
+ for (i = 0; i < num_entries; i++) {
+ writel(bridge->scratch_page, bridge->gatt_table+i);
+ readl(bridge->gatt_table+i); /* PCI Posting. */
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(agp_generic_create_gatt_table);
+
+int agp_generic_free_gatt_table(struct agp_bridge_data *bridge)
+{
+ int page_order;
+ char *table, *table_end;
+ void *temp;
+ struct page *page;
+
+ temp = bridge->current_size;
+
+ switch (bridge->driver->size_type) {
+ case U8_APER_SIZE:
+ page_order = A_SIZE_8(temp)->page_order;
+ break;
+ case U16_APER_SIZE:
+ page_order = A_SIZE_16(temp)->page_order;
+ break;
+ case U32_APER_SIZE:
+ page_order = A_SIZE_32(temp)->page_order;
+ break;
+ case FIXED_APER_SIZE:
+ page_order = A_SIZE_FIX(temp)->page_order;
+ break;
+ case LVL2_APER_SIZE:
+ /* The generic routines can't deal with 2 level gatt's */
+ return -EINVAL;
+ break;
+ default:
+ page_order = 0;
+ break;
+ }
+
+ /* Do not worry about freeing memory, because if this is
+ * called, then all agp memory is deallocated and removed
+ * from the table. */
+
+ iounmap(bridge->gatt_table);
+ table = (char *) bridge->gatt_table_real;
+ table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1);
+
+ for (page = virt_to_page(table); page <= virt_to_page(table_end); page++)
+ ClearPageReserved(page);
+
+ free_pages((unsigned long) bridge->gatt_table_real, page_order);
+
+ agp_gatt_table = NULL;
+ bridge->gatt_table = NULL;
+ bridge->gatt_table_real = NULL;
+ bridge->gatt_bus_addr = 0;
+
+ return 0;
+}
+EXPORT_SYMBOL(agp_generic_free_gatt_table);
+
+
+int agp_generic_insert_memory(struct agp_memory * mem, off_t pg_start, int type)
+{
+ int num_entries;
+ size_t i;
+ off_t j;
+ void *temp;
+ struct agp_bridge_data *bridge;
+
+ bridge = mem->bridge;
+ if (!bridge)
+ return -EINVAL;
+
+ temp = bridge->current_size;
+
+ switch (bridge->driver->size_type) {
+ case U8_APER_SIZE:
+ num_entries = A_SIZE_8(temp)->num_entries;
+ break;
+ case U16_APER_SIZE:
+ num_entries = A_SIZE_16(temp)->num_entries;
+ break;
+ case U32_APER_SIZE:
+ num_entries = A_SIZE_32(temp)->num_entries;
+ break;
+ case FIXED_APER_SIZE:
+ num_entries = A_SIZE_FIX(temp)->num_entries;
+ break;
+ case LVL2_APER_SIZE:
+ /* The generic routines can't deal with 2 level gatt's */
+ return -EINVAL;
+ break;
+ default:
+ num_entries = 0;
+ break;
+ }
+
+ num_entries -= agp_memory_reserved/PAGE_SIZE;
+ if (num_entries < 0) num_entries = 0;
+
+ if (type != 0 || mem->type != 0) {
+ /* The generic routines know nothing of memory types */
+ return -EINVAL;
+ }
+
+ /* AK: could wrap */
+ if ((pg_start + mem->page_count) > num_entries)
+ return -EINVAL;
+
+ j = pg_start;
+
+ while (j < (pg_start + mem->page_count)) {
+ if (!PGE_EMPTY(bridge, readl(bridge->gatt_table+j)))
+ return -EBUSY;
+ j++;
+ }
+
+ if (mem->is_flushed == FALSE) {
+ bridge->driver->cache_flush();
+ mem->is_flushed = TRUE;
+ }
+
+ for (i = 0, j = pg_start; i < mem->page_count; i++, j++) {
+ writel(bridge->driver->mask_memory(bridge, mem->memory[i], mem->type), bridge->gatt_table+j);
+ readl(bridge->gatt_table+j); /* PCI Posting. */
+ }
+
+ bridge->driver->tlb_flush(mem);
+ return 0;
+}
+EXPORT_SYMBOL(agp_generic_insert_memory);
+
+
+int agp_generic_remove_memory(struct agp_memory *mem, off_t pg_start, int type)
+{
+ size_t i;
+ struct agp_bridge_data *bridge;
+
+ bridge = mem->bridge;
+ if (!bridge)
+ return -EINVAL;
+
+ if (type != 0 || mem->type != 0) {
+ /* The generic routines know nothing of memory types */
+ return -EINVAL;
+ }
+
+ /* AK: bogus, should encode addresses > 4GB */
+ for (i = pg_start; i < (mem->page_count + pg_start); i++) {
+ writel(bridge->scratch_page, bridge->gatt_table+i);
+ readl(bridge->gatt_table+i); /* PCI Posting. */
+ }
+
+ global_cache_flush();
+ bridge->driver->tlb_flush(mem);
+ return 0;
+}
+EXPORT_SYMBOL(agp_generic_remove_memory);
+
+
+struct agp_memory *agp_generic_alloc_by_type(size_t page_count, int type)
+{
+ return NULL;
+}
+EXPORT_SYMBOL(agp_generic_alloc_by_type);
+
+
+void agp_generic_free_by_type(struct agp_memory *curr)
+{
+ vfree(curr->memory);
+ agp_free_key(curr->key);
+ kfree(curr);
+}
+EXPORT_SYMBOL(agp_generic_free_by_type);
+
+
+/*
+ * Basic Page Allocation Routines -
+ * These routines handle page allocation and by default they reserve the allocated
+ * memory. They also handle incrementing the current_memory_agp value, Which is checked
+ * against a maximum value.
+ */
+
+void *agp_generic_alloc_page(struct agp_bridge_data *bridge)
+{
+ struct page * page;
+
+ page = alloc_page(GFP_KERNEL);
+ if (page == NULL)
+ return NULL;
+
+ map_page_into_agp(page);
+
+ get_page(page);
+ SetPageLocked(page);
+ atomic_inc(&agp_bridge->current_memory_agp);
+ return page_address(page);
+}
+EXPORT_SYMBOL(agp_generic_alloc_page);
+
+
+void agp_generic_destroy_page(void *addr)
+{
+ struct page *page;
+
+ if (addr == NULL)
+ return;
+
+ page = virt_to_page(addr);
+ unmap_page_from_agp(page);
+ put_page(page);
+ unlock_page(page);
+ free_page((unsigned long)addr);
+ atomic_dec(&agp_bridge->current_memory_agp);
+}
+EXPORT_SYMBOL(agp_generic_destroy_page);
+
+/* End Basic Page Allocation Routines */
+
+
+/**
+ * agp_enable - initialise the agp point-to-point connection.
+ *
+ * @mode: agp mode register value to configure with.
+ */
+void agp_enable(struct agp_bridge_data *bridge, u32 mode)
+{
+ if (!bridge)
+ return;
+ bridge->driver->agp_enable(bridge, mode);
+}
+EXPORT_SYMBOL(agp_enable);
+
+/* When we remove the global variable agp_bridge from all drivers
+ * then agp_alloc_bridge and agp_generic_find_bridge need to be updated
+ */
+
+struct agp_bridge_data *agp_generic_find_bridge(struct pci_dev *pdev)
+{
+ if (list_empty(&agp_bridges))
+ return NULL;
+
+ return agp_bridge;
+}
+
+static void ipi_handler(void *null)
+{
+ flush_agp_cache();
+}
+
+void global_cache_flush(void)
+{
+ if (on_each_cpu(ipi_handler, NULL, 1, 1) != 0)
+ panic(PFX "timed out waiting for the other CPUs!\n");
+}
+EXPORT_SYMBOL(global_cache_flush);
+
+unsigned long agp_generic_mask_memory(struct agp_bridge_data *bridge,
+ unsigned long addr, int type)
+{
+ /* memory type is ignored in the generic routine */
+ if (bridge->driver->masks)
+ return addr | bridge->driver->masks[0].mask;
+ else
+ return addr;
+}
+EXPORT_SYMBOL(agp_generic_mask_memory);
+
+/*
+ * These functions are implemented according to the AGPv3 spec,
+ * which covers implementation details that had previously been
+ * left open.
+ */
+
+int agp3_generic_fetch_size(void)
+{
+ u16 temp_size;
+ int i;
+ struct aper_size_info_16 *values;
+
+ pci_read_config_word(agp_bridge->dev, agp_bridge->capndx+AGPAPSIZE, &temp_size);
+ values = A_SIZE_16(agp_bridge->driver->aperture_sizes);
+
+ for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++) {
+ if (temp_size == values[i].size_value) {
+ agp_bridge->previous_size =
+ agp_bridge->current_size = (void *) (values + i);
+
+ agp_bridge->aperture_size_idx = i;
+ return values[i].size;
+ }
+ }
+ return 0;
+}
+EXPORT_SYMBOL(agp3_generic_fetch_size);
+
+void agp3_generic_tlbflush(struct agp_memory *mem)
+{
+ u32 ctrl;
+ pci_read_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, &ctrl);
+ pci_write_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, ctrl & ~AGPCTRL_GTLBEN);
+ pci_write_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, ctrl);
+}
+EXPORT_SYMBOL(agp3_generic_tlbflush);
+
+int agp3_generic_configure(void)
+{
+ u32 temp;
+ struct aper_size_info_16 *current_size;
+
+ current_size = A_SIZE_16(agp_bridge->current_size);
+
+ pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp);
+ agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+
+ /* set aperture size */
+ pci_write_config_word(agp_bridge->dev, agp_bridge->capndx+AGPAPSIZE, current_size->size_value);
+ /* set gart pointer */
+ pci_write_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPGARTLO, agp_bridge->gatt_bus_addr);
+ /* enable aperture and GTLB */
+ pci_read_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, &temp);
+ pci_write_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, temp | AGPCTRL_APERENB | AGPCTRL_GTLBEN);
+ return 0;
+}
+EXPORT_SYMBOL(agp3_generic_configure);
+
+void agp3_generic_cleanup(void)
+{
+ u32 ctrl;
+ pci_read_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, &ctrl);
+ pci_write_config_dword(agp_bridge->dev, agp_bridge->capndx+AGPCTRL, ctrl & ~AGPCTRL_APERENB);
+}
+EXPORT_SYMBOL(agp3_generic_cleanup);
+
+struct aper_size_info_16 agp3_generic_sizes[AGP_GENERIC_SIZES_ENTRIES] =
+{
+ {4096, 1048576, 10,0x000},
+ {2048, 524288, 9, 0x800},
+ {1024, 262144, 8, 0xc00},
+ { 512, 131072, 7, 0xe00},
+ { 256, 65536, 6, 0xf00},
+ { 128, 32768, 5, 0xf20},
+ { 64, 16384, 4, 0xf30},
+ { 32, 8192, 3, 0xf38},
+ { 16, 4096, 2, 0xf3c},
+ { 8, 2048, 1, 0xf3e},
+ { 4, 1024, 0, 0xf3f}
+};
+EXPORT_SYMBOL(agp3_generic_sizes);
+
diff --git a/drivers/char/agp/hp-agp.c b/drivers/char/agp/hp-agp.c
new file mode 100644
index 00000000000..6052bfa04c7
--- /dev/null
+++ b/drivers/char/agp/hp-agp.c
@@ -0,0 +1,552 @@
+/*
+ * HP zx1 AGPGART routines.
+ *
+ * (c) Copyright 2002, 2003 Hewlett-Packard Development Company, L.P.
+ * Bjorn Helgaas <bjorn.helgaas@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/acpi.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/agp_backend.h>
+
+#include <asm/acpi-ext.h>
+
+#include "agp.h"
+
+#ifndef log2
+#define log2(x) ffz(~(x))
+#endif
+
+#define HP_ZX1_IOC_OFFSET 0x1000 /* ACPI reports SBA, we want IOC */
+
+/* HP ZX1 IOC registers */
+#define HP_ZX1_IBASE 0x300
+#define HP_ZX1_IMASK 0x308
+#define HP_ZX1_PCOM 0x310
+#define HP_ZX1_TCNFG 0x318
+#define HP_ZX1_PDIR_BASE 0x320
+
+#define HP_ZX1_IOVA_BASE GB(1UL)
+#define HP_ZX1_IOVA_SIZE GB(1UL)
+#define HP_ZX1_GART_SIZE (HP_ZX1_IOVA_SIZE / 2)
+#define HP_ZX1_SBA_IOMMU_COOKIE 0x0000badbadc0ffeeUL
+
+#define HP_ZX1_PDIR_VALID_BIT 0x8000000000000000UL
+#define HP_ZX1_IOVA_TO_PDIR(va) ((va - hp_private.iova_base) >> hp_private.io_tlb_shift)
+
+#define AGP8X_MODE_BIT 3
+#define AGP8X_MODE (1 << AGP8X_MODE_BIT)
+
+/* AGP bridge need not be PCI device, but DRM thinks it is. */
+static struct pci_dev fake_bridge_dev;
+
+static int hp_zx1_gart_found;
+
+static struct aper_size_info_fixed hp_zx1_sizes[] =
+{
+ {0, 0, 0}, /* filled in by hp_zx1_fetch_size() */
+};
+
+static struct gatt_mask hp_zx1_masks[] =
+{
+ {.mask = HP_ZX1_PDIR_VALID_BIT, .type = 0}
+};
+
+static struct _hp_private {
+ volatile u8 __iomem *ioc_regs;
+ volatile u8 __iomem *lba_regs;
+ int lba_cap_offset;
+ u64 *io_pdir; // PDIR for entire IOVA
+ u64 *gatt; // PDIR just for GART (subset of above)
+ u64 gatt_entries;
+ u64 iova_base;
+ u64 gart_base;
+ u64 gart_size;
+ u64 io_pdir_size;
+ int io_pdir_owner; // do we own it, or share it with sba_iommu?
+ int io_page_size;
+ int io_tlb_shift;
+ int io_tlb_ps; // IOC ps config
+ int io_pages_per_kpage;
+} hp_private;
+
+static int __init hp_zx1_ioc_shared(void)
+{
+ struct _hp_private *hp = &hp_private;
+
+ printk(KERN_INFO PFX "HP ZX1 IOC: IOPDIR shared with sba_iommu\n");
+
+ /*
+ * IOC already configured by sba_iommu module; just use
+ * its setup. We assume:
+ * - IOVA space is 1Gb in size
+ * - first 512Mb is IOMMU, second 512Mb is GART
+ */
+ hp->io_tlb_ps = readq(hp->ioc_regs+HP_ZX1_TCNFG);
+ switch (hp->io_tlb_ps) {
+ case 0: hp->io_tlb_shift = 12; break;
+ case 1: hp->io_tlb_shift = 13; break;
+ case 2: hp->io_tlb_shift = 14; break;
+ case 3: hp->io_tlb_shift = 16; break;
+ default:
+ printk(KERN_ERR PFX "Invalid IOTLB page size "
+ "configuration 0x%x\n", hp->io_tlb_ps);
+ hp->gatt = NULL;
+ hp->gatt_entries = 0;
+ return -ENODEV;
+ }
+ hp->io_page_size = 1 << hp->io_tlb_shift;
+ hp->io_pages_per_kpage = PAGE_SIZE / hp->io_page_size;
+
+ hp->iova_base = readq(hp->ioc_regs+HP_ZX1_IBASE) & ~0x1;
+ hp->gart_base = hp->iova_base + HP_ZX1_IOVA_SIZE - HP_ZX1_GART_SIZE;
+
+ hp->gart_size = HP_ZX1_GART_SIZE;
+ hp->gatt_entries = hp->gart_size / hp->io_page_size;
+
+ hp->io_pdir = phys_to_virt(readq(hp->ioc_regs+HP_ZX1_PDIR_BASE));
+ hp->gatt = &hp->io_pdir[HP_ZX1_IOVA_TO_PDIR(hp->gart_base)];
+
+ if (hp->gatt[0] != HP_ZX1_SBA_IOMMU_COOKIE) {
+ /* Normal case when no AGP device in system */
+ hp->gatt = NULL;
+ hp->gatt_entries = 0;
+ printk(KERN_ERR PFX "No reserved IO PDIR entry found; "
+ "GART disabled\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int __init
+hp_zx1_ioc_owner (void)
+{
+ struct _hp_private *hp = &hp_private;
+
+ printk(KERN_INFO PFX "HP ZX1 IOC: IOPDIR dedicated to GART\n");
+
+ /*
+ * Select an IOV page size no larger than system page size.
+ */
+ if (PAGE_SIZE >= KB(64)) {
+ hp->io_tlb_shift = 16;
+ hp->io_tlb_ps = 3;
+ } else if (PAGE_SIZE >= KB(16)) {
+ hp->io_tlb_shift = 14;
+ hp->io_tlb_ps = 2;
+ } else if (PAGE_SIZE >= KB(8)) {
+ hp->io_tlb_shift = 13;
+ hp->io_tlb_ps = 1;
+ } else {
+ hp->io_tlb_shift = 12;
+ hp->io_tlb_ps = 0;
+ }
+ hp->io_page_size = 1 << hp->io_tlb_shift;
+ hp->io_pages_per_kpage = PAGE_SIZE / hp->io_page_size;
+
+ hp->iova_base = HP_ZX1_IOVA_BASE;
+ hp->gart_size = HP_ZX1_GART_SIZE;
+ hp->gart_base = hp->iova_base + HP_ZX1_IOVA_SIZE - hp->gart_size;
+
+ hp->gatt_entries = hp->gart_size / hp->io_page_size;
+ hp->io_pdir_size = (HP_ZX1_IOVA_SIZE / hp->io_page_size) * sizeof(u64);
+
+ return 0;
+}
+
+static int __init
+hp_zx1_ioc_init (u64 hpa)
+{
+ struct _hp_private *hp = &hp_private;
+
+ hp->ioc_regs = ioremap(hpa, 1024);
+ if (!hp->ioc_regs)
+ return -ENOMEM;
+
+ /*
+ * If the IOTLB is currently disabled, we can take it over.
+ * Otherwise, we have to share with sba_iommu.
+ */
+ hp->io_pdir_owner = (readq(hp->ioc_regs+HP_ZX1_IBASE) & 0x1) == 0;
+
+ if (hp->io_pdir_owner)
+ return hp_zx1_ioc_owner();
+
+ return hp_zx1_ioc_shared();
+}
+
+static int
+hp_zx1_lba_find_capability (volatile u8 __iomem *hpa, int cap)
+{
+ u16 status;
+ u8 pos, id;
+ int ttl = 48;
+
+ status = readw(hpa+PCI_STATUS);
+ if (!(status & PCI_STATUS_CAP_LIST))
+ return 0;
+ pos = readb(hpa+PCI_CAPABILITY_LIST);
+ while (ttl-- && pos >= 0x40) {
+ pos &= ~3;
+ id = readb(hpa+pos+PCI_CAP_LIST_ID);
+ if (id == 0xff)
+ break;
+ if (id == cap)
+ return pos;
+ pos = readb(hpa+pos+PCI_CAP_LIST_NEXT);
+ }
+ return 0;
+}
+
+static int __init
+hp_zx1_lba_init (u64 hpa)
+{
+ struct _hp_private *hp = &hp_private;
+ int cap;
+
+ hp->lba_regs = ioremap(hpa, 256);
+ if (!hp->lba_regs)
+ return -ENOMEM;
+
+ hp->lba_cap_offset = hp_zx1_lba_find_capability(hp->lba_regs, PCI_CAP_ID_AGP);
+
+ cap = readl(hp->lba_regs+hp->lba_cap_offset) & 0xff;
+ if (cap != PCI_CAP_ID_AGP) {
+ printk(KERN_ERR PFX "Invalid capability ID 0x%02x at 0x%x\n",
+ cap, hp->lba_cap_offset);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int
+hp_zx1_fetch_size(void)
+{
+ int size;
+
+ size = hp_private.gart_size / MB(1);
+ hp_zx1_sizes[0].size = size;
+ agp_bridge->current_size = (void *) &hp_zx1_sizes[0];
+ return size;
+}
+
+static int
+hp_zx1_configure (void)
+{
+ struct _hp_private *hp = &hp_private;
+
+ agp_bridge->gart_bus_addr = hp->gart_base;
+ agp_bridge->capndx = hp->lba_cap_offset;
+ agp_bridge->mode = readl(hp->lba_regs+hp->lba_cap_offset+PCI_AGP_STATUS);
+
+ if (hp->io_pdir_owner) {
+ writel(virt_to_phys(hp->io_pdir), hp->ioc_regs+HP_ZX1_PDIR_BASE);
+ readl(hp->ioc_regs+HP_ZX1_PDIR_BASE);
+ writel(hp->io_tlb_ps, hp->ioc_regs+HP_ZX1_TCNFG);
+ readl(hp->ioc_regs+HP_ZX1_TCNFG);
+ writel(~(HP_ZX1_IOVA_SIZE-1), hp->ioc_regs+HP_ZX1_IMASK);
+ readl(hp->ioc_regs+HP_ZX1_IMASK);
+ writel(hp->iova_base|1, hp->ioc_regs+HP_ZX1_IBASE);
+ readl(hp->ioc_regs+HP_ZX1_IBASE);
+ writel(hp->iova_base|log2(HP_ZX1_IOVA_SIZE), hp->ioc_regs+HP_ZX1_PCOM);
+ readl(hp->ioc_regs+HP_ZX1_PCOM);
+ }
+
+ return 0;
+}
+
+static void
+hp_zx1_cleanup (void)
+{
+ struct _hp_private *hp = &hp_private;
+
+ if (hp->ioc_regs) {
+ if (hp->io_pdir_owner) {
+ writeq(0, hp->ioc_regs+HP_ZX1_IBASE);
+ readq(hp->ioc_regs+HP_ZX1_IBASE);
+ }
+ iounmap(hp->ioc_regs);
+ }
+ if (hp->lba_regs)
+ iounmap(hp->lba_regs);
+}
+
+static void
+hp_zx1_tlbflush (struct agp_memory *mem)
+{
+ struct _hp_private *hp = &hp_private;
+
+ writeq(hp->gart_base | log2(hp->gart_size), hp->ioc_regs+HP_ZX1_PCOM);
+ readq(hp->ioc_regs+HP_ZX1_PCOM);
+}
+
+static int
+hp_zx1_create_gatt_table (struct agp_bridge_data *bridge)
+{
+ struct _hp_private *hp = &hp_private;
+ int i;
+
+ if (hp->io_pdir_owner) {
+ hp->io_pdir = (u64 *) __get_free_pages(GFP_KERNEL,
+ get_order(hp->io_pdir_size));
+ if (!hp->io_pdir) {
+ printk(KERN_ERR PFX "Couldn't allocate contiguous "
+ "memory for I/O PDIR\n");
+ hp->gatt = NULL;
+ hp->gatt_entries = 0;
+ return -ENOMEM;
+ }
+ memset(hp->io_pdir, 0, hp->io_pdir_size);
+
+ hp->gatt = &hp->io_pdir[HP_ZX1_IOVA_TO_PDIR(hp->gart_base)];
+ }
+
+ for (i = 0; i < hp->gatt_entries; i++) {
+ hp->gatt[i] = (unsigned long) agp_bridge->scratch_page;
+ }
+
+ return 0;
+}
+
+static int
+hp_zx1_free_gatt_table (struct agp_bridge_data *bridge)
+{
+ struct _hp_private *hp = &hp_private;
+
+ if (hp->io_pdir_owner)
+ free_pages((unsigned long) hp->io_pdir,
+ get_order(hp->io_pdir_size));
+ else
+ hp->gatt[0] = HP_ZX1_SBA_IOMMU_COOKIE;
+ return 0;
+}
+
+static int
+hp_zx1_insert_memory (struct agp_memory *mem, off_t pg_start, int type)
+{
+ struct _hp_private *hp = &hp_private;
+ int i, k;
+ off_t j, io_pg_start;
+ int io_pg_count;
+
+ if (type != 0 || mem->type != 0) {
+ return -EINVAL;
+ }
+
+ io_pg_start = hp->io_pages_per_kpage * pg_start;
+ io_pg_count = hp->io_pages_per_kpage * mem->page_count;
+ if ((io_pg_start + io_pg_count) > hp->gatt_entries) {
+ return -EINVAL;
+ }
+
+ j = io_pg_start;
+ while (j < (io_pg_start + io_pg_count)) {
+ if (hp->gatt[j]) {
+ return -EBUSY;
+ }
+ j++;
+ }
+
+ if (mem->is_flushed == FALSE) {
+ global_cache_flush();
+ mem->is_flushed = TRUE;
+ }
+
+ for (i = 0, j = io_pg_start; i < mem->page_count; i++) {
+ unsigned long paddr;
+
+ paddr = mem->memory[i];
+ for (k = 0;
+ k < hp->io_pages_per_kpage;
+ k++, j++, paddr += hp->io_page_size) {
+ hp->gatt[j] =
+ agp_bridge->driver->mask_memory(agp_bridge,
+ paddr, type);
+ }
+ }
+
+ agp_bridge->driver->tlb_flush(mem);
+ return 0;
+}
+
+static int
+hp_zx1_remove_memory (struct agp_memory *mem, off_t pg_start, int type)
+{
+ struct _hp_private *hp = &hp_private;
+ int i, io_pg_start, io_pg_count;
+
+ if (type != 0 || mem->type != 0) {
+ return -EINVAL;
+ }
+
+ io_pg_start = hp->io_pages_per_kpage * pg_start;
+ io_pg_count = hp->io_pages_per_kpage * mem->page_count;
+ for (i = io_pg_start; i < io_pg_count + io_pg_start; i++) {
+ hp->gatt[i] = agp_bridge->scratch_page;
+ }
+
+ agp_bridge->driver->tlb_flush(mem);
+ return 0;
+}
+
+static unsigned long
+hp_zx1_mask_memory (struct agp_bridge_data *bridge,
+ unsigned long addr, int type)
+{
+ return HP_ZX1_PDIR_VALID_BIT | addr;
+}
+
+static void
+hp_zx1_enable (struct agp_bridge_data *bridge, u32 mode)
+{
+ struct _hp_private *hp = &hp_private;
+ u32 command;
+
+ command = readl(hp->lba_regs+hp->lba_cap_offset+PCI_AGP_STATUS);
+ command = agp_collect_device_status(bridge, mode, command);
+ command |= 0x00000100;
+
+ writel(command, hp->lba_regs+hp->lba_cap_offset+PCI_AGP_COMMAND);
+
+ agp_device_command(command, (mode & AGP8X_MODE) != 0);
+}
+
+struct agp_bridge_driver hp_zx1_driver = {
+ .owner = THIS_MODULE,
+ .size_type = FIXED_APER_SIZE,
+ .configure = hp_zx1_configure,
+ .fetch_size = hp_zx1_fetch_size,
+ .cleanup = hp_zx1_cleanup,
+ .tlb_flush = hp_zx1_tlbflush,
+ .mask_memory = hp_zx1_mask_memory,
+ .masks = hp_zx1_masks,
+ .agp_enable = hp_zx1_enable,
+ .cache_flush = global_cache_flush,
+ .create_gatt_table = hp_zx1_create_gatt_table,
+ .free_gatt_table = hp_zx1_free_gatt_table,
+ .insert_memory = hp_zx1_insert_memory,
+ .remove_memory = hp_zx1_remove_memory,
+ .alloc_by_type = agp_generic_alloc_by_type,
+ .free_by_type = agp_generic_free_by_type,
+ .agp_alloc_page = agp_generic_alloc_page,
+ .agp_destroy_page = agp_generic_destroy_page,
+ .cant_use_aperture = 1,
+};
+
+static int __init
+hp_zx1_setup (u64 ioc_hpa, u64 lba_hpa)
+{
+ struct agp_bridge_data *bridge;
+ int error = 0;
+
+ error = hp_zx1_ioc_init(ioc_hpa);
+ if (error)
+ goto fail;
+
+ error = hp_zx1_lba_init(lba_hpa);
+ if (error)
+ goto fail;
+
+ bridge = agp_alloc_bridge();
+ if (!bridge) {
+ error = -ENOMEM;
+ goto fail;
+ }
+ bridge->driver = &hp_zx1_driver;
+
+ fake_bridge_dev.vendor = PCI_VENDOR_ID_HP;
+ fake_bridge_dev.device = PCI_DEVICE_ID_HP_PCIX_LBA;
+ bridge->dev = &fake_bridge_dev;
+
+ error = agp_add_bridge(bridge);
+ fail:
+ if (error)
+ hp_zx1_cleanup();
+ return error;
+}
+
+static acpi_status __init
+zx1_gart_probe (acpi_handle obj, u32 depth, void *context, void **ret)
+{
+ acpi_handle handle, parent;
+ acpi_status status;
+ struct acpi_buffer buffer;
+ struct acpi_device_info *info;
+ u64 lba_hpa, sba_hpa, length;
+ int match;
+
+ status = hp_acpi_csr_space(obj, &lba_hpa, &length);
+ if (ACPI_FAILURE(status))
+ return AE_OK; /* keep looking for another bridge */
+
+ /* Look for an enclosing IOC scope and find its CSR space */
+ handle = obj;
+ do {
+ buffer.length = ACPI_ALLOCATE_LOCAL_BUFFER;
+ status = acpi_get_object_info(handle, &buffer);
+ if (ACPI_SUCCESS(status)) {
+ /* TBD check _CID also */
+ info = buffer.pointer;
+ info->hardware_id.value[sizeof(info->hardware_id)-1] = '\0';
+ match = (strcmp(info->hardware_id.value, "HWP0001") == 0);
+ ACPI_MEM_FREE(info);
+ if (match) {
+ status = hp_acpi_csr_space(handle, &sba_hpa, &length);
+ if (ACPI_SUCCESS(status))
+ break;
+ else {
+ printk(KERN_ERR PFX "Detected HP ZX1 "
+ "AGP LBA but no IOC.\n");
+ return AE_OK;
+ }
+ }
+ }
+
+ status = acpi_get_parent(handle, &parent);
+ handle = parent;
+ } while (ACPI_SUCCESS(status));
+
+ if (hp_zx1_setup(sba_hpa + HP_ZX1_IOC_OFFSET, lba_hpa))
+ return AE_OK;
+
+ printk(KERN_INFO PFX "Detected HP ZX1 %s AGP chipset (ioc=%lx, lba=%lx)\n",
+ (char *) context, sba_hpa + HP_ZX1_IOC_OFFSET, lba_hpa);
+
+ hp_zx1_gart_found = 1;
+ return AE_CTRL_TERMINATE; /* we only support one bridge; quit looking */
+}
+
+static int __init
+agp_hp_init (void)
+{
+ if (agp_off)
+ return -EINVAL;
+
+ acpi_get_devices("HWP0003", zx1_gart_probe, "HWP0003", NULL);
+ if (hp_zx1_gart_found)
+ return 0;
+
+ acpi_get_devices("HWP0007", zx1_gart_probe, "HWP0007", NULL);
+ if (hp_zx1_gart_found)
+ return 0;
+
+ return -ENODEV;
+}
+
+static void __exit
+agp_hp_cleanup (void)
+{
+}
+
+module_init(agp_hp_init);
+module_exit(agp_hp_cleanup);
+
+MODULE_LICENSE("GPL and additional rights");
diff --git a/drivers/char/agp/i460-agp.c b/drivers/char/agp/i460-agp.c
new file mode 100644
index 00000000000..adbea896c0d
--- /dev/null
+++ b/drivers/char/agp/i460-agp.c
@@ -0,0 +1,642 @@
+/*
+ * For documentation on the i460 AGP interface, see Chapter 7 (AGP Subsystem) of
+ * the "Intel 460GTX Chipset Software Developer's Manual":
+ * http://developer.intel.com/design/itanium/downloads/24870401s.htm
+ */
+/*
+ * 460GX support by Chris Ahna <christopher.j.ahna@intel.com>
+ * Clean up & simplification by David Mosberger-Tang <davidm@hpl.hp.com>
+ */
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/agp_backend.h>
+
+#include "agp.h"
+
+#define INTEL_I460_BAPBASE 0x98
+#define INTEL_I460_GXBCTL 0xa0
+#define INTEL_I460_AGPSIZ 0xa2
+#define INTEL_I460_ATTBASE 0xfe200000
+#define INTEL_I460_GATT_VALID (1UL << 24)
+#define INTEL_I460_GATT_COHERENT (1UL << 25)
+
+/*
+ * The i460 can operate with large (4MB) pages, but there is no sane way to support this
+ * within the current kernel/DRM environment, so we disable the relevant code for now.
+ * See also comments in ia64_alloc_page()...
+ */
+#define I460_LARGE_IO_PAGES 0
+
+#if I460_LARGE_IO_PAGES
+# define I460_IO_PAGE_SHIFT i460.io_page_shift
+#else
+# define I460_IO_PAGE_SHIFT 12
+#endif
+
+#define I460_IOPAGES_PER_KPAGE (PAGE_SIZE >> I460_IO_PAGE_SHIFT)
+#define I460_KPAGES_PER_IOPAGE (1 << (I460_IO_PAGE_SHIFT - PAGE_SHIFT))
+#define I460_SRAM_IO_DISABLE (1 << 4)
+#define I460_BAPBASE_ENABLE (1 << 3)
+#define I460_AGPSIZ_MASK 0x7
+#define I460_4M_PS (1 << 1)
+
+/* Control bits for Out-Of-GART coherency and Burst Write Combining */
+#define I460_GXBCTL_OOG (1UL << 0)
+#define I460_GXBCTL_BWC (1UL << 2)
+
+/*
+ * gatt_table entries are 32-bits wide on the i460; the generic code ought to declare the
+ * gatt_table and gatt_table_real pointers a "void *"...
+ */
+#define RD_GATT(index) readl((u32 *) i460.gatt + (index))
+#define WR_GATT(index, val) writel((val), (u32 *) i460.gatt + (index))
+/*
+ * The 460 spec says we have to read the last location written to make sure that all
+ * writes have taken effect
+ */
+#define WR_FLUSH_GATT(index) RD_GATT(index)
+
+#define log2(x) ffz(~(x))
+
+static struct {
+ void *gatt; /* ioremap'd GATT area */
+
+ /* i460 supports multiple GART page sizes, so GART pageshift is dynamic: */
+ u8 io_page_shift;
+
+ /* BIOS configures chipset to one of 2 possible apbase values: */
+ u8 dynamic_apbase;
+
+ /* structure for tracking partial use of 4MB GART pages: */
+ struct lp_desc {
+ unsigned long *alloced_map; /* bitmap of kernel-pages in use */
+ int refcount; /* number of kernel pages using the large page */
+ u64 paddr; /* physical address of large page */
+ } *lp_desc;
+} i460;
+
+static struct aper_size_info_8 i460_sizes[3] =
+{
+ /*
+ * The 32GB aperture is only available with a 4M GART page size. Due to the
+ * dynamic GART page size, we can't figure out page_order or num_entries until
+ * runtime.
+ */
+ {32768, 0, 0, 4},
+ {1024, 0, 0, 2},
+ {256, 0, 0, 1}
+};
+
+static struct gatt_mask i460_masks[] =
+{
+ {
+ .mask = INTEL_I460_GATT_VALID | INTEL_I460_GATT_COHERENT,
+ .type = 0
+ }
+};
+
+static int i460_fetch_size (void)
+{
+ int i;
+ u8 temp;
+ struct aper_size_info_8 *values;
+
+ /* Determine the GART page size */
+ pci_read_config_byte(agp_bridge->dev, INTEL_I460_GXBCTL, &temp);
+ i460.io_page_shift = (temp & I460_4M_PS) ? 22 : 12;
+ pr_debug("i460_fetch_size: io_page_shift=%d\n", i460.io_page_shift);
+
+ if (i460.io_page_shift != I460_IO_PAGE_SHIFT) {
+ printk(KERN_ERR PFX
+ "I/O (GART) page-size %ZuKB doesn't match expected size %ZuKB\n",
+ 1UL << (i460.io_page_shift - 10), 1UL << (I460_IO_PAGE_SHIFT));
+ return 0;
+ }
+
+ values = A_SIZE_8(agp_bridge->driver->aperture_sizes);
+
+ pci_read_config_byte(agp_bridge->dev, INTEL_I460_AGPSIZ, &temp);
+
+ /* Exit now if the IO drivers for the GART SRAMS are turned off */
+ if (temp & I460_SRAM_IO_DISABLE) {
+ printk(KERN_ERR PFX "GART SRAMS disabled on 460GX chipset\n");
+ printk(KERN_ERR PFX "AGPGART operation not possible\n");
+ return 0;
+ }
+
+ /* Make sure we don't try to create an 2 ^ 23 entry GATT */
+ if ((i460.io_page_shift == 0) && ((temp & I460_AGPSIZ_MASK) == 4)) {
+ printk(KERN_ERR PFX "We can't have a 32GB aperture with 4KB GART pages\n");
+ return 0;
+ }
+
+ /* Determine the proper APBASE register */
+ if (temp & I460_BAPBASE_ENABLE)
+ i460.dynamic_apbase = INTEL_I460_BAPBASE;
+ else
+ i460.dynamic_apbase = AGP_APBASE;
+
+ for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++) {
+ /*
+ * Dynamically calculate the proper num_entries and page_order values for
+ * the define aperture sizes. Take care not to shift off the end of
+ * values[i].size.
+ */
+ values[i].num_entries = (values[i].size << 8) >> (I460_IO_PAGE_SHIFT - 12);
+ values[i].page_order = log2((sizeof(u32)*values[i].num_entries) >> PAGE_SHIFT);
+ }
+
+ for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++) {
+ /* Neglect control bits when matching up size_value */
+ if ((temp & I460_AGPSIZ_MASK) == values[i].size_value) {
+ agp_bridge->previous_size = agp_bridge->current_size = (void *) (values + i);
+ agp_bridge->aperture_size_idx = i;
+ return values[i].size;
+ }
+ }
+
+ return 0;
+}
+
+/* There isn't anything to do here since 460 has no GART TLB. */
+static void i460_tlb_flush (struct agp_memory *mem)
+{
+ return;
+}
+
+/*
+ * This utility function is needed to prevent corruption of the control bits
+ * which are stored along with the aperture size in 460's AGPSIZ register
+ */
+static void i460_write_agpsiz (u8 size_value)
+{
+ u8 temp;
+
+ pci_read_config_byte(agp_bridge->dev, INTEL_I460_AGPSIZ, &temp);
+ pci_write_config_byte(agp_bridge->dev, INTEL_I460_AGPSIZ,
+ ((temp & ~I460_AGPSIZ_MASK) | size_value));
+}
+
+static void i460_cleanup (void)
+{
+ struct aper_size_info_8 *previous_size;
+
+ previous_size = A_SIZE_8(agp_bridge->previous_size);
+ i460_write_agpsiz(previous_size->size_value);
+
+ if (I460_IO_PAGE_SHIFT > PAGE_SHIFT)
+ kfree(i460.lp_desc);
+}
+
+static int i460_configure (void)
+{
+ union {
+ u32 small[2];
+ u64 large;
+ } temp;
+ size_t size;
+ u8 scratch;
+ struct aper_size_info_8 *current_size;
+
+ temp.large = 0;
+
+ current_size = A_SIZE_8(agp_bridge->current_size);
+ i460_write_agpsiz(current_size->size_value);
+
+ /*
+ * Do the necessary rigmarole to read all eight bytes of APBASE.
+ * This has to be done since the AGP aperture can be above 4GB on
+ * 460 based systems.
+ */
+ pci_read_config_dword(agp_bridge->dev, i460.dynamic_apbase, &(temp.small[0]));
+ pci_read_config_dword(agp_bridge->dev, i460.dynamic_apbase + 4, &(temp.small[1]));
+
+ /* Clear BAR control bits */
+ agp_bridge->gart_bus_addr = temp.large & ~((1UL << 3) - 1);
+
+ pci_read_config_byte(agp_bridge->dev, INTEL_I460_GXBCTL, &scratch);
+ pci_write_config_byte(agp_bridge->dev, INTEL_I460_GXBCTL,
+ (scratch & 0x02) | I460_GXBCTL_OOG | I460_GXBCTL_BWC);
+
+ /*
+ * Initialize partial allocation trackers if a GART page is bigger than a kernel
+ * page.
+ */
+ if (I460_IO_PAGE_SHIFT > PAGE_SHIFT) {
+ size = current_size->num_entries * sizeof(i460.lp_desc[0]);
+ i460.lp_desc = kmalloc(size, GFP_KERNEL);
+ if (!i460.lp_desc)
+ return -ENOMEM;
+ memset(i460.lp_desc, 0, size);
+ }
+ return 0;
+}
+
+static int i460_create_gatt_table (struct agp_bridge_data *bridge)
+{
+ int page_order, num_entries, i;
+ void *temp;
+
+ /*
+ * Load up the fixed address of the GART SRAMS which hold our GATT table.
+ */
+ temp = agp_bridge->current_size;
+ page_order = A_SIZE_8(temp)->page_order;
+ num_entries = A_SIZE_8(temp)->num_entries;
+
+ i460.gatt = ioremap(INTEL_I460_ATTBASE, PAGE_SIZE << page_order);
+
+ /* These are no good, the should be removed from the agp_bridge strucure... */
+ agp_bridge->gatt_table_real = NULL;
+ agp_bridge->gatt_table = NULL;
+ agp_bridge->gatt_bus_addr = 0;
+
+ for (i = 0; i < num_entries; ++i)
+ WR_GATT(i, 0);
+ WR_FLUSH_GATT(i - 1);
+ return 0;
+}
+
+static int i460_free_gatt_table (struct agp_bridge_data *bridge)
+{
+ int num_entries, i;
+ void *temp;
+
+ temp = agp_bridge->current_size;
+
+ num_entries = A_SIZE_8(temp)->num_entries;
+
+ for (i = 0; i < num_entries; ++i)
+ WR_GATT(i, 0);
+ WR_FLUSH_GATT(num_entries - 1);
+
+ iounmap(i460.gatt);
+ return 0;
+}
+
+/*
+ * The following functions are called when the I/O (GART) page size is smaller than
+ * PAGE_SIZE.
+ */
+
+static int i460_insert_memory_small_io_page (struct agp_memory *mem,
+ off_t pg_start, int type)
+{
+ unsigned long paddr, io_pg_start, io_page_size;
+ int i, j, k, num_entries;
+ void *temp;
+
+ pr_debug("i460_insert_memory_small_io_page(mem=%p, pg_start=%ld, type=%d, paddr0=0x%lx)\n",
+ mem, pg_start, type, mem->memory[0]);
+
+ io_pg_start = I460_IOPAGES_PER_KPAGE * pg_start;
+
+ temp = agp_bridge->current_size;
+ num_entries = A_SIZE_8(temp)->num_entries;
+
+ if ((io_pg_start + I460_IOPAGES_PER_KPAGE * mem->page_count) > num_entries) {
+ printk(KERN_ERR PFX "Looks like we're out of AGP memory\n");
+ return -EINVAL;
+ }
+
+ j = io_pg_start;
+ while (j < (io_pg_start + I460_IOPAGES_PER_KPAGE * mem->page_count)) {
+ if (!PGE_EMPTY(agp_bridge, RD_GATT(j))) {
+ pr_debug("i460_insert_memory_small_io_page: GATT[%d]=0x%x is busy\n",
+ j, RD_GATT(j));
+ return -EBUSY;
+ }
+ j++;
+ }
+
+ io_page_size = 1UL << I460_IO_PAGE_SHIFT;
+ for (i = 0, j = io_pg_start; i < mem->page_count; i++) {
+ paddr = mem->memory[i];
+ for (k = 0; k < I460_IOPAGES_PER_KPAGE; k++, j++, paddr += io_page_size)
+ WR_GATT(j, agp_bridge->driver->mask_memory(agp_bridge,
+ paddr, mem->type));
+ }
+ WR_FLUSH_GATT(j - 1);
+ return 0;
+}
+
+static int i460_remove_memory_small_io_page(struct agp_memory *mem,
+ off_t pg_start, int type)
+{
+ int i;
+
+ pr_debug("i460_remove_memory_small_io_page(mem=%p, pg_start=%ld, type=%d)\n",
+ mem, pg_start, type);
+
+ pg_start = I460_IOPAGES_PER_KPAGE * pg_start;
+
+ for (i = pg_start; i < (pg_start + I460_IOPAGES_PER_KPAGE * mem->page_count); i++)
+ WR_GATT(i, 0);
+ WR_FLUSH_GATT(i - 1);
+ return 0;
+}
+
+#if I460_LARGE_IO_PAGES
+
+/*
+ * These functions are called when the I/O (GART) page size exceeds PAGE_SIZE.
+ *
+ * This situation is interesting since AGP memory allocations that are smaller than a
+ * single GART page are possible. The i460.lp_desc array tracks partial allocation of the
+ * large GART pages to work around this issue.
+ *
+ * i460.lp_desc[pg_num].refcount tracks the number of kernel pages in use within GART page
+ * pg_num. i460.lp_desc[pg_num].paddr is the physical address of the large page and
+ * i460.lp_desc[pg_num].alloced_map is a bitmap of kernel pages that are in use (allocated).
+ */
+
+static int i460_alloc_large_page (struct lp_desc *lp)
+{
+ unsigned long order = I460_IO_PAGE_SHIFT - PAGE_SHIFT;
+ size_t map_size;
+ void *lpage;
+
+ lpage = (void *) __get_free_pages(GFP_KERNEL, order);
+ if (!lpage) {
+ printk(KERN_ERR PFX "Couldn't alloc 4M GART page...\n");
+ return -ENOMEM;
+ }
+
+ map_size = ((I460_KPAGES_PER_IOPAGE + BITS_PER_LONG - 1) & -BITS_PER_LONG)/8;
+ lp->alloced_map = kmalloc(map_size, GFP_KERNEL);
+ if (!lp->alloced_map) {
+ free_pages((unsigned long) lpage, order);
+ printk(KERN_ERR PFX "Out of memory, we're in trouble...\n");
+ return -ENOMEM;
+ }
+ memset(lp->alloced_map, 0, map_size);
+
+ lp->paddr = virt_to_phys(lpage);
+ lp->refcount = 0;
+ atomic_add(I460_KPAGES_PER_IOPAGE, &agp_bridge->current_memory_agp);
+ return 0;
+}
+
+static void i460_free_large_page (struct lp_desc *lp)
+{
+ kfree(lp->alloced_map);
+ lp->alloced_map = NULL;
+
+ free_pages((unsigned long) phys_to_virt(lp->paddr), I460_IO_PAGE_SHIFT - PAGE_SHIFT);
+ atomic_sub(I460_KPAGES_PER_IOPAGE, &agp_bridge->current_memory_agp);
+}
+
+static int i460_insert_memory_large_io_page (struct agp_memory *mem,
+ off_t pg_start, int type)
+{
+ int i, start_offset, end_offset, idx, pg, num_entries;
+ struct lp_desc *start, *end, *lp;
+ void *temp;
+
+ temp = agp_bridge->current_size;
+ num_entries = A_SIZE_8(temp)->num_entries;
+
+ /* Figure out what pg_start means in terms of our large GART pages */
+ start = &i460.lp_desc[pg_start / I460_KPAGES_PER_IOPAGE];
+ end = &i460.lp_desc[(pg_start + mem->page_count - 1) / I460_KPAGES_PER_IOPAGE];
+ start_offset = pg_start % I460_KPAGES_PER_IOPAGE;
+ end_offset = (pg_start + mem->page_count - 1) % I460_KPAGES_PER_IOPAGE;
+
+ if (end > i460.lp_desc + num_entries) {
+ printk(KERN_ERR PFX "Looks like we're out of AGP memory\n");
+ return -EINVAL;
+ }
+
+ /* Check if the requested region of the aperture is free */
+ for (lp = start; lp <= end; ++lp) {
+ if (!lp->alloced_map)
+ continue; /* OK, the entire large page is available... */
+
+ for (idx = ((lp == start) ? start_offset : 0);
+ idx < ((lp == end) ? (end_offset + 1) : I460_KPAGES_PER_IOPAGE);
+ idx++)
+ {
+ if (test_bit(idx, lp->alloced_map))
+ return -EBUSY;
+ }
+ }
+
+ for (lp = start, i = 0; lp <= end; ++lp) {
+ if (!lp->alloced_map) {
+ /* Allocate new GART pages... */
+ if (i460_alloc_large_page(lp) < 0)
+ return -ENOMEM;
+ pg = lp - i460.lp_desc;
+ WR_GATT(pg, agp_bridge->driver->mask_memory(agp_bridge,
+ lp->paddr, 0));
+ WR_FLUSH_GATT(pg);
+ }
+
+ for (idx = ((lp == start) ? start_offset : 0);
+ idx < ((lp == end) ? (end_offset + 1) : I460_KPAGES_PER_IOPAGE);
+ idx++, i++)
+ {
+ mem->memory[i] = lp->paddr + idx*PAGE_SIZE;
+ __set_bit(idx, lp->alloced_map);
+ ++lp->refcount;
+ }
+ }
+ return 0;
+}
+
+static int i460_remove_memory_large_io_page (struct agp_memory *mem,
+ off_t pg_start, int type)
+{
+ int i, pg, start_offset, end_offset, idx, num_entries;
+ struct lp_desc *start, *end, *lp;
+ void *temp;
+
+ temp = agp_bridge->driver->current_size;
+ num_entries = A_SIZE_8(temp)->num_entries;
+
+ /* Figure out what pg_start means in terms of our large GART pages */
+ start = &i460.lp_desc[pg_start / I460_KPAGES_PER_IOPAGE];
+ end = &i460.lp_desc[(pg_start + mem->page_count - 1) / I460_KPAGES_PER_IOPAGE];
+ start_offset = pg_start % I460_KPAGES_PER_IOPAGE;
+ end_offset = (pg_start + mem->page_count - 1) % I460_KPAGES_PER_IOPAGE;
+
+ for (i = 0, lp = start; lp <= end; ++lp) {
+ for (idx = ((lp == start) ? start_offset : 0);
+ idx < ((lp == end) ? (end_offset + 1) : I460_KPAGES_PER_IOPAGE);
+ idx++, i++)
+ {
+ mem->memory[i] = 0;
+ __clear_bit(idx, lp->alloced_map);
+ --lp->refcount;
+ }
+
+ /* Free GART pages if they are unused */
+ if (lp->refcount == 0) {
+ pg = lp - i460.lp_desc;
+ WR_GATT(pg, 0);
+ WR_FLUSH_GATT(pg);
+ i460_free_large_page(lp);
+ }
+ }
+ return 0;
+}
+
+/* Wrapper routines to call the approriate {small_io_page,large_io_page} function */
+
+static int i460_insert_memory (struct agp_memory *mem,
+ off_t pg_start, int type)
+{
+ if (I460_IO_PAGE_SHIFT <= PAGE_SHIFT)
+ return i460_insert_memory_small_io_page(mem, pg_start, type);
+ else
+ return i460_insert_memory_large_io_page(mem, pg_start, type);
+}
+
+static int i460_remove_memory (struct agp_memory *mem,
+ off_t pg_start, int type)
+{
+ if (I460_IO_PAGE_SHIFT <= PAGE_SHIFT)
+ return i460_remove_memory_small_io_page(mem, pg_start, type);
+ else
+ return i460_remove_memory_large_io_page(mem, pg_start, type);
+}
+
+/*
+ * If the I/O (GART) page size is bigger than the kernel page size, we don't want to
+ * allocate memory until we know where it is to be bound in the aperture (a
+ * multi-kernel-page alloc might fit inside of an already allocated GART page).
+ *
+ * Let's just hope nobody counts on the allocated AGP memory being there before bind time
+ * (I don't think current drivers do)...
+ */
+static void *i460_alloc_page (struct agp_bridge_data *bridge)
+{
+ void *page;
+
+ if (I460_IO_PAGE_SHIFT <= PAGE_SHIFT)
+ page = agp_generic_alloc_page(agp_bridge);
+ else
+ /* Returning NULL would cause problems */
+ /* AK: really dubious code. */
+ page = (void *)~0UL;
+ return page;
+}
+
+static void i460_destroy_page (void *page)
+{
+ if (I460_IO_PAGE_SHIFT <= PAGE_SHIFT)
+ agp_generic_destroy_page(page);
+}
+
+#endif /* I460_LARGE_IO_PAGES */
+
+static unsigned long i460_mask_memory (struct agp_bridge_data *bridge,
+ unsigned long addr, int type)
+{
+ /* Make sure the returned address is a valid GATT entry */
+ return bridge->driver->masks[0].mask
+ | (((addr & ~((1 << I460_IO_PAGE_SHIFT) - 1)) & 0xffffff000) >> 12);
+}
+
+struct agp_bridge_driver intel_i460_driver = {
+ .owner = THIS_MODULE,
+ .aperture_sizes = i460_sizes,
+ .size_type = U8_APER_SIZE,
+ .num_aperture_sizes = 3,
+ .configure = i460_configure,
+ .fetch_size = i460_fetch_size,
+ .cleanup = i460_cleanup,
+ .tlb_flush = i460_tlb_flush,
+ .mask_memory = i460_mask_memory,
+ .masks = i460_masks,
+ .agp_enable = agp_generic_enable,
+ .cache_flush = global_cache_flush,
+ .create_gatt_table = i460_create_gatt_table,
+ .free_gatt_table = i460_free_gatt_table,
+#if I460_LARGE_IO_PAGES
+ .insert_memory = i460_insert_memory,
+ .remove_memory = i460_remove_memory,
+ .agp_alloc_page = i460_alloc_page,
+ .agp_destroy_page = i460_destroy_page,
+#else
+ .insert_memory = i460_insert_memory_small_io_page,
+ .remove_memory = i460_remove_memory_small_io_page,
+ .agp_alloc_page = agp_generic_alloc_page,
+ .agp_destroy_page = agp_generic_destroy_page,
+#endif
+ .alloc_by_type = agp_generic_alloc_by_type,
+ .free_by_type = agp_generic_free_by_type,
+ .cant_use_aperture = 1,
+};
+
+static int __devinit agp_intel_i460_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct agp_bridge_data *bridge;
+ u8 cap_ptr;
+
+ cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP);
+ if (!cap_ptr)
+ return -ENODEV;
+
+ bridge = agp_alloc_bridge();
+ if (!bridge)
+ return -ENOMEM;
+
+ bridge->driver = &intel_i460_driver;
+ bridge->dev = pdev;
+ bridge->capndx = cap_ptr;
+
+ printk(KERN_INFO PFX "Detected Intel 460GX chipset\n");
+
+ pci_set_drvdata(pdev, bridge);
+ return agp_add_bridge(bridge);
+}
+
+static void __devexit agp_intel_i460_remove(struct pci_dev *pdev)
+{
+ struct agp_bridge_data *bridge = pci_get_drvdata(pdev);
+
+ agp_remove_bridge(bridge);
+ agp_put_bridge(bridge);
+}
+
+static struct pci_device_id agp_intel_i460_pci_table[] = {
+ {
+ .class = (PCI_CLASS_BRIDGE_HOST << 8),
+ .class_mask = ~0,
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .device = PCI_DEVICE_ID_INTEL_84460GX,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ },
+ { }
+};
+
+MODULE_DEVICE_TABLE(pci, agp_intel_i460_pci_table);
+
+static struct pci_driver agp_intel_i460_pci_driver = {
+ .name = "agpgart-intel-i460",
+ .id_table = agp_intel_i460_pci_table,
+ .probe = agp_intel_i460_probe,
+ .remove = __devexit_p(agp_intel_i460_remove),
+};
+
+static int __init agp_intel_i460_init(void)
+{
+ if (agp_off)
+ return -EINVAL;
+ return pci_register_driver(&agp_intel_i460_pci_driver);
+}
+
+static void __exit agp_intel_i460_cleanup(void)
+{
+ pci_unregister_driver(&agp_intel_i460_pci_driver);
+}
+
+module_init(agp_intel_i460_init);
+module_exit(agp_intel_i460_cleanup);
+
+MODULE_AUTHOR("Chris Ahna <Christopher.J.Ahna@intel.com>");
+MODULE_LICENSE("GPL and additional rights");
diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c
new file mode 100644
index 00000000000..8c7d727432b
--- /dev/null
+++ b/drivers/char/agp/intel-agp.c
@@ -0,0 +1,1833 @@
+/*
+ * Intel AGPGART routines.
+ */
+
+/*
+ * Intel(R) 855GM/852GM and 865G support added by David Dawes
+ * <dawes@tungstengraphics.com>.
+ *
+ * Intel(R) 915G/915GM support added by Alan Hourihane
+ * <alanh@tungstengraphics.com>.
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/pagemap.h>
+#include <linux/agp_backend.h>
+#include "agp.h"
+
+/* Intel 815 register */
+#define INTEL_815_APCONT 0x51
+#define INTEL_815_ATTBASE_MASK ~0x1FFFFFFF
+
+/* Intel i820 registers */
+#define INTEL_I820_RDCR 0x51
+#define INTEL_I820_ERRSTS 0xc8
+
+/* Intel i840 registers */
+#define INTEL_I840_MCHCFG 0x50
+#define INTEL_I840_ERRSTS 0xc8
+
+/* Intel i850 registers */
+#define INTEL_I850_MCHCFG 0x50
+#define INTEL_I850_ERRSTS 0xc8
+
+/* intel 915G registers */
+#define I915_GMADDR 0x18
+#define I915_MMADDR 0x10
+#define I915_PTEADDR 0x1C
+#define I915_GMCH_GMS_STOLEN_48M (0x6 << 4)
+#define I915_GMCH_GMS_STOLEN_64M (0x7 << 4)
+
+
+/* Intel 7505 registers */
+#define INTEL_I7505_APSIZE 0x74
+#define INTEL_I7505_NCAPID 0x60
+#define INTEL_I7505_NISTAT 0x6c
+#define INTEL_I7505_ATTBASE 0x78
+#define INTEL_I7505_ERRSTS 0x42
+#define INTEL_I7505_AGPCTRL 0x70
+#define INTEL_I7505_MCHCFG 0x50
+
+static struct aper_size_info_fixed intel_i810_sizes[] =
+{
+ {64, 16384, 4},
+ /* The 32M mode still requires a 64k gatt */
+ {32, 8192, 4}
+};
+
+#define AGP_DCACHE_MEMORY 1
+#define AGP_PHYS_MEMORY 2
+
+static struct gatt_mask intel_i810_masks[] =
+{
+ {.mask = I810_PTE_VALID, .type = 0},
+ {.mask = (I810_PTE_VALID | I810_PTE_LOCAL), .type = AGP_DCACHE_MEMORY},
+ {.mask = I810_PTE_VALID, .type = 0}
+};
+
+static struct _intel_i810_private {
+ struct pci_dev *i810_dev; /* device one */
+ volatile u8 __iomem *registers;
+ int num_dcache_entries;
+} intel_i810_private;
+
+static int intel_i810_fetch_size(void)
+{
+ u32 smram_miscc;
+ struct aper_size_info_fixed *values;
+
+ pci_read_config_dword(agp_bridge->dev, I810_SMRAM_MISCC, &smram_miscc);
+ values = A_SIZE_FIX(agp_bridge->driver->aperture_sizes);
+
+ if ((smram_miscc & I810_GMS) == I810_GMS_DISABLE) {
+ printk(KERN_WARNING PFX "i810 is disabled\n");
+ return 0;
+ }
+ if ((smram_miscc & I810_GFX_MEM_WIN_SIZE) == I810_GFX_MEM_WIN_32M) {
+ agp_bridge->previous_size =
+ agp_bridge->current_size = (void *) (values + 1);
+ agp_bridge->aperture_size_idx = 1;
+ return values[1].size;
+ } else {
+ agp_bridge->previous_size =
+ agp_bridge->current_size = (void *) (values);
+ agp_bridge->aperture_size_idx = 0;
+ return values[0].size;
+ }
+
+ return 0;
+}
+
+static int intel_i810_configure(void)
+{
+ struct aper_size_info_fixed *current_size;
+ u32 temp;
+ int i;
+
+ current_size = A_SIZE_FIX(agp_bridge->current_size);
+
+ pci_read_config_dword(intel_i810_private.i810_dev, I810_MMADDR, &temp);
+ temp &= 0xfff80000;
+
+ intel_i810_private.registers = ioremap(temp, 128 * 4096);
+ if (!intel_i810_private.registers) {
+ printk(KERN_ERR PFX "Unable to remap memory.\n");
+ return -ENOMEM;
+ }
+
+ if ((readl(intel_i810_private.registers+I810_DRAM_CTL)
+ & I810_DRAM_ROW_0) == I810_DRAM_ROW_0_SDRAM) {
+ /* This will need to be dynamically assigned */
+ printk(KERN_INFO PFX "detected 4MB dedicated video ram.\n");
+ intel_i810_private.num_dcache_entries = 1024;
+ }
+ pci_read_config_dword(intel_i810_private.i810_dev, I810_GMADDR, &temp);
+ agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+ writel(agp_bridge->gatt_bus_addr | I810_PGETBL_ENABLED, intel_i810_private.registers+I810_PGETBL_CTL);
+ readl(intel_i810_private.registers+I810_PGETBL_CTL); /* PCI Posting. */
+
+ if (agp_bridge->driver->needs_scratch_page) {
+ for (i = 0; i < current_size->num_entries; i++) {
+ writel(agp_bridge->scratch_page, intel_i810_private.registers+I810_PTE_BASE+(i*4));
+ readl(intel_i810_private.registers+I810_PTE_BASE+(i*4)); /* PCI posting. */
+ }
+ }
+ global_cache_flush();
+ return 0;
+}
+
+static void intel_i810_cleanup(void)
+{
+ writel(0, intel_i810_private.registers+I810_PGETBL_CTL);
+ readl(intel_i810_private.registers); /* PCI Posting. */
+ iounmap(intel_i810_private.registers);
+}
+
+static void intel_i810_tlbflush(struct agp_memory *mem)
+{
+ return;
+}
+
+static void intel_i810_agp_enable(struct agp_bridge_data *bridge, u32 mode)
+{
+ return;
+}
+
+/* Exists to support ARGB cursors */
+static void *i8xx_alloc_pages(void)
+{
+ struct page * page;
+
+ page = alloc_pages(GFP_KERNEL, 2);
+ if (page == NULL)
+ return NULL;
+
+ if (change_page_attr(page, 4, PAGE_KERNEL_NOCACHE) < 0) {
+ global_flush_tlb();
+ __free_page(page);
+ return NULL;
+ }
+ global_flush_tlb();
+ get_page(page);
+ SetPageLocked(page);
+ atomic_inc(&agp_bridge->current_memory_agp);
+ return page_address(page);
+}
+
+static void i8xx_destroy_pages(void *addr)
+{
+ struct page *page;
+
+ if (addr == NULL)
+ return;
+
+ page = virt_to_page(addr);
+ change_page_attr(page, 4, PAGE_KERNEL);
+ global_flush_tlb();
+ put_page(page);
+ unlock_page(page);
+ free_pages((unsigned long)addr, 2);
+ atomic_dec(&agp_bridge->current_memory_agp);
+}
+
+static int intel_i810_insert_entries(struct agp_memory *mem, off_t pg_start,
+ int type)
+{
+ int i, j, num_entries;
+ void *temp;
+
+ temp = agp_bridge->current_size;
+ num_entries = A_SIZE_FIX(temp)->num_entries;
+
+ if ((pg_start + mem->page_count) > num_entries) {
+ return -EINVAL;
+ }
+ for (j = pg_start; j < (pg_start + mem->page_count); j++) {
+ if (!PGE_EMPTY(agp_bridge, readl(agp_bridge->gatt_table+j)))
+ return -EBUSY;
+ }
+
+ if (type != 0 || mem->type != 0) {
+ if ((type == AGP_DCACHE_MEMORY) && (mem->type == AGP_DCACHE_MEMORY)) {
+ /* special insert */
+ global_cache_flush();
+ for (i = pg_start; i < (pg_start + mem->page_count); i++) {
+ writel((i*4096)|I810_PTE_LOCAL|I810_PTE_VALID, intel_i810_private.registers+I810_PTE_BASE+(i*4));
+ readl(intel_i810_private.registers+I810_PTE_BASE+(i*4)); /* PCI Posting. */
+ }
+ global_cache_flush();
+ agp_bridge->driver->tlb_flush(mem);
+ return 0;
+ }
+ if((type == AGP_PHYS_MEMORY) && (mem->type == AGP_PHYS_MEMORY))
+ goto insert;
+ return -EINVAL;
+ }
+
+insert:
+ global_cache_flush();
+ for (i = 0, j = pg_start; i < mem->page_count; i++, j++) {
+ writel(agp_bridge->driver->mask_memory(agp_bridge,
+ mem->memory[i], mem->type),
+ intel_i810_private.registers+I810_PTE_BASE+(j*4));
+ readl(intel_i810_private.registers+I810_PTE_BASE+(j*4)); /* PCI Posting. */
+ }
+ global_cache_flush();
+
+ agp_bridge->driver->tlb_flush(mem);
+ return 0;
+}
+
+static int intel_i810_remove_entries(struct agp_memory *mem, off_t pg_start,
+ int type)
+{
+ int i;
+
+ for (i = pg_start; i < (mem->page_count + pg_start); i++) {
+ writel(agp_bridge->scratch_page, intel_i810_private.registers+I810_PTE_BASE+(i*4));
+ readl(intel_i810_private.registers+I810_PTE_BASE+(i*4)); /* PCI Posting. */
+ }
+
+ global_cache_flush();
+ agp_bridge->driver->tlb_flush(mem);
+ return 0;
+}
+
+/*
+ * The i810/i830 requires a physical address to program its mouse
+ * pointer into hardware.
+ * However the Xserver still writes to it through the agp aperture.
+ */
+static struct agp_memory *alloc_agpphysmem_i8xx(size_t pg_count, int type)
+{
+ struct agp_memory *new;
+ void *addr;
+
+ if (pg_count != 1 && pg_count != 4)
+ return NULL;
+
+ switch (pg_count) {
+ case 1: addr = agp_bridge->driver->agp_alloc_page(agp_bridge);
+ break;
+ case 4:
+ /* kludge to get 4 physical pages for ARGB cursor */
+ addr = i8xx_alloc_pages();
+ break;
+ default:
+ return NULL;
+ }
+
+ if (addr == NULL)
+ return NULL;
+
+ new = agp_create_memory(pg_count);
+ if (new == NULL)
+ return NULL;
+
+ new->memory[0] = virt_to_phys(addr);
+ if (pg_count == 4) {
+ /* kludge to get 4 physical pages for ARGB cursor */
+ new->memory[1] = new->memory[0] + PAGE_SIZE;
+ new->memory[2] = new->memory[1] + PAGE_SIZE;
+ new->memory[3] = new->memory[2] + PAGE_SIZE;
+ }
+ new->page_count = pg_count;
+ new->num_scratch_pages = pg_count;
+ new->type = AGP_PHYS_MEMORY;
+ new->physical = new->memory[0];
+ return new;
+}
+
+static struct agp_memory *intel_i810_alloc_by_type(size_t pg_count, int type)
+{
+ struct agp_memory *new;
+
+ if (type == AGP_DCACHE_MEMORY) {
+ if (pg_count != intel_i810_private.num_dcache_entries)
+ return NULL;
+
+ new = agp_create_memory(1);
+ if (new == NULL)
+ return NULL;
+
+ new->type = AGP_DCACHE_MEMORY;
+ new->page_count = pg_count;
+ new->num_scratch_pages = 0;
+ vfree(new->memory);
+ return new;
+ }
+ if (type == AGP_PHYS_MEMORY)
+ return alloc_agpphysmem_i8xx(pg_count, type);
+
+ return NULL;
+}
+
+static void intel_i810_free_by_type(struct agp_memory *curr)
+{
+ agp_free_key(curr->key);
+ if(curr->type == AGP_PHYS_MEMORY) {
+ if (curr->page_count == 4)
+ i8xx_destroy_pages(phys_to_virt(curr->memory[0]));
+ else
+ agp_bridge->driver->agp_destroy_page(
+ phys_to_virt(curr->memory[0]));
+ vfree(curr->memory);
+ }
+ kfree(curr);
+}
+
+static unsigned long intel_i810_mask_memory(struct agp_bridge_data *bridge,
+ unsigned long addr, int type)
+{
+ /* Type checking must be done elsewhere */
+ return addr | bridge->driver->masks[type].mask;
+}
+
+static struct aper_size_info_fixed intel_i830_sizes[] =
+{
+ {128, 32768, 5},
+ /* The 64M mode still requires a 128k gatt */
+ {64, 16384, 5},
+ {256, 65536, 6},
+};
+
+static struct _intel_i830_private {
+ struct pci_dev *i830_dev; /* device one */
+ volatile u8 __iomem *registers;
+ volatile u32 __iomem *gtt; /* I915G */
+ int gtt_entries;
+} intel_i830_private;
+
+static void intel_i830_init_gtt_entries(void)
+{
+ u16 gmch_ctrl;
+ int gtt_entries;
+ u8 rdct;
+ int local = 0;
+ static const int ddt[4] = { 0, 16, 32, 64 };
+ int size;
+
+ pci_read_config_word(agp_bridge->dev,I830_GMCH_CTRL,&gmch_ctrl);
+
+ /* We obtain the size of the GTT, which is also stored (for some
+ * reason) at the top of stolen memory. Then we add 4KB to that
+ * for the video BIOS popup, which is also stored in there. */
+ size = agp_bridge->driver->fetch_size() + 4;
+
+ if (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82830_HB ||
+ agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82845G_HB) {
+ switch (gmch_ctrl & I830_GMCH_GMS_MASK) {
+ case I830_GMCH_GMS_STOLEN_512:
+ gtt_entries = KB(512) - KB(size);
+ break;
+ case I830_GMCH_GMS_STOLEN_1024:
+ gtt_entries = MB(1) - KB(size);
+ break;
+ case I830_GMCH_GMS_STOLEN_8192:
+ gtt_entries = MB(8) - KB(size);
+ break;
+ case I830_GMCH_GMS_LOCAL:
+ rdct = readb(intel_i830_private.registers+I830_RDRAM_CHANNEL_TYPE);
+ gtt_entries = (I830_RDRAM_ND(rdct) + 1) *
+ MB(ddt[I830_RDRAM_DDT(rdct)]);
+ local = 1;
+ break;
+ default:
+ gtt_entries = 0;
+ break;
+ }
+ } else {
+ switch (gmch_ctrl & I830_GMCH_GMS_MASK) {
+ case I855_GMCH_GMS_STOLEN_1M:
+ gtt_entries = MB(1) - KB(size);
+ break;
+ case I855_GMCH_GMS_STOLEN_4M:
+ gtt_entries = MB(4) - KB(size);
+ break;
+ case I855_GMCH_GMS_STOLEN_8M:
+ gtt_entries = MB(8) - KB(size);
+ break;
+ case I855_GMCH_GMS_STOLEN_16M:
+ gtt_entries = MB(16) - KB(size);
+ break;
+ case I855_GMCH_GMS_STOLEN_32M:
+ gtt_entries = MB(32) - KB(size);
+ break;
+ case I915_GMCH_GMS_STOLEN_48M:
+ /* Check it's really I915G */
+ if (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82915G_HB ||
+ agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82915GM_HB)
+ gtt_entries = MB(48) - KB(size);
+ else
+ gtt_entries = 0;
+ break;
+ case I915_GMCH_GMS_STOLEN_64M:
+ /* Check it's really I915G */
+ if (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82915G_HB ||
+ agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82915GM_HB)
+ gtt_entries = MB(64) - KB(size);
+ else
+ gtt_entries = 0;
+ default:
+ gtt_entries = 0;
+ break;
+ }
+ }
+ if (gtt_entries > 0)
+ printk(KERN_INFO PFX "Detected %dK %s memory.\n",
+ gtt_entries / KB(1), local ? "local" : "stolen");
+ else
+ printk(KERN_INFO PFX
+ "No pre-allocated video memory detected.\n");
+ gtt_entries /= KB(4);
+
+ intel_i830_private.gtt_entries = gtt_entries;
+}
+
+/* The intel i830 automatically initializes the agp aperture during POST.
+ * Use the memory already set aside for in the GTT.
+ */
+static int intel_i830_create_gatt_table(struct agp_bridge_data *bridge)
+{
+ int page_order;
+ struct aper_size_info_fixed *size;
+ int num_entries;
+ u32 temp;
+
+ size = agp_bridge->current_size;
+ page_order = size->page_order;
+ num_entries = size->num_entries;
+ agp_bridge->gatt_table_real = NULL;
+
+ pci_read_config_dword(intel_i830_private.i830_dev,I810_MMADDR,&temp);
+ temp &= 0xfff80000;
+
+ intel_i830_private.registers = ioremap(temp,128 * 4096);
+ if (!intel_i830_private.registers)
+ return -ENOMEM;
+
+ temp = readl(intel_i830_private.registers+I810_PGETBL_CTL) & 0xfffff000;
+ global_cache_flush(); /* FIXME: ?? */
+
+ /* we have to call this as early as possible after the MMIO base address is known */
+ intel_i830_init_gtt_entries();
+
+ agp_bridge->gatt_table = NULL;
+
+ agp_bridge->gatt_bus_addr = temp;
+
+ return 0;
+}
+
+/* Return the gatt table to a sane state. Use the top of stolen
+ * memory for the GTT.
+ */
+static int intel_i830_free_gatt_table(struct agp_bridge_data *bridge)
+{
+ return 0;
+}
+
+static int intel_i830_fetch_size(void)
+{
+ u16 gmch_ctrl;
+ struct aper_size_info_fixed *values;
+
+ values = A_SIZE_FIX(agp_bridge->driver->aperture_sizes);
+
+ if (agp_bridge->dev->device != PCI_DEVICE_ID_INTEL_82830_HB &&
+ agp_bridge->dev->device != PCI_DEVICE_ID_INTEL_82845G_HB) {
+ /* 855GM/852GM/865G has 128MB aperture size */
+ agp_bridge->previous_size = agp_bridge->current_size = (void *) values;
+ agp_bridge->aperture_size_idx = 0;
+ return values[0].size;
+ }
+
+ pci_read_config_word(agp_bridge->dev,I830_GMCH_CTRL,&gmch_ctrl);
+
+ if ((gmch_ctrl & I830_GMCH_MEM_MASK) == I830_GMCH_MEM_128M) {
+ agp_bridge->previous_size = agp_bridge->current_size = (void *) values;
+ agp_bridge->aperture_size_idx = 0;
+ return values[0].size;
+ } else {
+ agp_bridge->previous_size = agp_bridge->current_size = (void *) (values + 1);
+ agp_bridge->aperture_size_idx = 1;
+ return values[1].size;
+ }
+
+ return 0;
+}
+
+static int intel_i830_configure(void)
+{
+ struct aper_size_info_fixed *current_size;
+ u32 temp;
+ u16 gmch_ctrl;
+ int i;
+
+ current_size = A_SIZE_FIX(agp_bridge->current_size);
+
+ pci_read_config_dword(intel_i830_private.i830_dev,I810_GMADDR,&temp);
+ agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+
+ pci_read_config_word(agp_bridge->dev,I830_GMCH_CTRL,&gmch_ctrl);
+ gmch_ctrl |= I830_GMCH_ENABLED;
+ pci_write_config_word(agp_bridge->dev,I830_GMCH_CTRL,gmch_ctrl);
+
+ writel(agp_bridge->gatt_bus_addr|I810_PGETBL_ENABLED, intel_i830_private.registers+I810_PGETBL_CTL);
+ readl(intel_i830_private.registers+I810_PGETBL_CTL); /* PCI Posting. */
+
+ if (agp_bridge->driver->needs_scratch_page) {
+ for (i = intel_i830_private.gtt_entries; i < current_size->num_entries; i++) {
+ writel(agp_bridge->scratch_page, intel_i830_private.registers+I810_PTE_BASE+(i*4));
+ readl(intel_i830_private.registers+I810_PTE_BASE+(i*4)); /* PCI Posting. */
+ }
+ }
+
+ global_cache_flush();
+ return 0;
+}
+
+static void intel_i830_cleanup(void)
+{
+ iounmap(intel_i830_private.registers);
+}
+
+static int intel_i830_insert_entries(struct agp_memory *mem,off_t pg_start, int type)
+{
+ int i,j,num_entries;
+ void *temp;
+
+ temp = agp_bridge->current_size;
+ num_entries = A_SIZE_FIX(temp)->num_entries;
+
+ if (pg_start < intel_i830_private.gtt_entries) {
+ printk (KERN_DEBUG PFX "pg_start == 0x%.8lx,intel_i830_private.gtt_entries == 0x%.8x\n",
+ pg_start,intel_i830_private.gtt_entries);
+
+ printk (KERN_INFO PFX "Trying to insert into local/stolen memory\n");
+ return -EINVAL;
+ }
+
+ if ((pg_start + mem->page_count) > num_entries)
+ return -EINVAL;
+
+ /* The i830 can't check the GTT for entries since its read only,
+ * depend on the caller to make the correct offset decisions.
+ */
+
+ if ((type != 0 && type != AGP_PHYS_MEMORY) ||
+ (mem->type != 0 && mem->type != AGP_PHYS_MEMORY))
+ return -EINVAL;
+
+ global_cache_flush(); /* FIXME: Necessary ?*/
+
+ for (i = 0, j = pg_start; i < mem->page_count; i++, j++) {
+ writel(agp_bridge->driver->mask_memory(agp_bridge,
+ mem->memory[i], mem->type),
+ intel_i830_private.registers+I810_PTE_BASE+(j*4));
+ readl(intel_i830_private.registers+I810_PTE_BASE+(j*4)); /* PCI Posting. */
+ }
+
+ global_cache_flush();
+ agp_bridge->driver->tlb_flush(mem);
+ return 0;
+}
+
+static int intel_i830_remove_entries(struct agp_memory *mem,off_t pg_start,
+ int type)
+{
+ int i;
+
+ global_cache_flush();
+
+ if (pg_start < intel_i830_private.gtt_entries) {
+ printk (KERN_INFO PFX "Trying to disable local/stolen memory\n");
+ return -EINVAL;
+ }
+
+ for (i = pg_start; i < (mem->page_count + pg_start); i++) {
+ writel(agp_bridge->scratch_page, intel_i830_private.registers+I810_PTE_BASE+(i*4));
+ readl(intel_i830_private.registers+I810_PTE_BASE+(i*4)); /* PCI Posting. */
+ }
+
+ global_cache_flush();
+ agp_bridge->driver->tlb_flush(mem);
+ return 0;
+}
+
+static struct agp_memory *intel_i830_alloc_by_type(size_t pg_count,int type)
+{
+ if (type == AGP_PHYS_MEMORY)
+ return alloc_agpphysmem_i8xx(pg_count, type);
+
+ /* always return NULL for other allocation types for now */
+ return NULL;
+}
+
+static int intel_i915_configure(void)
+{
+ struct aper_size_info_fixed *current_size;
+ u32 temp;
+ u16 gmch_ctrl;
+ int i;
+
+ current_size = A_SIZE_FIX(agp_bridge->current_size);
+
+ pci_read_config_dword(intel_i830_private.i830_dev, I915_GMADDR, &temp);
+
+ agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+
+ pci_read_config_word(agp_bridge->dev,I830_GMCH_CTRL,&gmch_ctrl);
+ gmch_ctrl |= I830_GMCH_ENABLED;
+ pci_write_config_word(agp_bridge->dev,I830_GMCH_CTRL,gmch_ctrl);
+
+ writel(agp_bridge->gatt_bus_addr|I810_PGETBL_ENABLED, intel_i830_private.registers+I810_PGETBL_CTL);
+ readl(intel_i830_private.registers+I810_PGETBL_CTL); /* PCI Posting. */
+
+ if (agp_bridge->driver->needs_scratch_page) {
+ for (i = intel_i830_private.gtt_entries; i < current_size->num_entries; i++) {
+ writel(agp_bridge->scratch_page, intel_i830_private.gtt+i);
+ readl(intel_i830_private.gtt+i); /* PCI Posting. */
+ }
+ }
+
+ global_cache_flush();
+ return 0;
+}
+
+static void intel_i915_cleanup(void)
+{
+ iounmap(intel_i830_private.gtt);
+ iounmap(intel_i830_private.registers);
+}
+
+static int intel_i915_insert_entries(struct agp_memory *mem,off_t pg_start,
+ int type)
+{
+ int i,j,num_entries;
+ void *temp;
+
+ temp = agp_bridge->current_size;
+ num_entries = A_SIZE_FIX(temp)->num_entries;
+
+ if (pg_start < intel_i830_private.gtt_entries) {
+ printk (KERN_DEBUG PFX "pg_start == 0x%.8lx,intel_i830_private.gtt_entries == 0x%.8x\n",
+ pg_start,intel_i830_private.gtt_entries);
+
+ printk (KERN_INFO PFX "Trying to insert into local/stolen memory\n");
+ return -EINVAL;
+ }
+
+ if ((pg_start + mem->page_count) > num_entries)
+ return -EINVAL;
+
+ /* The i830 can't check the GTT for entries since its read only,
+ * depend on the caller to make the correct offset decisions.
+ */
+
+ if ((type != 0 && type != AGP_PHYS_MEMORY) ||
+ (mem->type != 0 && mem->type != AGP_PHYS_MEMORY))
+ return -EINVAL;
+
+ global_cache_flush();
+
+ for (i = 0, j = pg_start; i < mem->page_count; i++, j++) {
+ writel(agp_bridge->driver->mask_memory(agp_bridge,
+ mem->memory[i], mem->type), intel_i830_private.gtt+j);
+ readl(intel_i830_private.gtt+j); /* PCI Posting. */
+ }
+
+ global_cache_flush();
+ agp_bridge->driver->tlb_flush(mem);
+ return 0;
+}
+
+static int intel_i915_remove_entries(struct agp_memory *mem,off_t pg_start,
+ int type)
+{
+ int i;
+
+ global_cache_flush();
+
+ if (pg_start < intel_i830_private.gtt_entries) {
+ printk (KERN_INFO PFX "Trying to disable local/stolen memory\n");
+ return -EINVAL;
+ }
+
+ for (i = pg_start; i < (mem->page_count + pg_start); i++) {
+ writel(agp_bridge->scratch_page, intel_i830_private.gtt+i);
+ readl(intel_i830_private.gtt+i);
+ }
+
+ global_cache_flush();
+ agp_bridge->driver->tlb_flush(mem);
+ return 0;
+}
+
+static int intel_i915_fetch_size(void)
+{
+ struct aper_size_info_fixed *values;
+ u32 temp, offset = 0;
+
+#define I915_256MB_ADDRESS_MASK (1<<27)
+
+ values = A_SIZE_FIX(agp_bridge->driver->aperture_sizes);
+
+ pci_read_config_dword(intel_i830_private.i830_dev, I915_GMADDR, &temp);
+ if (temp & I915_256MB_ADDRESS_MASK)
+ offset = 0; /* 128MB aperture */
+ else
+ offset = 2; /* 256MB aperture */
+ agp_bridge->previous_size = agp_bridge->current_size = (void *)(values + offset);
+ return values[offset].size;
+}
+
+/* The intel i915 automatically initializes the agp aperture during POST.
+ * Use the memory already set aside for in the GTT.
+ */
+static int intel_i915_create_gatt_table(struct agp_bridge_data *bridge)
+{
+ int page_order;
+ struct aper_size_info_fixed *size;
+ int num_entries;
+ u32 temp, temp2;
+
+ size = agp_bridge->current_size;
+ page_order = size->page_order;
+ num_entries = size->num_entries;
+ agp_bridge->gatt_table_real = NULL;
+
+ pci_read_config_dword(intel_i830_private.i830_dev, I915_MMADDR, &temp);
+ pci_read_config_dword(intel_i830_private.i830_dev, I915_PTEADDR,&temp2);
+
+ intel_i830_private.gtt = ioremap(temp2, 256 * 1024);
+ if (!intel_i830_private.gtt)
+ return -ENOMEM;
+
+ temp &= 0xfff80000;
+
+ intel_i830_private.registers = ioremap(temp,128 * 4096);
+ if (!intel_i830_private.registers)
+ return -ENOMEM;
+
+ temp = readl(intel_i830_private.registers+I810_PGETBL_CTL) & 0xfffff000;
+ global_cache_flush(); /* FIXME: ? */
+
+ /* we have to call this as early as possible after the MMIO base address is known */
+ intel_i830_init_gtt_entries();
+
+ agp_bridge->gatt_table = NULL;
+
+ agp_bridge->gatt_bus_addr = temp;
+
+ return 0;
+}
+
+static int intel_fetch_size(void)
+{
+ int i;
+ u16 temp;
+ struct aper_size_info_16 *values;
+
+ pci_read_config_word(agp_bridge->dev, INTEL_APSIZE, &temp);
+ values = A_SIZE_16(agp_bridge->driver->aperture_sizes);
+
+ for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++) {
+ if (temp == values[i].size_value) {
+ agp_bridge->previous_size = agp_bridge->current_size = (void *) (values + i);
+ agp_bridge->aperture_size_idx = i;
+ return values[i].size;
+ }
+ }
+
+ return 0;
+}
+
+static int __intel_8xx_fetch_size(u8 temp)
+{
+ int i;
+ struct aper_size_info_8 *values;
+
+ values = A_SIZE_8(agp_bridge->driver->aperture_sizes);
+
+ for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++) {
+ if (temp == values[i].size_value) {
+ agp_bridge->previous_size =
+ agp_bridge->current_size = (void *) (values + i);
+ agp_bridge->aperture_size_idx = i;
+ return values[i].size;
+ }
+ }
+ return 0;
+}
+
+static int intel_8xx_fetch_size(void)
+{
+ u8 temp;
+
+ pci_read_config_byte(agp_bridge->dev, INTEL_APSIZE, &temp);
+ return __intel_8xx_fetch_size(temp);
+}
+
+static int intel_815_fetch_size(void)
+{
+ u8 temp;
+
+ /* Intel 815 chipsets have a _weird_ APSIZE register with only
+ * one non-reserved bit, so mask the others out ... */
+ pci_read_config_byte(agp_bridge->dev, INTEL_APSIZE, &temp);
+ temp &= (1 << 3);
+
+ return __intel_8xx_fetch_size(temp);
+}
+
+static void intel_tlbflush(struct agp_memory *mem)
+{
+ pci_write_config_dword(agp_bridge->dev, INTEL_AGPCTRL, 0x2200);
+ pci_write_config_dword(agp_bridge->dev, INTEL_AGPCTRL, 0x2280);
+}
+
+
+static void intel_8xx_tlbflush(struct agp_memory *mem)
+{
+ u32 temp;
+ pci_read_config_dword(agp_bridge->dev, INTEL_AGPCTRL, &temp);
+ pci_write_config_dword(agp_bridge->dev, INTEL_AGPCTRL, temp & ~(1 << 7));
+ pci_read_config_dword(agp_bridge->dev, INTEL_AGPCTRL, &temp);
+ pci_write_config_dword(agp_bridge->dev, INTEL_AGPCTRL, temp | (1 << 7));
+}
+
+
+static void intel_cleanup(void)
+{
+ u16 temp;
+ struct aper_size_info_16 *previous_size;
+
+ previous_size = A_SIZE_16(agp_bridge->previous_size);
+ pci_read_config_word(agp_bridge->dev, INTEL_NBXCFG, &temp);
+ pci_write_config_word(agp_bridge->dev, INTEL_NBXCFG, temp & ~(1 << 9));
+ pci_write_config_word(agp_bridge->dev, INTEL_APSIZE, previous_size->size_value);
+}
+
+
+static void intel_8xx_cleanup(void)
+{
+ u16 temp;
+ struct aper_size_info_8 *previous_size;
+
+ previous_size = A_SIZE_8(agp_bridge->previous_size);
+ pci_read_config_word(agp_bridge->dev, INTEL_NBXCFG, &temp);
+ pci_write_config_word(agp_bridge->dev, INTEL_NBXCFG, temp & ~(1 << 9));
+ pci_write_config_byte(agp_bridge->dev, INTEL_APSIZE, previous_size->size_value);
+}
+
+
+static int intel_configure(void)
+{
+ u32 temp;
+ u16 temp2;
+ struct aper_size_info_16 *current_size;
+
+ current_size = A_SIZE_16(agp_bridge->current_size);
+
+ /* aperture size */
+ pci_write_config_word(agp_bridge->dev, INTEL_APSIZE, current_size->size_value);
+
+ /* address to map to */
+ pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp);
+ agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+
+ /* attbase - aperture base */
+ pci_write_config_dword(agp_bridge->dev, INTEL_ATTBASE, agp_bridge->gatt_bus_addr);
+
+ /* agpctrl */
+ pci_write_config_dword(agp_bridge->dev, INTEL_AGPCTRL, 0x2280);
+
+ /* paccfg/nbxcfg */
+ pci_read_config_word(agp_bridge->dev, INTEL_NBXCFG, &temp2);
+ pci_write_config_word(agp_bridge->dev, INTEL_NBXCFG,
+ (temp2 & ~(1 << 10)) | (1 << 9));
+ /* clear any possible error conditions */
+ pci_write_config_byte(agp_bridge->dev, INTEL_ERRSTS + 1, 7);
+ return 0;
+}
+
+static int intel_815_configure(void)
+{
+ u32 temp, addr;
+ u8 temp2;
+ struct aper_size_info_8 *current_size;
+
+ /* attbase - aperture base */
+ /* the Intel 815 chipset spec. says that bits 29-31 in the
+ * ATTBASE register are reserved -> try not to write them */
+ if (agp_bridge->gatt_bus_addr & INTEL_815_ATTBASE_MASK) {
+ printk (KERN_EMERG PFX "gatt bus addr too high");
+ return -EINVAL;
+ }
+
+ current_size = A_SIZE_8(agp_bridge->current_size);
+
+ /* aperture size */
+ pci_write_config_byte(agp_bridge->dev, INTEL_APSIZE,
+ current_size->size_value);
+
+ /* address to map to */
+ pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp);
+ agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+
+ pci_read_config_dword(agp_bridge->dev, INTEL_ATTBASE, &addr);
+ addr &= INTEL_815_ATTBASE_MASK;
+ addr |= agp_bridge->gatt_bus_addr;
+ pci_write_config_dword(agp_bridge->dev, INTEL_ATTBASE, addr);
+
+ /* agpctrl */
+ pci_write_config_dword(agp_bridge->dev, INTEL_AGPCTRL, 0x0000);
+
+ /* apcont */
+ pci_read_config_byte(agp_bridge->dev, INTEL_815_APCONT, &temp2);
+ pci_write_config_byte(agp_bridge->dev, INTEL_815_APCONT, temp2 | (1 << 1));
+
+ /* clear any possible error conditions */
+ /* Oddness : this chipset seems to have no ERRSTS register ! */
+ return 0;
+}
+
+static void intel_820_tlbflush(struct agp_memory *mem)
+{
+ return;
+}
+
+static void intel_820_cleanup(void)
+{
+ u8 temp;
+ struct aper_size_info_8 *previous_size;
+
+ previous_size = A_SIZE_8(agp_bridge->previous_size);
+ pci_read_config_byte(agp_bridge->dev, INTEL_I820_RDCR, &temp);
+ pci_write_config_byte(agp_bridge->dev, INTEL_I820_RDCR,
+ temp & ~(1 << 1));
+ pci_write_config_byte(agp_bridge->dev, INTEL_APSIZE,
+ previous_size->size_value);
+}
+
+
+static int intel_820_configure(void)
+{
+ u32 temp;
+ u8 temp2;
+ struct aper_size_info_8 *current_size;
+
+ current_size = A_SIZE_8(agp_bridge->current_size);
+
+ /* aperture size */
+ pci_write_config_byte(agp_bridge->dev, INTEL_APSIZE, current_size->size_value);
+
+ /* address to map to */
+ pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp);
+ agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+
+ /* attbase - aperture base */
+ pci_write_config_dword(agp_bridge->dev, INTEL_ATTBASE, agp_bridge->gatt_bus_addr);
+
+ /* agpctrl */
+ pci_write_config_dword(agp_bridge->dev, INTEL_AGPCTRL, 0x0000);
+
+ /* global enable aperture access */
+ /* This flag is not accessed through MCHCFG register as in */
+ /* i850 chipset. */
+ pci_read_config_byte(agp_bridge->dev, INTEL_I820_RDCR, &temp2);
+ pci_write_config_byte(agp_bridge->dev, INTEL_I820_RDCR, temp2 | (1 << 1));
+ /* clear any possible AGP-related error conditions */
+ pci_write_config_word(agp_bridge->dev, INTEL_I820_ERRSTS, 0x001c);
+ return 0;
+}
+
+static int intel_840_configure(void)
+{
+ u32 temp;
+ u16 temp2;
+ struct aper_size_info_8 *current_size;
+
+ current_size = A_SIZE_8(agp_bridge->current_size);
+
+ /* aperture size */
+ pci_write_config_byte(agp_bridge->dev, INTEL_APSIZE, current_size->size_value);
+
+ /* address to map to */
+ pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp);
+ agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+
+ /* attbase - aperture base */
+ pci_write_config_dword(agp_bridge->dev, INTEL_ATTBASE, agp_bridge->gatt_bus_addr);
+
+ /* agpctrl */
+ pci_write_config_dword(agp_bridge->dev, INTEL_AGPCTRL, 0x0000);
+
+ /* mcgcfg */
+ pci_read_config_word(agp_bridge->dev, INTEL_I840_MCHCFG, &temp2);
+ pci_write_config_word(agp_bridge->dev, INTEL_I840_MCHCFG, temp2 | (1 << 9));
+ /* clear any possible error conditions */
+ pci_write_config_word(agp_bridge->dev, INTEL_I840_ERRSTS, 0xc000);
+ return 0;
+}
+
+static int intel_845_configure(void)
+{
+ u32 temp;
+ u8 temp2;
+ struct aper_size_info_8 *current_size;
+
+ current_size = A_SIZE_8(agp_bridge->current_size);
+
+ /* aperture size */
+ pci_write_config_byte(agp_bridge->dev, INTEL_APSIZE, current_size->size_value);
+
+ /* address to map to */
+ pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp);
+ agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+
+ /* attbase - aperture base */
+ pci_write_config_dword(agp_bridge->dev, INTEL_ATTBASE, agp_bridge->gatt_bus_addr);
+
+ /* agpctrl */
+ pci_write_config_dword(agp_bridge->dev, INTEL_AGPCTRL, 0x0000);
+
+ /* agpm */
+ pci_read_config_byte(agp_bridge->dev, INTEL_I845_AGPM, &temp2);
+ pci_write_config_byte(agp_bridge->dev, INTEL_I845_AGPM, temp2 | (1 << 1));
+ /* clear any possible error conditions */
+ pci_write_config_word(agp_bridge->dev, INTEL_I845_ERRSTS, 0x001c);
+ return 0;
+}
+
+static int intel_850_configure(void)
+{
+ u32 temp;
+ u16 temp2;
+ struct aper_size_info_8 *current_size;
+
+ current_size = A_SIZE_8(agp_bridge->current_size);
+
+ /* aperture size */
+ pci_write_config_byte(agp_bridge->dev, INTEL_APSIZE, current_size->size_value);
+
+ /* address to map to */
+ pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp);
+ agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+
+ /* attbase - aperture base */
+ pci_write_config_dword(agp_bridge->dev, INTEL_ATTBASE, agp_bridge->gatt_bus_addr);
+
+ /* agpctrl */
+ pci_write_config_dword(agp_bridge->dev, INTEL_AGPCTRL, 0x0000);
+
+ /* mcgcfg */
+ pci_read_config_word(agp_bridge->dev, INTEL_I850_MCHCFG, &temp2);
+ pci_write_config_word(agp_bridge->dev, INTEL_I850_MCHCFG, temp2 | (1 << 9));
+ /* clear any possible AGP-related error conditions */
+ pci_write_config_word(agp_bridge->dev, INTEL_I850_ERRSTS, 0x001c);
+ return 0;
+}
+
+static int intel_860_configure(void)
+{
+ u32 temp;
+ u16 temp2;
+ struct aper_size_info_8 *current_size;
+
+ current_size = A_SIZE_8(agp_bridge->current_size);
+
+ /* aperture size */
+ pci_write_config_byte(agp_bridge->dev, INTEL_APSIZE, current_size->size_value);
+
+ /* address to map to */
+ pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp);
+ agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+
+ /* attbase - aperture base */
+ pci_write_config_dword(agp_bridge->dev, INTEL_ATTBASE, agp_bridge->gatt_bus_addr);
+
+ /* agpctrl */
+ pci_write_config_dword(agp_bridge->dev, INTEL_AGPCTRL, 0x0000);
+
+ /* mcgcfg */
+ pci_read_config_word(agp_bridge->dev, INTEL_I860_MCHCFG, &temp2);
+ pci_write_config_word(agp_bridge->dev, INTEL_I860_MCHCFG, temp2 | (1 << 9));
+ /* clear any possible AGP-related error conditions */
+ pci_write_config_word(agp_bridge->dev, INTEL_I860_ERRSTS, 0xf700);
+ return 0;
+}
+
+static int intel_830mp_configure(void)
+{
+ u32 temp;
+ u16 temp2;
+ struct aper_size_info_8 *current_size;
+
+ current_size = A_SIZE_8(agp_bridge->current_size);
+
+ /* aperture size */
+ pci_write_config_byte(agp_bridge->dev, INTEL_APSIZE, current_size->size_value);
+
+ /* address to map to */
+ pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp);
+ agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+
+ /* attbase - aperture base */
+ pci_write_config_dword(agp_bridge->dev, INTEL_ATTBASE, agp_bridge->gatt_bus_addr);
+
+ /* agpctrl */
+ pci_write_config_dword(agp_bridge->dev, INTEL_AGPCTRL, 0x0000);
+
+ /* gmch */
+ pci_read_config_word(agp_bridge->dev, INTEL_NBXCFG, &temp2);
+ pci_write_config_word(agp_bridge->dev, INTEL_NBXCFG, temp2 | (1 << 9));
+ /* clear any possible AGP-related error conditions */
+ pci_write_config_word(agp_bridge->dev, INTEL_I830_ERRSTS, 0x1c);
+ return 0;
+}
+
+static int intel_7505_configure(void)
+{
+ u32 temp;
+ u16 temp2;
+ struct aper_size_info_8 *current_size;
+
+ current_size = A_SIZE_8(agp_bridge->current_size);
+
+ /* aperture size */
+ pci_write_config_byte(agp_bridge->dev, INTEL_APSIZE, current_size->size_value);
+
+ /* address to map to */
+ pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp);
+ agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+
+ /* attbase - aperture base */
+ pci_write_config_dword(agp_bridge->dev, INTEL_ATTBASE, agp_bridge->gatt_bus_addr);
+
+ /* agpctrl */
+ pci_write_config_dword(agp_bridge->dev, INTEL_AGPCTRL, 0x0000);
+
+ /* mchcfg */
+ pci_read_config_word(agp_bridge->dev, INTEL_I7505_MCHCFG, &temp2);
+ pci_write_config_word(agp_bridge->dev, INTEL_I7505_MCHCFG, temp2 | (1 << 9));
+
+ return 0;
+}
+
+/* Setup function */
+static struct gatt_mask intel_generic_masks[] =
+{
+ {.mask = 0x00000017, .type = 0}
+};
+
+static struct aper_size_info_8 intel_815_sizes[2] =
+{
+ {64, 16384, 4, 0},
+ {32, 8192, 3, 8},
+};
+
+static struct aper_size_info_8 intel_8xx_sizes[7] =
+{
+ {256, 65536, 6, 0},
+ {128, 32768, 5, 32},
+ {64, 16384, 4, 48},
+ {32, 8192, 3, 56},
+ {16, 4096, 2, 60},
+ {8, 2048, 1, 62},
+ {4, 1024, 0, 63}
+};
+
+static struct aper_size_info_16 intel_generic_sizes[7] =
+{
+ {256, 65536, 6, 0},
+ {128, 32768, 5, 32},
+ {64, 16384, 4, 48},
+ {32, 8192, 3, 56},
+ {16, 4096, 2, 60},
+ {8, 2048, 1, 62},
+ {4, 1024, 0, 63}
+};
+
+static struct aper_size_info_8 intel_830mp_sizes[4] =
+{
+ {256, 65536, 6, 0},
+ {128, 32768, 5, 32},
+ {64, 16384, 4, 48},
+ {32, 8192, 3, 56}
+};
+
+static struct agp_bridge_driver intel_generic_driver = {
+ .owner = THIS_MODULE,
+ .aperture_sizes = intel_generic_sizes,
+ .size_type = U16_APER_SIZE,
+ .num_aperture_sizes = 7,
+ .configure = intel_configure,
+ .fetch_size = intel_fetch_size,
+ .cleanup = intel_cleanup,
+ .tlb_flush = intel_tlbflush,
+ .mask_memory = agp_generic_mask_memory,
+ .masks = intel_generic_masks,
+ .agp_enable = agp_generic_enable,
+ .cache_flush = global_cache_flush,
+ .create_gatt_table = agp_generic_create_gatt_table,
+ .free_gatt_table = agp_generic_free_gatt_table,
+ .insert_memory = agp_generic_insert_memory,
+ .remove_memory = agp_generic_remove_memory,
+ .alloc_by_type = agp_generic_alloc_by_type,
+ .free_by_type = agp_generic_free_by_type,
+ .agp_alloc_page = agp_generic_alloc_page,
+ .agp_destroy_page = agp_generic_destroy_page,
+};
+
+static struct agp_bridge_driver intel_810_driver = {
+ .owner = THIS_MODULE,
+ .aperture_sizes = intel_i810_sizes,
+ .size_type = FIXED_APER_SIZE,
+ .num_aperture_sizes = 2,
+ .needs_scratch_page = TRUE,
+ .configure = intel_i810_configure,
+ .fetch_size = intel_i810_fetch_size,
+ .cleanup = intel_i810_cleanup,
+ .tlb_flush = intel_i810_tlbflush,
+ .mask_memory = intel_i810_mask_memory,
+ .masks = intel_i810_masks,
+ .agp_enable = intel_i810_agp_enable,
+ .cache_flush = global_cache_flush,
+ .create_gatt_table = agp_generic_create_gatt_table,
+ .free_gatt_table = agp_generic_free_gatt_table,
+ .insert_memory = intel_i810_insert_entries,
+ .remove_memory = intel_i810_remove_entries,
+ .alloc_by_type = intel_i810_alloc_by_type,
+ .free_by_type = intel_i810_free_by_type,
+ .agp_alloc_page = agp_generic_alloc_page,
+ .agp_destroy_page = agp_generic_destroy_page,
+};
+
+static struct agp_bridge_driver intel_815_driver = {
+ .owner = THIS_MODULE,
+ .aperture_sizes = intel_815_sizes,
+ .size_type = U8_APER_SIZE,
+ .num_aperture_sizes = 2,
+ .configure = intel_815_configure,
+ .fetch_size = intel_815_fetch_size,
+ .cleanup = intel_8xx_cleanup,
+ .tlb_flush = intel_8xx_tlbflush,
+ .mask_memory = agp_generic_mask_memory,
+ .masks = intel_generic_masks,
+ .agp_enable = agp_generic_enable,
+ .cache_flush = global_cache_flush,
+ .create_gatt_table = agp_generic_create_gatt_table,
+ .free_gatt_table = agp_generic_free_gatt_table,
+ .insert_memory = agp_generic_insert_memory,
+ .remove_memory = agp_generic_remove_memory,
+ .alloc_by_type = agp_generic_alloc_by_type,
+ .free_by_type = agp_generic_free_by_type,
+ .agp_alloc_page = agp_generic_alloc_page,
+ .agp_destroy_page = agp_generic_destroy_page,
+};
+
+static struct agp_bridge_driver intel_830_driver = {
+ .owner = THIS_MODULE,
+ .aperture_sizes = intel_i830_sizes,
+ .size_type = FIXED_APER_SIZE,
+ .num_aperture_sizes = 3,
+ .needs_scratch_page = TRUE,
+ .configure = intel_i830_configure,
+ .fetch_size = intel_i830_fetch_size,
+ .cleanup = intel_i830_cleanup,
+ .tlb_flush = intel_i810_tlbflush,
+ .mask_memory = intel_i810_mask_memory,
+ .masks = intel_i810_masks,
+ .agp_enable = intel_i810_agp_enable,
+ .cache_flush = global_cache_flush,
+ .create_gatt_table = intel_i830_create_gatt_table,
+ .free_gatt_table = intel_i830_free_gatt_table,
+ .insert_memory = intel_i830_insert_entries,
+ .remove_memory = intel_i830_remove_entries,
+ .alloc_by_type = intel_i830_alloc_by_type,
+ .free_by_type = intel_i810_free_by_type,
+ .agp_alloc_page = agp_generic_alloc_page,
+ .agp_destroy_page = agp_generic_destroy_page,
+};
+
+static struct agp_bridge_driver intel_820_driver = {
+ .owner = THIS_MODULE,
+ .aperture_sizes = intel_8xx_sizes,
+ .size_type = U8_APER_SIZE,
+ .num_aperture_sizes = 7,
+ .configure = intel_820_configure,
+ .fetch_size = intel_8xx_fetch_size,
+ .cleanup = intel_820_cleanup,
+ .tlb_flush = intel_820_tlbflush,
+ .mask_memory = agp_generic_mask_memory,
+ .masks = intel_generic_masks,
+ .agp_enable = agp_generic_enable,
+ .cache_flush = global_cache_flush,
+ .create_gatt_table = agp_generic_create_gatt_table,
+ .free_gatt_table = agp_generic_free_gatt_table,
+ .insert_memory = agp_generic_insert_memory,
+ .remove_memory = agp_generic_remove_memory,
+ .alloc_by_type = agp_generic_alloc_by_type,
+ .free_by_type = agp_generic_free_by_type,
+ .agp_alloc_page = agp_generic_alloc_page,
+ .agp_destroy_page = agp_generic_destroy_page,
+};
+
+static struct agp_bridge_driver intel_830mp_driver = {
+ .owner = THIS_MODULE,
+ .aperture_sizes = intel_830mp_sizes,
+ .size_type = U8_APER_SIZE,
+ .num_aperture_sizes = 4,
+ .configure = intel_830mp_configure,
+ .fetch_size = intel_8xx_fetch_size,
+ .cleanup = intel_8xx_cleanup,
+ .tlb_flush = intel_8xx_tlbflush,
+ .mask_memory = agp_generic_mask_memory,
+ .masks = intel_generic_masks,
+ .agp_enable = agp_generic_enable,
+ .cache_flush = global_cache_flush,
+ .create_gatt_table = agp_generic_create_gatt_table,
+ .free_gatt_table = agp_generic_free_gatt_table,
+ .insert_memory = agp_generic_insert_memory,
+ .remove_memory = agp_generic_remove_memory,
+ .alloc_by_type = agp_generic_alloc_by_type,
+ .free_by_type = agp_generic_free_by_type,
+ .agp_alloc_page = agp_generic_alloc_page,
+ .agp_destroy_page = agp_generic_destroy_page,
+};
+
+static struct agp_bridge_driver intel_840_driver = {
+ .owner = THIS_MODULE,
+ .aperture_sizes = intel_8xx_sizes,
+ .size_type = U8_APER_SIZE,
+ .num_aperture_sizes = 7,
+ .configure = intel_840_configure,
+ .fetch_size = intel_8xx_fetch_size,
+ .cleanup = intel_8xx_cleanup,
+ .tlb_flush = intel_8xx_tlbflush,
+ .mask_memory = agp_generic_mask_memory,
+ .masks = intel_generic_masks,
+ .agp_enable = agp_generic_enable,
+ .cache_flush = global_cache_flush,
+ .create_gatt_table = agp_generic_create_gatt_table,
+ .free_gatt_table = agp_generic_free_gatt_table,
+ .insert_memory = agp_generic_insert_memory,
+ .remove_memory = agp_generic_remove_memory,
+ .alloc_by_type = agp_generic_alloc_by_type,
+ .free_by_type = agp_generic_free_by_type,
+ .agp_alloc_page = agp_generic_alloc_page,
+ .agp_destroy_page = agp_generic_destroy_page,
+};
+
+static struct agp_bridge_driver intel_845_driver = {
+ .owner = THIS_MODULE,
+ .aperture_sizes = intel_8xx_sizes,
+ .size_type = U8_APER_SIZE,
+ .num_aperture_sizes = 7,
+ .configure = intel_845_configure,
+ .fetch_size = intel_8xx_fetch_size,
+ .cleanup = intel_8xx_cleanup,
+ .tlb_flush = intel_8xx_tlbflush,
+ .mask_memory = agp_generic_mask_memory,
+ .masks = intel_generic_masks,
+ .agp_enable = agp_generic_enable,
+ .cache_flush = global_cache_flush,
+ .create_gatt_table = agp_generic_create_gatt_table,
+ .free_gatt_table = agp_generic_free_gatt_table,
+ .insert_memory = agp_generic_insert_memory,
+ .remove_memory = agp_generic_remove_memory,
+ .alloc_by_type = agp_generic_alloc_by_type,
+ .free_by_type = agp_generic_free_by_type,
+ .agp_alloc_page = agp_generic_alloc_page,
+ .agp_destroy_page = agp_generic_destroy_page,
+};
+
+static struct agp_bridge_driver intel_850_driver = {
+ .owner = THIS_MODULE,
+ .aperture_sizes = intel_8xx_sizes,
+ .size_type = U8_APER_SIZE,
+ .num_aperture_sizes = 7,
+ .configure = intel_850_configure,
+ .fetch_size = intel_8xx_fetch_size,
+ .cleanup = intel_8xx_cleanup,
+ .tlb_flush = intel_8xx_tlbflush,
+ .mask_memory = agp_generic_mask_memory,
+ .masks = intel_generic_masks,
+ .agp_enable = agp_generic_enable,
+ .cache_flush = global_cache_flush,
+ .create_gatt_table = agp_generic_create_gatt_table,
+ .free_gatt_table = agp_generic_free_gatt_table,
+ .insert_memory = agp_generic_insert_memory,
+ .remove_memory = agp_generic_remove_memory,
+ .alloc_by_type = agp_generic_alloc_by_type,
+ .free_by_type = agp_generic_free_by_type,
+ .agp_alloc_page = agp_generic_alloc_page,
+ .agp_destroy_page = agp_generic_destroy_page,
+};
+
+static struct agp_bridge_driver intel_860_driver = {
+ .owner = THIS_MODULE,
+ .aperture_sizes = intel_8xx_sizes,
+ .size_type = U8_APER_SIZE,
+ .num_aperture_sizes = 7,
+ .configure = intel_860_configure,
+ .fetch_size = intel_8xx_fetch_size,
+ .cleanup = intel_8xx_cleanup,
+ .tlb_flush = intel_8xx_tlbflush,
+ .mask_memory = agp_generic_mask_memory,
+ .masks = intel_generic_masks,
+ .agp_enable = agp_generic_enable,
+ .cache_flush = global_cache_flush,
+ .create_gatt_table = agp_generic_create_gatt_table,
+ .free_gatt_table = agp_generic_free_gatt_table,
+ .insert_memory = agp_generic_insert_memory,
+ .remove_memory = agp_generic_remove_memory,
+ .alloc_by_type = agp_generic_alloc_by_type,
+ .free_by_type = agp_generic_free_by_type,
+ .agp_alloc_page = agp_generic_alloc_page,
+ .agp_destroy_page = agp_generic_destroy_page,
+};
+
+static struct agp_bridge_driver intel_915_driver = {
+ .owner = THIS_MODULE,
+ .aperture_sizes = intel_i830_sizes,
+ .size_type = FIXED_APER_SIZE,
+ .num_aperture_sizes = 3,
+ .needs_scratch_page = TRUE,
+ .configure = intel_i915_configure,
+ .fetch_size = intel_i915_fetch_size,
+ .cleanup = intel_i915_cleanup,
+ .tlb_flush = intel_i810_tlbflush,
+ .mask_memory = intel_i810_mask_memory,
+ .masks = intel_i810_masks,
+ .agp_enable = intel_i810_agp_enable,
+ .cache_flush = global_cache_flush,
+ .create_gatt_table = intel_i915_create_gatt_table,
+ .free_gatt_table = intel_i830_free_gatt_table,
+ .insert_memory = intel_i915_insert_entries,
+ .remove_memory = intel_i915_remove_entries,
+ .alloc_by_type = intel_i830_alloc_by_type,
+ .free_by_type = intel_i810_free_by_type,
+ .agp_alloc_page = agp_generic_alloc_page,
+ .agp_destroy_page = agp_generic_destroy_page,
+};
+
+
+static struct agp_bridge_driver intel_7505_driver = {
+ .owner = THIS_MODULE,
+ .aperture_sizes = intel_8xx_sizes,
+ .size_type = U8_APER_SIZE,
+ .num_aperture_sizes = 7,
+ .configure = intel_7505_configure,
+ .fetch_size = intel_8xx_fetch_size,
+ .cleanup = intel_8xx_cleanup,
+ .tlb_flush = intel_8xx_tlbflush,
+ .mask_memory = agp_generic_mask_memory,
+ .masks = intel_generic_masks,
+ .agp_enable = agp_generic_enable,
+ .cache_flush = global_cache_flush,
+ .create_gatt_table = agp_generic_create_gatt_table,
+ .free_gatt_table = agp_generic_free_gatt_table,
+ .insert_memory = agp_generic_insert_memory,
+ .remove_memory = agp_generic_remove_memory,
+ .alloc_by_type = agp_generic_alloc_by_type,
+ .free_by_type = agp_generic_free_by_type,
+ .agp_alloc_page = agp_generic_alloc_page,
+ .agp_destroy_page = agp_generic_destroy_page,
+};
+
+static int find_i810(u16 device)
+{
+ struct pci_dev *i810_dev;
+
+ i810_dev = pci_get_device(PCI_VENDOR_ID_INTEL, device, NULL);
+ if (!i810_dev)
+ return 0;
+ intel_i810_private.i810_dev = i810_dev;
+ return 1;
+}
+
+static int find_i830(u16 device)
+{
+ struct pci_dev *i830_dev;
+
+ i830_dev = pci_get_device(PCI_VENDOR_ID_INTEL, device, NULL);
+ if (i830_dev && PCI_FUNC(i830_dev->devfn) != 0) {
+ i830_dev = pci_get_device(PCI_VENDOR_ID_INTEL,
+ device, i830_dev);
+ }
+
+ if (!i830_dev)
+ return 0;
+
+ intel_i830_private.i830_dev = i830_dev;
+ return 1;
+}
+
+static int __devinit agp_intel_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct agp_bridge_data *bridge;
+ char *name = "(unknown)";
+ u8 cap_ptr = 0;
+ struct resource *r;
+
+ cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP);
+
+ bridge = agp_alloc_bridge();
+ if (!bridge)
+ return -ENOMEM;
+
+ switch (pdev->device) {
+ case PCI_DEVICE_ID_INTEL_82443LX_0:
+ bridge->driver = &intel_generic_driver;
+ name = "440LX";
+ break;
+ case PCI_DEVICE_ID_INTEL_82443BX_0:
+ bridge->driver = &intel_generic_driver;
+ name = "440BX";
+ break;
+ case PCI_DEVICE_ID_INTEL_82443GX_0:
+ bridge->driver = &intel_generic_driver;
+ name = "440GX";
+ break;
+ case PCI_DEVICE_ID_INTEL_82810_MC1:
+ name = "i810";
+ if (!find_i810(PCI_DEVICE_ID_INTEL_82810_IG1))
+ goto fail;
+ bridge->driver = &intel_810_driver;
+ break;
+ case PCI_DEVICE_ID_INTEL_82810_MC3:
+ name = "i810 DC100";
+ if (!find_i810(PCI_DEVICE_ID_INTEL_82810_IG3))
+ goto fail;
+ bridge->driver = &intel_810_driver;
+ break;
+ case PCI_DEVICE_ID_INTEL_82810E_MC:
+ name = "i810 E";
+ if (!find_i810(PCI_DEVICE_ID_INTEL_82810E_IG))
+ goto fail;
+ bridge->driver = &intel_810_driver;
+ break;
+ case PCI_DEVICE_ID_INTEL_82815_MC:
+ /*
+ * The i815 can operate either as an i810 style
+ * integrated device, or as an AGP4X motherboard.
+ */
+ if (find_i810(PCI_DEVICE_ID_INTEL_82815_CGC))
+ bridge->driver = &intel_810_driver;
+ else
+ bridge->driver = &intel_815_driver;
+ name = "i815";
+ break;
+ case PCI_DEVICE_ID_INTEL_82820_HB:
+ case PCI_DEVICE_ID_INTEL_82820_UP_HB:
+ bridge->driver = &intel_820_driver;
+ name = "i820";
+ break;
+ case PCI_DEVICE_ID_INTEL_82830_HB:
+ if (find_i830(PCI_DEVICE_ID_INTEL_82830_CGC)) {
+ bridge->driver = &intel_830_driver;
+ } else {
+ bridge->driver = &intel_830mp_driver;
+ }
+ name = "830M";
+ break;
+ case PCI_DEVICE_ID_INTEL_82840_HB:
+ bridge->driver = &intel_840_driver;
+ name = "i840";
+ break;
+ case PCI_DEVICE_ID_INTEL_82845_HB:
+ bridge->driver = &intel_845_driver;
+ name = "i845";
+ break;
+ case PCI_DEVICE_ID_INTEL_82845G_HB:
+ if (find_i830(PCI_DEVICE_ID_INTEL_82845G_IG)) {
+ bridge->driver = &intel_830_driver;
+ } else {
+ bridge->driver = &intel_845_driver;
+ }
+ name = "845G";
+ break;
+ case PCI_DEVICE_ID_INTEL_82850_HB:
+ bridge->driver = &intel_850_driver;
+ name = "i850";
+ break;
+ case PCI_DEVICE_ID_INTEL_82855PM_HB:
+ bridge->driver = &intel_845_driver;
+ name = "855PM";
+ break;
+ case PCI_DEVICE_ID_INTEL_82855GM_HB:
+ if (find_i830(PCI_DEVICE_ID_INTEL_82855GM_IG)) {
+ bridge->driver = &intel_830_driver;
+ name = "855";
+ } else {
+ bridge->driver = &intel_845_driver;
+ name = "855GM";
+ }
+ break;
+ case PCI_DEVICE_ID_INTEL_82860_HB:
+ bridge->driver = &intel_860_driver;
+ name = "i860";
+ break;
+ case PCI_DEVICE_ID_INTEL_82865_HB:
+ if (find_i830(PCI_DEVICE_ID_INTEL_82865_IG)) {
+ bridge->driver = &intel_830_driver;
+ } else {
+ bridge->driver = &intel_845_driver;
+ }
+ name = "865";
+ break;
+ case PCI_DEVICE_ID_INTEL_82875_HB:
+ bridge->driver = &intel_845_driver;
+ name = "i875";
+ break;
+ case PCI_DEVICE_ID_INTEL_82915G_HB:
+ if (find_i830(PCI_DEVICE_ID_INTEL_82915G_IG)) {
+ bridge->driver = &intel_915_driver;
+ } else {
+ bridge->driver = &intel_845_driver;
+ }
+ name = "915G";
+ break;
+ case PCI_DEVICE_ID_INTEL_82915GM_HB:
+ if (find_i830(PCI_DEVICE_ID_INTEL_82915GM_IG)) {
+ bridge->driver = &intel_915_driver;
+ } else {
+ bridge->driver = &intel_845_driver;
+ }
+ name = "915GM";
+ break;
+ case PCI_DEVICE_ID_INTEL_7505_0:
+ bridge->driver = &intel_7505_driver;
+ name = "E7505";
+ break;
+ case PCI_DEVICE_ID_INTEL_7205_0:
+ bridge->driver = &intel_7505_driver;
+ name = "E7205";
+ break;
+ default:
+ if (cap_ptr)
+ printk(KERN_WARNING PFX "Unsupported Intel chipset (device id: %04x)\n",
+ pdev->device);
+ agp_put_bridge(bridge);
+ return -ENODEV;
+ };
+
+ bridge->dev = pdev;
+ bridge->capndx = cap_ptr;
+
+ if (bridge->driver == &intel_810_driver)
+ bridge->dev_private_data = &intel_i810_private;
+ else if (bridge->driver == &intel_830_driver)
+ bridge->dev_private_data = &intel_i830_private;
+
+ printk(KERN_INFO PFX "Detected an Intel %s Chipset.\n", name);
+
+ /*
+ * The following fixes the case where the BIOS has "forgotten" to
+ * provide an address range for the GART.
+ * 20030610 - hamish@zot.org
+ */
+ r = &pdev->resource[0];
+ if (!r->start && r->end) {
+ if(pci_assign_resource(pdev, 0)) {
+ printk(KERN_ERR PFX "could not assign resource 0\n");
+ agp_put_bridge(bridge);
+ return -ENODEV;
+ }
+ }
+
+ /*
+ * If the device has not been properly setup, the following will catch
+ * the problem and should stop the system from crashing.
+ * 20030610 - hamish@zot.org
+ */
+ if (pci_enable_device(pdev)) {
+ printk(KERN_ERR PFX "Unable to Enable PCI device\n");
+ agp_put_bridge(bridge);
+ return -ENODEV;
+ }
+
+ /* Fill in the mode register */
+ if (cap_ptr) {
+ pci_read_config_dword(pdev,
+ bridge->capndx+PCI_AGP_STATUS,
+ &bridge->mode);
+ }
+
+ pci_set_drvdata(pdev, bridge);
+ return agp_add_bridge(bridge);
+
+fail:
+ printk(KERN_ERR PFX "Detected an Intel %s chipset, "
+ "but could not find the secondary device.\n", name);
+ agp_put_bridge(bridge);
+ return -ENODEV;
+}
+
+static void __devexit agp_intel_remove(struct pci_dev *pdev)
+{
+ struct agp_bridge_data *bridge = pci_get_drvdata(pdev);
+
+ agp_remove_bridge(bridge);
+
+ if (intel_i810_private.i810_dev)
+ pci_dev_put(intel_i810_private.i810_dev);
+ if (intel_i830_private.i830_dev)
+ pci_dev_put(intel_i830_private.i830_dev);
+
+ agp_put_bridge(bridge);
+}
+
+static int agp_intel_resume(struct pci_dev *pdev)
+{
+ struct agp_bridge_data *bridge = pci_get_drvdata(pdev);
+
+ pci_restore_state(pdev);
+
+ if (bridge->driver == &intel_generic_driver)
+ intel_configure();
+ else if (bridge->driver == &intel_850_driver)
+ intel_850_configure();
+ else if (bridge->driver == &intel_845_driver)
+ intel_845_configure();
+ else if (bridge->driver == &intel_830mp_driver)
+ intel_830mp_configure();
+ else if (bridge->driver == &intel_915_driver)
+ intel_i915_configure();
+ else if (bridge->driver == &intel_830_driver)
+ intel_i830_configure();
+ else if (bridge->driver == &intel_810_driver)
+ intel_i810_configure();
+
+ return 0;
+}
+
+static struct pci_device_id agp_intel_pci_table[] = {
+#define ID(x) \
+ { \
+ .class = (PCI_CLASS_BRIDGE_HOST << 8), \
+ .class_mask = ~0, \
+ .vendor = PCI_VENDOR_ID_INTEL, \
+ .device = x, \
+ .subvendor = PCI_ANY_ID, \
+ .subdevice = PCI_ANY_ID, \
+ }
+ ID(PCI_DEVICE_ID_INTEL_82443LX_0),
+ ID(PCI_DEVICE_ID_INTEL_82443BX_0),
+ ID(PCI_DEVICE_ID_INTEL_82443GX_0),
+ ID(PCI_DEVICE_ID_INTEL_82810_MC1),
+ ID(PCI_DEVICE_ID_INTEL_82810_MC3),
+ ID(PCI_DEVICE_ID_INTEL_82810E_MC),
+ ID(PCI_DEVICE_ID_INTEL_82815_MC),
+ ID(PCI_DEVICE_ID_INTEL_82820_HB),
+ ID(PCI_DEVICE_ID_INTEL_82820_UP_HB),
+ ID(PCI_DEVICE_ID_INTEL_82830_HB),
+ ID(PCI_DEVICE_ID_INTEL_82840_HB),
+ ID(PCI_DEVICE_ID_INTEL_82845_HB),
+ ID(PCI_DEVICE_ID_INTEL_82845G_HB),
+ ID(PCI_DEVICE_ID_INTEL_82850_HB),
+ ID(PCI_DEVICE_ID_INTEL_82855PM_HB),
+ ID(PCI_DEVICE_ID_INTEL_82855GM_HB),
+ ID(PCI_DEVICE_ID_INTEL_82860_HB),
+ ID(PCI_DEVICE_ID_INTEL_82865_HB),
+ ID(PCI_DEVICE_ID_INTEL_82875_HB),
+ ID(PCI_DEVICE_ID_INTEL_7505_0),
+ ID(PCI_DEVICE_ID_INTEL_7205_0),
+ ID(PCI_DEVICE_ID_INTEL_82915G_HB),
+ ID(PCI_DEVICE_ID_INTEL_82915GM_HB),
+ { }
+};
+
+MODULE_DEVICE_TABLE(pci, agp_intel_pci_table);
+
+static struct pci_driver agp_intel_pci_driver = {
+ .name = "agpgart-intel",
+ .id_table = agp_intel_pci_table,
+ .probe = agp_intel_probe,
+ .remove = __devexit_p(agp_intel_remove),
+ .resume = agp_intel_resume,
+};
+
+static int __init agp_intel_init(void)
+{
+ if (agp_off)
+ return -EINVAL;
+ return pci_register_driver(&agp_intel_pci_driver);
+}
+
+static void __exit agp_intel_cleanup(void)
+{
+ pci_unregister_driver(&agp_intel_pci_driver);
+}
+
+module_init(agp_intel_init);
+module_exit(agp_intel_cleanup);
+
+MODULE_AUTHOR("Dave Jones <davej@codemonkey.org.uk>");
+MODULE_LICENSE("GPL and additional rights");
diff --git a/drivers/char/agp/isoch.c b/drivers/char/agp/isoch.c
new file mode 100644
index 00000000000..c9ac731504f
--- /dev/null
+++ b/drivers/char/agp/isoch.c
@@ -0,0 +1,470 @@
+/*
+ * Setup routines for AGP 3.5 compliant bridges.
+ */
+
+#include <linux/list.h>
+#include <linux/pci.h>
+#include <linux/agp_backend.h>
+#include <linux/module.h>
+
+#include "agp.h"
+
+/* Generic AGP 3.5 enabling routines */
+
+struct agp_3_5_dev {
+ struct list_head list;
+ u8 capndx;
+ u32 maxbw;
+ struct pci_dev *dev;
+};
+
+static void agp_3_5_dev_list_insert(struct list_head *head, struct list_head *new)
+{
+ struct agp_3_5_dev *cur, *n = list_entry(new, struct agp_3_5_dev, list);
+ struct list_head *pos;
+
+ list_for_each(pos, head) {
+ cur = list_entry(pos, struct agp_3_5_dev, list);
+ if(cur->maxbw > n->maxbw)
+ break;
+ }
+ list_add_tail(new, pos);
+}
+
+static void agp_3_5_dev_list_sort(struct agp_3_5_dev *list, unsigned int ndevs)
+{
+ struct agp_3_5_dev *cur;
+ struct pci_dev *dev;
+ struct list_head *pos, *tmp, *head = &list->list, *start = head->next;
+ u32 nistat;
+
+ INIT_LIST_HEAD(head);
+
+ for (pos=start; pos!=head; ) {
+ cur = list_entry(pos, struct agp_3_5_dev, list);
+ dev = cur->dev;
+
+ pci_read_config_dword(dev, cur->capndx+AGPNISTAT, &nistat);
+ cur->maxbw = (nistat >> 16) & 0xff;
+
+ tmp = pos;
+ pos = pos->next;
+ agp_3_5_dev_list_insert(head, tmp);
+ }
+}
+
+/*
+ * Initialize all isochronous transfer parameters for an AGP 3.0
+ * node (i.e. a host bridge in combination with the adapters
+ * lying behind it...)
+ */
+
+static int agp_3_5_isochronous_node_enable(struct agp_bridge_data *bridge,
+ struct agp_3_5_dev *dev_list, unsigned int ndevs)
+{
+ /*
+ * Convenience structure to make the calculations clearer
+ * here. The field names come straight from the AGP 3.0 spec.
+ */
+ struct isoch_data {
+ u32 maxbw;
+ u32 n;
+ u32 y;
+ u32 l;
+ u32 rq;
+ struct agp_3_5_dev *dev;
+ };
+
+ struct pci_dev *td = bridge->dev, *dev;
+ struct list_head *head = &dev_list->list, *pos;
+ struct agp_3_5_dev *cur;
+ struct isoch_data *master, target;
+ unsigned int cdev = 0;
+ u32 mnistat, tnistat, tstatus, mcmd;
+ u16 tnicmd, mnicmd;
+ u8 mcapndx;
+ u32 tot_bw = 0, tot_n = 0, tot_rq = 0, y_max, rq_isoch, rq_async;
+ u32 step, rem, rem_isoch, rem_async;
+ int ret = 0;
+
+ /*
+ * We'll work with an array of isoch_data's (one for each
+ * device in dev_list) throughout this function.
+ */
+ if ((master = kmalloc(ndevs * sizeof(*master), GFP_KERNEL)) == NULL) {
+ ret = -ENOMEM;
+ goto get_out;
+ }
+
+ /*
+ * Sort the device list by maxbw. We need to do this because the
+ * spec suggests that the devices with the smallest requirements
+ * have their resources allocated first, with all remaining resources
+ * falling to the device with the largest requirement.
+ *
+ * We don't exactly do this, we divide target resources by ndevs
+ * and split them amongst the AGP 3.0 devices. The remainder of such
+ * division operations are dropped on the last device, sort of like
+ * the spec mentions it should be done.
+ *
+ * We can't do this sort when we initially construct the dev_list
+ * because we don't know until this function whether isochronous
+ * transfers are enabled and consequently whether maxbw will mean
+ * anything.
+ */
+ agp_3_5_dev_list_sort(dev_list, ndevs);
+
+ pci_read_config_dword(td, bridge->capndx+AGPNISTAT, &tnistat);
+ pci_read_config_dword(td, bridge->capndx+AGPSTAT, &tstatus);
+
+ /* Extract power-on defaults from the target */
+ target.maxbw = (tnistat >> 16) & 0xff;
+ target.n = (tnistat >> 8) & 0xff;
+ target.y = (tnistat >> 6) & 0x3;
+ target.l = (tnistat >> 3) & 0x7;
+ target.rq = (tstatus >> 24) & 0xff;
+
+ y_max = target.y;
+
+ /*
+ * Extract power-on defaults for each device in dev_list. Along
+ * the way, calculate the total isochronous bandwidth required
+ * by these devices and the largest requested payload size.
+ */
+ list_for_each(pos, head) {
+ cur = list_entry(pos, struct agp_3_5_dev, list);
+ dev = cur->dev;
+
+ mcapndx = cur->capndx;
+
+ pci_read_config_dword(dev, cur->capndx+AGPNISTAT, &mnistat);
+
+ master[cdev].maxbw = (mnistat >> 16) & 0xff;
+ master[cdev].n = (mnistat >> 8) & 0xff;
+ master[cdev].y = (mnistat >> 6) & 0x3;
+ master[cdev].dev = cur;
+
+ tot_bw += master[cdev].maxbw;
+ y_max = max(y_max, master[cdev].y);
+
+ cdev++;
+ }
+
+ /* Check if this configuration has any chance of working */
+ if (tot_bw > target.maxbw) {
+ printk(KERN_ERR PFX "isochronous bandwidth required "
+ "by AGP 3.0 devices exceeds that which is supported by "
+ "the AGP 3.0 bridge!\n");
+ ret = -ENODEV;
+ goto free_and_exit;
+ }
+
+ target.y = y_max;
+
+ /*
+ * Write the calculated payload size into the target's NICMD
+ * register. Doing this directly effects the ISOCH_N value
+ * in the target's NISTAT register, so we need to do this now
+ * to get an accurate value for ISOCH_N later.
+ */
+ pci_read_config_word(td, bridge->capndx+AGPNICMD, &tnicmd);
+ tnicmd &= ~(0x3 << 6);
+ tnicmd |= target.y << 6;
+ pci_write_config_word(td, bridge->capndx+AGPNICMD, tnicmd);
+
+ /* Reread the target's ISOCH_N */
+ pci_read_config_dword(td, bridge->capndx+AGPNISTAT, &tnistat);
+ target.n = (tnistat >> 8) & 0xff;
+
+ /* Calculate the minimum ISOCH_N needed by each master */
+ for (cdev=0; cdev<ndevs; cdev++) {
+ master[cdev].y = target.y;
+ master[cdev].n = master[cdev].maxbw / (master[cdev].y + 1);
+
+ tot_n += master[cdev].n;
+ }
+
+ /* Exit if the minimal ISOCH_N allocation among the masters is more
+ * than the target can handle. */
+ if (tot_n > target.n) {
+ printk(KERN_ERR PFX "number of isochronous "
+ "transactions per period required by AGP 3.0 devices "
+ "exceeds that which is supported by the AGP 3.0 "
+ "bridge!\n");
+ ret = -ENODEV;
+ goto free_and_exit;
+ }
+
+ /* Calculate left over ISOCH_N capability in the target. We'll give
+ * this to the hungriest device (as per the spec) */
+ rem = target.n - tot_n;
+
+ /*
+ * Calculate the minimum isochronous RQ depth needed by each master.
+ * Along the way, distribute the extra ISOCH_N capability calculated
+ * above.
+ */
+ for (cdev=0; cdev<ndevs; cdev++) {
+ /*
+ * This is a little subtle. If ISOCH_Y > 64B, then ISOCH_Y
+ * byte isochronous writes will be broken into 64B pieces.
+ * This means we need to budget more RQ depth to account for
+ * these kind of writes (each isochronous write is actually
+ * many writes on the AGP bus).
+ */
+ master[cdev].rq = master[cdev].n;
+ if(master[cdev].y > 0x1)
+ master[cdev].rq *= (1 << (master[cdev].y - 1));
+
+ tot_rq += master[cdev].rq;
+
+ if (cdev == ndevs-1)
+ master[cdev].n += rem;
+ }
+
+ /* Figure the number of isochronous and asynchronous RQ slots the
+ * target is providing. */
+ rq_isoch = (target.y > 0x1) ? target.n * (1 << (target.y - 1)) : target.n;
+ rq_async = target.rq - rq_isoch;
+
+ /* Exit if the minimal RQ needs of the masters exceeds what the target
+ * can provide. */
+ if (tot_rq > rq_isoch) {
+ printk(KERN_ERR PFX "number of request queue slots "
+ "required by the isochronous bandwidth requested by "
+ "AGP 3.0 devices exceeds the number provided by the "
+ "AGP 3.0 bridge!\n");
+ ret = -ENODEV;
+ goto free_and_exit;
+ }
+
+ /* Calculate asynchronous RQ capability in the target (per master) as
+ * well as the total number of leftover isochronous RQ slots. */
+ step = rq_async / ndevs;
+ rem_async = step + (rq_async % ndevs);
+ rem_isoch = rq_isoch - tot_rq;
+
+ /* Distribute the extra RQ slots calculated above and write our
+ * isochronous settings out to the actual devices. */
+ for (cdev=0; cdev<ndevs; cdev++) {
+ cur = master[cdev].dev;
+ dev = cur->dev;
+
+ mcapndx = cur->capndx;
+
+ master[cdev].rq += (cdev == ndevs - 1)
+ ? (rem_async + rem_isoch) : step;
+
+ pci_read_config_word(dev, cur->capndx+AGPNICMD, &mnicmd);
+ pci_read_config_dword(dev, cur->capndx+AGPCMD, &mcmd);
+
+ mnicmd &= ~(0xff << 8);
+ mnicmd &= ~(0x3 << 6);
+ mcmd &= ~(0xff << 24);
+
+ mnicmd |= master[cdev].n << 8;
+ mnicmd |= master[cdev].y << 6;
+ mcmd |= master[cdev].rq << 24;
+
+ pci_write_config_dword(dev, cur->capndx+AGPCMD, mcmd);
+ pci_write_config_word(dev, cur->capndx+AGPNICMD, mnicmd);
+ }
+
+free_and_exit:
+ kfree(master);
+
+get_out:
+ return ret;
+}
+
+/*
+ * This function basically allocates request queue slots among the
+ * AGP 3.0 systems in nonisochronous nodes. The algorithm is
+ * pretty stupid, divide the total number of RQ slots provided by the
+ * target by ndevs. Distribute this many slots to each AGP 3.0 device,
+ * giving any left over slots to the last device in dev_list.
+ */
+static void agp_3_5_nonisochronous_node_enable(struct agp_bridge_data *bridge,
+ struct agp_3_5_dev *dev_list, unsigned int ndevs)
+{
+ struct agp_3_5_dev *cur;
+ struct list_head *head = &dev_list->list, *pos;
+ u32 tstatus, mcmd;
+ u32 trq, mrq, rem;
+ unsigned int cdev = 0;
+
+ pci_read_config_dword(bridge->dev, bridge->capndx+AGPSTAT, &tstatus);
+
+ trq = (tstatus >> 24) & 0xff;
+ mrq = trq / ndevs;
+
+ rem = mrq + (trq % ndevs);
+
+ for (pos=head->next; cdev<ndevs; cdev++, pos=pos->next) {
+ cur = list_entry(pos, struct agp_3_5_dev, list);
+
+ pci_read_config_dword(cur->dev, cur->capndx+AGPCMD, &mcmd);
+ mcmd &= ~(0xff << 24);
+ mcmd |= ((cdev == ndevs - 1) ? rem : mrq) << 24;
+ pci_write_config_dword(cur->dev, cur->capndx+AGPCMD, mcmd);
+ }
+}
+
+/*
+ * Fully configure and enable an AGP 3.0 host bridge and all the devices
+ * lying behind it.
+ */
+int agp_3_5_enable(struct agp_bridge_data *bridge)
+{
+ struct pci_dev *td = bridge->dev, *dev = NULL;
+ u8 mcapndx;
+ u32 isoch, arqsz;
+ u32 tstatus, mstatus, ncapid;
+ u32 mmajor;
+ u16 mpstat;
+ struct agp_3_5_dev *dev_list, *cur;
+ struct list_head *head, *pos;
+ unsigned int ndevs = 0;
+ int ret = 0;
+
+ /* Extract some power-on defaults from the target */
+ pci_read_config_dword(td, bridge->capndx+AGPSTAT, &tstatus);
+ isoch = (tstatus >> 17) & 0x1;
+ if (isoch == 0) /* isoch xfers not available, bail out. */
+ return -ENODEV;
+
+ arqsz = (tstatus >> 13) & 0x7;
+
+ /*
+ * Allocate a head for our AGP 3.5 device list
+ * (multiple AGP v3 devices are allowed behind a single bridge).
+ */
+ if ((dev_list = kmalloc(sizeof(*dev_list), GFP_KERNEL)) == NULL) {
+ ret = -ENOMEM;
+ goto get_out;
+ }
+ head = &dev_list->list;
+ INIT_LIST_HEAD(head);
+
+ /* Find all AGP devices, and add them to dev_list. */
+ for_each_pci_dev(dev) {
+ mcapndx = pci_find_capability(dev, PCI_CAP_ID_AGP);
+ if (mcapndx == 0)
+ continue;
+
+ switch ((dev->class >>8) & 0xff00) {
+ case 0x0600: /* Bridge */
+ /* Skip bridges. We should call this function for each one. */
+ continue;
+
+ case 0x0001: /* Unclassified device */
+ /* Don't know what this is, but log it for investigation. */
+ if (mcapndx != 0) {
+ printk (KERN_INFO PFX "Wacky, found unclassified AGP device. %x:%x\n",
+ dev->vendor, dev->device);
+ }
+ continue;
+
+ case 0x0300: /* Display controller */
+ case 0x0400: /* Multimedia controller */
+ if((cur = kmalloc(sizeof(*cur), GFP_KERNEL)) == NULL) {
+ ret = -ENOMEM;
+ goto free_and_exit;
+ }
+ cur->dev = dev;
+
+ pos = &cur->list;
+ list_add(pos, head);
+ ndevs++;
+ continue;
+
+ default:
+ continue;
+ }
+ }
+
+ /*
+ * Take an initial pass through the devices lying behind our host
+ * bridge. Make sure each one is actually an AGP 3.0 device, otherwise
+ * exit with an error message. Along the way store the AGP 3.0
+ * cap_ptr for each device
+ */
+ list_for_each(pos, head) {
+ cur = list_entry(pos, struct agp_3_5_dev, list);
+ dev = cur->dev;
+
+ pci_read_config_word(dev, PCI_STATUS, &mpstat);
+ if ((mpstat & PCI_STATUS_CAP_LIST) == 0)
+ continue;
+
+ pci_read_config_byte(dev, PCI_CAPABILITY_LIST, &mcapndx);
+ if (mcapndx != 0) {
+ do {
+ pci_read_config_dword(dev, mcapndx, &ncapid);
+ if ((ncapid & 0xff) != 2)
+ mcapndx = (ncapid >> 8) & 0xff;
+ }
+ while (((ncapid & 0xff) != 2) && (mcapndx != 0));
+ }
+
+ if (mcapndx == 0) {
+ printk(KERN_ERR PFX "woah! Non-AGP device "
+ "found on the secondary bus of an AGP 3.5 bridge!\n");
+ ret = -ENODEV;
+ goto free_and_exit;
+ }
+
+ mmajor = (ncapid >> AGP_MAJOR_VERSION_SHIFT) & 0xf;
+ if (mmajor < 3) {
+ printk(KERN_ERR PFX "woah! AGP 2.0 device "
+ "found on the secondary bus of an AGP 3.5 "
+ "bridge operating with AGP 3.0 electricals!\n");
+ ret = -ENODEV;
+ goto free_and_exit;
+ }
+
+ cur->capndx = mcapndx;
+
+ pci_read_config_dword(dev, cur->capndx+AGPSTAT, &mstatus);
+
+ if (((mstatus >> 3) & 0x1) == 0) {
+ printk(KERN_ERR PFX "woah! AGP 3.x device "
+ "not operating in AGP 3.x mode found on the "
+ "secondary bus of an AGP 3.5 bridge operating "
+ "with AGP 3.0 electricals!\n");
+ ret = -ENODEV;
+ goto free_and_exit;
+ }
+ }
+
+ /*
+ * Call functions to divide target resources amongst the AGP 3.0
+ * masters. This process is dramatically different depending on
+ * whether isochronous transfers are supported.
+ */
+ if (isoch) {
+ ret = agp_3_5_isochronous_node_enable(bridge, dev_list, ndevs);
+ if (ret) {
+ printk(KERN_INFO PFX "Something bad happened setting "
+ "up isochronous xfers. Falling back to "
+ "non-isochronous xfer mode.\n");
+ } else {
+ goto free_and_exit;
+ }
+ }
+ agp_3_5_nonisochronous_node_enable(bridge, dev_list, ndevs);
+
+free_and_exit:
+ /* Be sure to free the dev_list */
+ for (pos=head->next; pos!=head; ) {
+ cur = list_entry(pos, struct agp_3_5_dev, list);
+
+ pos = pos->next;
+ kfree(cur);
+ }
+ kfree(dev_list);
+
+get_out:
+ return ret;
+}
+
diff --git a/drivers/char/agp/nvidia-agp.c b/drivers/char/agp/nvidia-agp.c
new file mode 100644
index 00000000000..4f7a3e8bc91
--- /dev/null
+++ b/drivers/char/agp/nvidia-agp.c
@@ -0,0 +1,424 @@
+/*
+ * Nvidia AGPGART routines.
+ * Based upon a 2.4 agpgart diff by the folks from NVIDIA, and hacked up
+ * to work in 2.5 by Dave Jones <davej@codemonkey.org.uk>
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/agp_backend.h>
+#include <linux/gfp.h>
+#include <linux/page-flags.h>
+#include <linux/mm.h>
+#include "agp.h"
+
+/* NVIDIA registers */
+#define NVIDIA_0_APSIZE 0x80
+#define NVIDIA_1_WBC 0xf0
+#define NVIDIA_2_GARTCTRL 0xd0
+#define NVIDIA_2_APBASE 0xd8
+#define NVIDIA_2_APLIMIT 0xdc
+#define NVIDIA_2_ATTBASE(i) (0xe0 + (i) * 4)
+#define NVIDIA_3_APBASE 0x50
+#define NVIDIA_3_APLIMIT 0x54
+
+
+static struct _nvidia_private {
+ struct pci_dev *dev_1;
+ struct pci_dev *dev_2;
+ struct pci_dev *dev_3;
+ volatile u32 __iomem *aperture;
+ int num_active_entries;
+ off_t pg_offset;
+ u32 wbc_mask;
+} nvidia_private;
+
+
+static int nvidia_fetch_size(void)
+{
+ int i;
+ u8 size_value;
+ struct aper_size_info_8 *values;
+
+ pci_read_config_byte(agp_bridge->dev, NVIDIA_0_APSIZE, &size_value);
+ size_value &= 0x0f;
+ values = A_SIZE_8(agp_bridge->driver->aperture_sizes);
+
+ for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++) {
+ if (size_value == values[i].size_value) {
+ agp_bridge->previous_size =
+ agp_bridge->current_size = (void *) (values + i);
+ agp_bridge->aperture_size_idx = i;
+ return values[i].size;
+ }
+ }
+
+ return 0;
+}
+
+#define SYSCFG 0xC0010010
+#define IORR_BASE0 0xC0010016
+#define IORR_MASK0 0xC0010017
+#define AMD_K7_NUM_IORR 2
+
+static int nvidia_init_iorr(u32 base, u32 size)
+{
+ u32 base_hi, base_lo;
+ u32 mask_hi, mask_lo;
+ u32 sys_hi, sys_lo;
+ u32 iorr_addr, free_iorr_addr;
+
+ /* Find the iorr that is already used for the base */
+ /* If not found, determine the uppermost available iorr */
+ free_iorr_addr = AMD_K7_NUM_IORR;
+ for(iorr_addr = 0; iorr_addr < AMD_K7_NUM_IORR; iorr_addr++) {
+ rdmsr(IORR_BASE0 + 2 * iorr_addr, base_lo, base_hi);
+ rdmsr(IORR_MASK0 + 2 * iorr_addr, mask_lo, mask_hi);
+
+ if ((base_lo & 0xfffff000) == (base & 0xfffff000))
+ break;
+
+ if ((mask_lo & 0x00000800) == 0)
+ free_iorr_addr = iorr_addr;
+ }
+
+ if (iorr_addr >= AMD_K7_NUM_IORR) {
+ iorr_addr = free_iorr_addr;
+ if (iorr_addr >= AMD_K7_NUM_IORR)
+ return -EINVAL;
+ }
+ base_hi = 0x0;
+ base_lo = (base & ~0xfff) | 0x18;
+ mask_hi = 0xf;
+ mask_lo = ((~(size - 1)) & 0xfffff000) | 0x800;
+ wrmsr(IORR_BASE0 + 2 * iorr_addr, base_lo, base_hi);
+ wrmsr(IORR_MASK0 + 2 * iorr_addr, mask_lo, mask_hi);
+
+ rdmsr(SYSCFG, sys_lo, sys_hi);
+ sys_lo |= 0x00100000;
+ wrmsr(SYSCFG, sys_lo, sys_hi);
+
+ return 0;
+}
+
+static int nvidia_configure(void)
+{
+ int i, rc, num_dirs;
+ u32 apbase, aplimit;
+ struct aper_size_info_8 *current_size;
+ u32 temp;
+
+ current_size = A_SIZE_8(agp_bridge->current_size);
+
+ /* aperture size */
+ pci_write_config_byte(agp_bridge->dev, NVIDIA_0_APSIZE,
+ current_size->size_value);
+
+ /* address to map to */
+ pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &apbase);
+ apbase &= PCI_BASE_ADDRESS_MEM_MASK;
+ agp_bridge->gart_bus_addr = apbase;
+ aplimit = apbase + (current_size->size * 1024 * 1024) - 1;
+ pci_write_config_dword(nvidia_private.dev_2, NVIDIA_2_APBASE, apbase);
+ pci_write_config_dword(nvidia_private.dev_2, NVIDIA_2_APLIMIT, aplimit);
+ pci_write_config_dword(nvidia_private.dev_3, NVIDIA_3_APBASE, apbase);
+ pci_write_config_dword(nvidia_private.dev_3, NVIDIA_3_APLIMIT, aplimit);
+ if (0 != (rc = nvidia_init_iorr(apbase, current_size->size * 1024 * 1024)))
+ return rc;
+
+ /* directory size is 64k */
+ num_dirs = current_size->size / 64;
+ nvidia_private.num_active_entries = current_size->num_entries;
+ nvidia_private.pg_offset = 0;
+ if (num_dirs == 0) {
+ num_dirs = 1;
+ nvidia_private.num_active_entries /= (64 / current_size->size);
+ nvidia_private.pg_offset = (apbase & (64 * 1024 * 1024 - 1) &
+ ~(current_size->size * 1024 * 1024 - 1)) / PAGE_SIZE;
+ }
+
+ /* attbase */
+ for(i = 0; i < 8; i++) {
+ pci_write_config_dword(nvidia_private.dev_2, NVIDIA_2_ATTBASE(i),
+ (agp_bridge->gatt_bus_addr + (i % num_dirs) * 64 * 1024) | 1);
+ }
+
+ /* gtlb control */
+ pci_read_config_dword(nvidia_private.dev_2, NVIDIA_2_GARTCTRL, &temp);
+ pci_write_config_dword(nvidia_private.dev_2, NVIDIA_2_GARTCTRL, temp | 0x11);
+
+ /* gart control */
+ pci_read_config_dword(agp_bridge->dev, NVIDIA_0_APSIZE, &temp);
+ pci_write_config_dword(agp_bridge->dev, NVIDIA_0_APSIZE, temp | 0x100);
+
+ /* map aperture */
+ nvidia_private.aperture =
+ (volatile u32 __iomem *) ioremap(apbase, 33 * PAGE_SIZE);
+
+ return 0;
+}
+
+static void nvidia_cleanup(void)
+{
+ struct aper_size_info_8 *previous_size;
+ u32 temp;
+
+ /* gart control */
+ pci_read_config_dword(agp_bridge->dev, NVIDIA_0_APSIZE, &temp);
+ pci_write_config_dword(agp_bridge->dev, NVIDIA_0_APSIZE, temp & ~(0x100));
+
+ /* gtlb control */
+ pci_read_config_dword(nvidia_private.dev_2, NVIDIA_2_GARTCTRL, &temp);
+ pci_write_config_dword(nvidia_private.dev_2, NVIDIA_2_GARTCTRL, temp & ~(0x11));
+
+ /* unmap aperture */
+ iounmap((void __iomem *) nvidia_private.aperture);
+
+ /* restore previous aperture size */
+ previous_size = A_SIZE_8(agp_bridge->previous_size);
+ pci_write_config_byte(agp_bridge->dev, NVIDIA_0_APSIZE,
+ previous_size->size_value);
+
+ /* restore iorr for previous aperture size */
+ nvidia_init_iorr(agp_bridge->gart_bus_addr,
+ previous_size->size * 1024 * 1024);
+}
+
+
+/*
+ * Note we can't use the generic routines, even though they are 99% the same.
+ * Aperture sizes <64M still requires a full 64k GART directory, but
+ * only use the portion of the TLB entries that correspond to the apertures
+ * alignment inside the surrounding 64M block.
+ */
+extern int agp_memory_reserved;
+
+static int nvidia_insert_memory(struct agp_memory *mem, off_t pg_start, int type)
+{
+ int i, j;
+
+ if ((type != 0) || (mem->type != 0))
+ return -EINVAL;
+
+ if ((pg_start + mem->page_count) >
+ (nvidia_private.num_active_entries - agp_memory_reserved/PAGE_SIZE))
+ return -EINVAL;
+
+ for(j = pg_start; j < (pg_start + mem->page_count); j++) {
+ if (!PGE_EMPTY(agp_bridge, readl(agp_bridge->gatt_table+nvidia_private.pg_offset+j)))
+ return -EBUSY;
+ }
+
+ if (mem->is_flushed == FALSE) {
+ global_cache_flush();
+ mem->is_flushed = TRUE;
+ }
+ for (i = 0, j = pg_start; i < mem->page_count; i++, j++) {
+ writel(agp_bridge->driver->mask_memory(agp_bridge,
+ mem->memory[i], mem->type),
+ agp_bridge->gatt_table+nvidia_private.pg_offset+j);
+ readl(agp_bridge->gatt_table+nvidia_private.pg_offset+j); /* PCI Posting. */
+ }
+ agp_bridge->driver->tlb_flush(mem);
+ return 0;
+}
+
+
+static int nvidia_remove_memory(struct agp_memory *mem, off_t pg_start, int type)
+{
+ int i;
+
+ if ((type != 0) || (mem->type != 0))
+ return -EINVAL;
+
+ for (i = pg_start; i < (mem->page_count + pg_start); i++)
+ writel(agp_bridge->scratch_page, agp_bridge->gatt_table+nvidia_private.pg_offset+i);
+
+ agp_bridge->driver->tlb_flush(mem);
+ return 0;
+}
+
+
+static void nvidia_tlbflush(struct agp_memory *mem)
+{
+ unsigned long end;
+ u32 wbc_reg, temp;
+ int i;
+
+ /* flush chipset */
+ if (nvidia_private.wbc_mask) {
+ pci_read_config_dword(nvidia_private.dev_1, NVIDIA_1_WBC, &wbc_reg);
+ wbc_reg |= nvidia_private.wbc_mask;
+ pci_write_config_dword(nvidia_private.dev_1, NVIDIA_1_WBC, wbc_reg);
+
+ end = jiffies + 3*HZ;
+ do {
+ pci_read_config_dword(nvidia_private.dev_1,
+ NVIDIA_1_WBC, &wbc_reg);
+ if ((signed)(end - jiffies) <= 0) {
+ printk(KERN_ERR PFX
+ "TLB flush took more than 3 seconds.\n");
+ }
+ } while (wbc_reg & nvidia_private.wbc_mask);
+ }
+
+ /* flush TLB entries */
+ for(i = 0; i < 32 + 1; i++)
+ temp = readl(nvidia_private.aperture+(i * PAGE_SIZE / sizeof(u32)));
+ for(i = 0; i < 32 + 1; i++)
+ temp = readl(nvidia_private.aperture+(i * PAGE_SIZE / sizeof(u32)));
+}
+
+
+static struct aper_size_info_8 nvidia_generic_sizes[5] =
+{
+ {512, 131072, 7, 0},
+ {256, 65536, 6, 8},
+ {128, 32768, 5, 12},
+ {64, 16384, 4, 14},
+ /* The 32M mode still requires a 64k gatt */
+ {32, 16384, 4, 15}
+};
+
+
+static struct gatt_mask nvidia_generic_masks[] =
+{
+ { .mask = 1, .type = 0}
+};
+
+
+struct agp_bridge_driver nvidia_driver = {
+ .owner = THIS_MODULE,
+ .aperture_sizes = nvidia_generic_sizes,
+ .size_type = U8_APER_SIZE,
+ .num_aperture_sizes = 5,
+ .configure = nvidia_configure,
+ .fetch_size = nvidia_fetch_size,
+ .cleanup = nvidia_cleanup,
+ .tlb_flush = nvidia_tlbflush,
+ .mask_memory = agp_generic_mask_memory,
+ .masks = nvidia_generic_masks,
+ .agp_enable = agp_generic_enable,
+ .cache_flush = global_cache_flush,
+ .create_gatt_table = agp_generic_create_gatt_table,
+ .free_gatt_table = agp_generic_free_gatt_table,
+ .insert_memory = nvidia_insert_memory,
+ .remove_memory = nvidia_remove_memory,
+ .alloc_by_type = agp_generic_alloc_by_type,
+ .free_by_type = agp_generic_free_by_type,
+ .agp_alloc_page = agp_generic_alloc_page,
+ .agp_destroy_page = agp_generic_destroy_page,
+};
+
+static int __devinit agp_nvidia_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct agp_bridge_data *bridge;
+ u8 cap_ptr;
+
+ nvidia_private.dev_1 =
+ pci_find_slot((unsigned int)pdev->bus->number, PCI_DEVFN(0, 1));
+ nvidia_private.dev_2 =
+ pci_find_slot((unsigned int)pdev->bus->number, PCI_DEVFN(0, 2));
+ nvidia_private.dev_3 =
+ pci_find_slot((unsigned int)pdev->bus->number, PCI_DEVFN(30, 0));
+
+ if (!nvidia_private.dev_1 || !nvidia_private.dev_2 || !nvidia_private.dev_3) {
+ printk(KERN_INFO PFX "Detected an NVIDIA nForce/nForce2 "
+ "chipset, but could not find the secondary devices.\n");
+ return -ENODEV;
+ }
+
+ cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP);
+ if (!cap_ptr)
+ return -ENODEV;
+
+ switch (pdev->device) {
+ case PCI_DEVICE_ID_NVIDIA_NFORCE:
+ printk(KERN_INFO PFX "Detected NVIDIA nForce chipset\n");
+ nvidia_private.wbc_mask = 0x00010000;
+ break;
+ case PCI_DEVICE_ID_NVIDIA_NFORCE2:
+ printk(KERN_INFO PFX "Detected NVIDIA nForce2 chipset\n");
+ nvidia_private.wbc_mask = 0x80000000;
+ break;
+ default:
+ printk(KERN_ERR PFX "Unsupported NVIDIA chipset (device id: %04x)\n",
+ pdev->device);
+ return -ENODEV;
+ }
+
+ bridge = agp_alloc_bridge();
+ if (!bridge)
+ return -ENOMEM;
+
+ bridge->driver = &nvidia_driver;
+ bridge->dev_private_data = &nvidia_private,
+ bridge->dev = pdev;
+ bridge->capndx = cap_ptr;
+
+ /* Fill in the mode register */
+ pci_read_config_dword(pdev,
+ bridge->capndx+PCI_AGP_STATUS,
+ &bridge->mode);
+
+ pci_set_drvdata(pdev, bridge);
+ return agp_add_bridge(bridge);
+}
+
+static void __devexit agp_nvidia_remove(struct pci_dev *pdev)
+{
+ struct agp_bridge_data *bridge = pci_get_drvdata(pdev);
+
+ agp_remove_bridge(bridge);
+ agp_put_bridge(bridge);
+}
+
+static struct pci_device_id agp_nvidia_pci_table[] = {
+ {
+ .class = (PCI_CLASS_BRIDGE_HOST << 8),
+ .class_mask = ~0,
+ .vendor = PCI_VENDOR_ID_NVIDIA,
+ .device = PCI_DEVICE_ID_NVIDIA_NFORCE,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ },
+ {
+ .class = (PCI_CLASS_BRIDGE_HOST << 8),
+ .class_mask = ~0,
+ .vendor = PCI_VENDOR_ID_NVIDIA,
+ .device = PCI_DEVICE_ID_NVIDIA_NFORCE2,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ },
+ { }
+};
+
+MODULE_DEVICE_TABLE(pci, agp_nvidia_pci_table);
+
+static struct pci_driver agp_nvidia_pci_driver = {
+ .name = "agpgart-nvidia",
+ .id_table = agp_nvidia_pci_table,
+ .probe = agp_nvidia_probe,
+ .remove = agp_nvidia_remove,
+};
+
+static int __init agp_nvidia_init(void)
+{
+ if (agp_off)
+ return -EINVAL;
+ return pci_register_driver(&agp_nvidia_pci_driver);
+}
+
+static void __exit agp_nvidia_cleanup(void)
+{
+ pci_unregister_driver(&agp_nvidia_pci_driver);
+}
+
+module_init(agp_nvidia_init);
+module_exit(agp_nvidia_cleanup);
+
+MODULE_LICENSE("GPL and additional rights");
+MODULE_AUTHOR("NVIDIA Corporation");
+
diff --git a/drivers/char/agp/sgi-agp.c b/drivers/char/agp/sgi-agp.c
new file mode 100644
index 00000000000..4b3eda26797
--- /dev/null
+++ b/drivers/char/agp/sgi-agp.c
@@ -0,0 +1,331 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2003-2005 Silicon Graphics, Inc. All Rights Reserved.
+ */
+
+/*
+ * SGI TIOCA AGPGART routines.
+ *
+ */
+
+#include <linux/acpi.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/agp_backend.h>
+#include <asm/sn/addrs.h>
+#include <asm/sn/pcidev.h>
+#include <asm/sn/pcibus_provider_defs.h>
+#include <asm/sn/tioca_provider.h>
+#include "agp.h"
+
+extern int agp_memory_reserved;
+extern uint32_t tioca_gart_found;
+extern struct list_head tioca_list;
+static struct agp_bridge_data **sgi_tioca_agp_bridges;
+
+/*
+ * The aperature size and related information is set up at TIOCA init time.
+ * Values for this table will be extracted and filled in at
+ * sgi_tioca_fetch_size() time.
+ */
+
+static struct aper_size_info_fixed sgi_tioca_sizes[] = {
+ {0, 0, 0},
+};
+
+static void *sgi_tioca_alloc_page(struct agp_bridge_data *bridge)
+{
+ struct page *page;
+ int nid;
+ struct tioca_kernel *info =
+ (struct tioca_kernel *)bridge->dev_private_data;
+
+ nid = info->ca_closest_node;
+ page = alloc_pages_node(nid, GFP_KERNEL, 0);
+ if (page == NULL) {
+ return 0;
+ }
+
+ get_page(page);
+ SetPageLocked(page);
+ atomic_inc(&agp_bridge->current_memory_agp);
+ return page_address(page);
+}
+
+/*
+ * Flush GART tlb's. Cannot selectively flush based on memory so the mem
+ * arg is ignored.
+ */
+
+static void sgi_tioca_tlbflush(struct agp_memory *mem)
+{
+ tioca_tlbflush(mem->bridge->dev_private_data);
+}
+
+/*
+ * Given an address of a host physical page, turn it into a valid gart
+ * entry.
+ */
+static unsigned long
+sgi_tioca_mask_memory(struct agp_bridge_data *bridge,
+ unsigned long addr, int type)
+{
+ return tioca_physpage_to_gart(addr);
+}
+
+static void sgi_tioca_agp_enable(struct agp_bridge_data *bridge, u32 mode)
+{
+ tioca_fastwrite_enable(bridge->dev_private_data);
+}
+
+/*
+ * sgi_tioca_configure() doesn't have anything to do since the base CA driver
+ * has alreay set up the GART.
+ */
+
+static int sgi_tioca_configure(void)
+{
+ return 0;
+}
+
+/*
+ * Determine gfx aperature size. This has already been determined by the
+ * CA driver init, so just need to set agp_bridge values accordingly.
+ */
+
+static int sgi_tioca_fetch_size(void)
+{
+ struct tioca_kernel *info =
+ (struct tioca_kernel *)agp_bridge->dev_private_data;
+
+ sgi_tioca_sizes[0].size = info->ca_gfxap_size / MB(1);
+ sgi_tioca_sizes[0].num_entries = info->ca_gfxgart_entries;
+
+ return sgi_tioca_sizes[0].size;
+}
+
+static int sgi_tioca_create_gatt_table(struct agp_bridge_data *bridge)
+{
+ struct tioca_kernel *info =
+ (struct tioca_kernel *)bridge->dev_private_data;
+
+ bridge->gatt_table_real = (u32 *) info->ca_gfxgart;
+ bridge->gatt_table = bridge->gatt_table_real;
+ bridge->gatt_bus_addr = info->ca_gfxgart_base;
+
+ return 0;
+}
+
+static int sgi_tioca_free_gatt_table(struct agp_bridge_data *bridge)
+{
+ return 0;
+}
+
+static int sgi_tioca_insert_memory(struct agp_memory *mem, off_t pg_start,
+ int type)
+{
+ int num_entries;
+ size_t i;
+ off_t j;
+ void *temp;
+ struct agp_bridge_data *bridge;
+
+ bridge = mem->bridge;
+ if (!bridge)
+ return -EINVAL;
+
+ temp = bridge->current_size;
+
+ switch (bridge->driver->size_type) {
+ case U8_APER_SIZE:
+ num_entries = A_SIZE_8(temp)->num_entries;
+ break;
+ case U16_APER_SIZE:
+ num_entries = A_SIZE_16(temp)->num_entries;
+ break;
+ case U32_APER_SIZE:
+ num_entries = A_SIZE_32(temp)->num_entries;
+ break;
+ case FIXED_APER_SIZE:
+ num_entries = A_SIZE_FIX(temp)->num_entries;
+ break;
+ case LVL2_APER_SIZE:
+ return -EINVAL;
+ break;
+ default:
+ num_entries = 0;
+ break;
+ }
+
+ num_entries -= agp_memory_reserved / PAGE_SIZE;
+ if (num_entries < 0)
+ num_entries = 0;
+
+ if (type != 0 || mem->type != 0) {
+ return -EINVAL;
+ }
+
+ if ((pg_start + mem->page_count) > num_entries)
+ return -EINVAL;
+
+ j = pg_start;
+
+ while (j < (pg_start + mem->page_count)) {
+ if (*(bridge->gatt_table + j))
+ return -EBUSY;
+ j++;
+ }
+
+ if (mem->is_flushed == FALSE) {
+ bridge->driver->cache_flush();
+ mem->is_flushed = TRUE;
+ }
+
+ for (i = 0, j = pg_start; i < mem->page_count; i++, j++) {
+ *(bridge->gatt_table + j) =
+ bridge->driver->mask_memory(bridge, mem->memory[i],
+ mem->type);
+ }
+
+ bridge->driver->tlb_flush(mem);
+ return 0;
+}
+
+static int sgi_tioca_remove_memory(struct agp_memory *mem, off_t pg_start,
+ int type)
+{
+ size_t i;
+ struct agp_bridge_data *bridge;
+
+ bridge = mem->bridge;
+ if (!bridge)
+ return -EINVAL;
+
+ if (type != 0 || mem->type != 0) {
+ return -EINVAL;
+ }
+
+ for (i = pg_start; i < (mem->page_count + pg_start); i++) {
+ *(bridge->gatt_table + i) = 0;
+ }
+
+ bridge->driver->tlb_flush(mem);
+ return 0;
+}
+
+static void sgi_tioca_cache_flush(void)
+{
+}
+
+/*
+ * Cleanup. Nothing to do as the CA driver owns the GART.
+ */
+
+static void sgi_tioca_cleanup(void)
+{
+}
+
+static struct agp_bridge_data *sgi_tioca_find_bridge(struct pci_dev *pdev)
+{
+ struct agp_bridge_data *bridge;
+
+ list_for_each_entry(bridge, &agp_bridges, list) {
+ if (bridge->dev->bus == pdev->bus)
+ break;
+ }
+ return bridge;
+}
+
+struct agp_bridge_driver sgi_tioca_driver = {
+ .owner = THIS_MODULE,
+ .size_type = U16_APER_SIZE,
+ .configure = sgi_tioca_configure,
+ .fetch_size = sgi_tioca_fetch_size,
+ .cleanup = sgi_tioca_cleanup,
+ .tlb_flush = sgi_tioca_tlbflush,
+ .mask_memory = sgi_tioca_mask_memory,
+ .agp_enable = sgi_tioca_agp_enable,
+ .cache_flush = sgi_tioca_cache_flush,
+ .create_gatt_table = sgi_tioca_create_gatt_table,
+ .free_gatt_table = sgi_tioca_free_gatt_table,
+ .insert_memory = sgi_tioca_insert_memory,
+ .remove_memory = sgi_tioca_remove_memory,
+ .alloc_by_type = agp_generic_alloc_by_type,
+ .free_by_type = agp_generic_free_by_type,
+ .agp_alloc_page = sgi_tioca_alloc_page,
+ .agp_destroy_page = agp_generic_destroy_page,
+ .cant_use_aperture = 1,
+ .needs_scratch_page = 0,
+ .num_aperture_sizes = 1,
+};
+
+static int __devinit agp_sgi_init(void)
+{
+ unsigned int j;
+ struct tioca_kernel *info;
+ struct pci_dev *pdev = NULL;
+
+ if (tioca_gart_found)
+ printk(KERN_INFO PFX "SGI TIO CA GART driver initialized.\n");
+ else
+ return 0;
+
+ sgi_tioca_agp_bridges =
+ (struct agp_bridge_data **)kmalloc(tioca_gart_found *
+ sizeof(struct agp_bridge_data *),
+ GFP_KERNEL);
+
+ j = 0;
+ list_for_each_entry(info, &tioca_list, ca_list) {
+ struct list_head *tmp;
+ list_for_each(tmp, info->ca_devices) {
+ u8 cap_ptr;
+ pdev = pci_dev_b(tmp);
+ if (pdev->class != (PCI_CLASS_DISPLAY_VGA << 8))
+ continue;
+ cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP);
+ if (!cap_ptr)
+ continue;
+ }
+ sgi_tioca_agp_bridges[j] = agp_alloc_bridge();
+ printk(KERN_INFO PFX "bridge %d = 0x%p\n", j,
+ sgi_tioca_agp_bridges[j]);
+ if (sgi_tioca_agp_bridges[j]) {
+ sgi_tioca_agp_bridges[j]->dev = pdev;
+ sgi_tioca_agp_bridges[j]->dev_private_data = info;
+ sgi_tioca_agp_bridges[j]->driver = &sgi_tioca_driver;
+ sgi_tioca_agp_bridges[j]->gart_bus_addr =
+ info->ca_gfxap_base;
+ sgi_tioca_agp_bridges[j]->mode = (0x7D << 24) | /* 126 requests */
+ (0x1 << 9) | /* SBA supported */
+ (0x1 << 5) | /* 64-bit addresses supported */
+ (0x1 << 4) | /* FW supported */
+ (0x1 << 3) | /* AGP 3.0 mode */
+ 0x2; /* 8x transfer only */
+ sgi_tioca_agp_bridges[j]->current_size =
+ sgi_tioca_agp_bridges[j]->previous_size =
+ (void *)&sgi_tioca_sizes[0];
+ agp_add_bridge(sgi_tioca_agp_bridges[j]);
+ }
+ j++;
+ }
+
+ agp_find_bridge = &sgi_tioca_find_bridge;
+ return 0;
+}
+
+static void __devexit agp_sgi_cleanup(void)
+{
+ if(sgi_tioca_agp_bridges)
+ kfree(sgi_tioca_agp_bridges);
+ sgi_tioca_agp_bridges=NULL;
+}
+
+module_init(agp_sgi_init);
+module_exit(agp_sgi_cleanup);
+
+MODULE_LICENSE("GPL and additional rights");
diff --git a/drivers/char/agp/sis-agp.c b/drivers/char/agp/sis-agp.c
new file mode 100644
index 00000000000..cfccacb2a64
--- /dev/null
+++ b/drivers/char/agp/sis-agp.c
@@ -0,0 +1,360 @@
+/*
+ * SiS AGPGART routines.
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/agp_backend.h>
+#include <linux/delay.h>
+#include "agp.h"
+
+#define SIS_ATTBASE 0x90
+#define SIS_APSIZE 0x94
+#define SIS_TLBCNTRL 0x97
+#define SIS_TLBFLUSH 0x98
+
+static int __devinitdata agp_sis_force_delay = 0;
+static int __devinitdata agp_sis_agp_spec = -1;
+
+static int sis_fetch_size(void)
+{
+ u8 temp_size;
+ int i;
+ struct aper_size_info_8 *values;
+
+ pci_read_config_byte(agp_bridge->dev, SIS_APSIZE, &temp_size);
+ values = A_SIZE_8(agp_bridge->driver->aperture_sizes);
+ for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++) {
+ if ((temp_size == values[i].size_value) ||
+ ((temp_size & ~(0x03)) ==
+ (values[i].size_value & ~(0x03)))) {
+ agp_bridge->previous_size =
+ agp_bridge->current_size = (void *) (values + i);
+
+ agp_bridge->aperture_size_idx = i;
+ return values[i].size;
+ }
+ }
+
+ return 0;
+}
+
+static void sis_tlbflush(struct agp_memory *mem)
+{
+ pci_write_config_byte(agp_bridge->dev, SIS_TLBFLUSH, 0x02);
+}
+
+static int sis_configure(void)
+{
+ u32 temp;
+ struct aper_size_info_8 *current_size;
+
+ current_size = A_SIZE_8(agp_bridge->current_size);
+ pci_write_config_byte(agp_bridge->dev, SIS_TLBCNTRL, 0x05);
+ pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp);
+ agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+ pci_write_config_dword(agp_bridge->dev, SIS_ATTBASE,
+ agp_bridge->gatt_bus_addr);
+ pci_write_config_byte(agp_bridge->dev, SIS_APSIZE,
+ current_size->size_value);
+ return 0;
+}
+
+static void sis_cleanup(void)
+{
+ struct aper_size_info_8 *previous_size;
+
+ previous_size = A_SIZE_8(agp_bridge->previous_size);
+ pci_write_config_byte(agp_bridge->dev, SIS_APSIZE,
+ (previous_size->size_value & ~(0x03)));
+}
+
+static void sis_delayed_enable(struct agp_bridge_data *bridge, u32 mode)
+{
+ struct pci_dev *device = NULL;
+ u32 command;
+ int rate;
+
+ printk(KERN_INFO PFX "Found an AGP %d.%d compliant device at %s.\n",
+ agp_bridge->major_version,
+ agp_bridge->minor_version,
+ pci_name(agp_bridge->dev));
+
+ pci_read_config_dword(agp_bridge->dev, agp_bridge->capndx + PCI_AGP_STATUS, &command);
+ command = agp_collect_device_status(bridge, mode, command);
+ command |= AGPSTAT_AGP_ENABLE;
+ rate = (command & 0x7) << 2;
+
+ for_each_pci_dev(device) {
+ u8 agp = pci_find_capability(device, PCI_CAP_ID_AGP);
+ if (!agp)
+ continue;
+
+ printk(KERN_INFO PFX "Putting AGP V3 device at %s into %dx mode\n",
+ pci_name(device), rate);
+
+ pci_write_config_dword(device, agp + PCI_AGP_COMMAND, command);
+
+ /*
+ * Weird: on some sis chipsets any rate change in the target
+ * command register triggers a 5ms screwup during which the master
+ * cannot be configured
+ */
+ if (device->device == bridge->dev->device) {
+ printk(KERN_INFO PFX "SiS delay workaround: giving bridge time to recover.\n");
+ msleep(10);
+ }
+ }
+}
+
+static struct aper_size_info_8 sis_generic_sizes[7] =
+{
+ {256, 65536, 6, 99},
+ {128, 32768, 5, 83},
+ {64, 16384, 4, 67},
+ {32, 8192, 3, 51},
+ {16, 4096, 2, 35},
+ {8, 2048, 1, 19},
+ {4, 1024, 0, 3}
+};
+
+struct agp_bridge_driver sis_driver = {
+ .owner = THIS_MODULE,
+ .aperture_sizes = sis_generic_sizes,
+ .size_type = U8_APER_SIZE,
+ .num_aperture_sizes = 7,
+ .configure = sis_configure,
+ .fetch_size = sis_fetch_size,
+ .cleanup = sis_cleanup,
+ .tlb_flush = sis_tlbflush,
+ .mask_memory = agp_generic_mask_memory,
+ .masks = NULL,
+ .agp_enable = agp_generic_enable,
+ .cache_flush = global_cache_flush,
+ .create_gatt_table = agp_generic_create_gatt_table,
+ .free_gatt_table = agp_generic_free_gatt_table,
+ .insert_memory = agp_generic_insert_memory,
+ .remove_memory = agp_generic_remove_memory,
+ .alloc_by_type = agp_generic_alloc_by_type,
+ .free_by_type = agp_generic_free_by_type,
+ .agp_alloc_page = agp_generic_alloc_page,
+ .agp_destroy_page = agp_generic_destroy_page,
+};
+
+static struct agp_device_ids sis_agp_device_ids[] __devinitdata =
+{
+ {
+ .device_id = PCI_DEVICE_ID_SI_5591_AGP,
+ .chipset_name = "5591",
+ },
+ {
+ .device_id = PCI_DEVICE_ID_SI_530,
+ .chipset_name = "530",
+ },
+ {
+ .device_id = PCI_DEVICE_ID_SI_540,
+ .chipset_name = "540",
+ },
+ {
+ .device_id = PCI_DEVICE_ID_SI_550,
+ .chipset_name = "550",
+ },
+ {
+ .device_id = PCI_DEVICE_ID_SI_620,
+ .chipset_name = "620",
+ },
+ {
+ .device_id = PCI_DEVICE_ID_SI_630,
+ .chipset_name = "630",
+ },
+ {
+ .device_id = PCI_DEVICE_ID_SI_635,
+ .chipset_name = "635",
+ },
+ {
+ .device_id = PCI_DEVICE_ID_SI_645,
+ .chipset_name = "645",
+ },
+ {
+ .device_id = PCI_DEVICE_ID_SI_646,
+ .chipset_name = "646",
+ },
+ {
+ .device_id = PCI_DEVICE_ID_SI_648,
+ .chipset_name = "648",
+ },
+ {
+ .device_id = PCI_DEVICE_ID_SI_650,
+ .chipset_name = "650",
+ },
+ {
+ .device_id = PCI_DEVICE_ID_SI_651,
+ .chipset_name = "651",
+ },
+ {
+ .device_id = PCI_DEVICE_ID_SI_655,
+ .chipset_name = "655",
+ },
+ {
+ .device_id = PCI_DEVICE_ID_SI_661,
+ .chipset_name = "661",
+ },
+ {
+ .device_id = PCI_DEVICE_ID_SI_730,
+ .chipset_name = "730",
+ },
+ {
+ .device_id = PCI_DEVICE_ID_SI_735,
+ .chipset_name = "735",
+ },
+ {
+ .device_id = PCI_DEVICE_ID_SI_740,
+ .chipset_name = "740",
+ },
+ {
+ .device_id = PCI_DEVICE_ID_SI_741,
+ .chipset_name = "741",
+ },
+ {
+ .device_id = PCI_DEVICE_ID_SI_745,
+ .chipset_name = "745",
+ },
+ {
+ .device_id = PCI_DEVICE_ID_SI_746,
+ .chipset_name = "746",
+ },
+ {
+ .device_id = PCI_DEVICE_ID_SI_760,
+ .chipset_name = "760",
+ },
+ { }, /* dummy final entry, always present */
+};
+
+
+// chipsets that require the 'delay hack'
+static int sis_broken_chipsets[] __devinitdata = {
+ PCI_DEVICE_ID_SI_648,
+ PCI_DEVICE_ID_SI_746,
+ 0 // terminator
+};
+
+static void __devinit sis_get_driver(struct agp_bridge_data *bridge)
+{
+ int i;
+
+ for(i=0; sis_broken_chipsets[i]!=0; ++i)
+ if(bridge->dev->device==sis_broken_chipsets[i])
+ break;
+
+ if(sis_broken_chipsets[i] || agp_sis_force_delay)
+ sis_driver.agp_enable=sis_delayed_enable;
+
+ // sis chipsets that indicate less than agp3.5
+ // are not actually fully agp3 compliant
+ if ((agp_bridge->major_version == 3 && agp_bridge->minor_version >= 5
+ && agp_sis_agp_spec!=0) || agp_sis_agp_spec==1) {
+ sis_driver.aperture_sizes = agp3_generic_sizes;
+ sis_driver.size_type = U16_APER_SIZE;
+ sis_driver.num_aperture_sizes = AGP_GENERIC_SIZES_ENTRIES;
+ sis_driver.configure = agp3_generic_configure;
+ sis_driver.fetch_size = agp3_generic_fetch_size;
+ sis_driver.cleanup = agp3_generic_cleanup;
+ sis_driver.tlb_flush = agp3_generic_tlbflush;
+ }
+}
+
+
+static int __devinit agp_sis_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct agp_device_ids *devs = sis_agp_device_ids;
+ struct agp_bridge_data *bridge;
+ u8 cap_ptr;
+ int j;
+
+ cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP);
+ if (!cap_ptr)
+ return -ENODEV;
+
+ /* probe for known chipsets */
+ for (j = 0; devs[j].chipset_name; j++) {
+ if (pdev->device == devs[j].device_id) {
+ printk(KERN_INFO PFX "Detected SiS %s chipset\n",
+ devs[j].chipset_name);
+ goto found;
+ }
+ }
+
+ printk(KERN_ERR PFX "Unsupported SiS chipset (device id: %04x)\n",
+ pdev->device);
+ return -ENODEV;
+
+found:
+ bridge = agp_alloc_bridge();
+ if (!bridge)
+ return -ENOMEM;
+
+ bridge->driver = &sis_driver;
+ bridge->dev = pdev;
+ bridge->capndx = cap_ptr;
+
+ get_agp_version(bridge);
+
+ /* Fill in the mode register */
+ pci_read_config_dword(pdev, bridge->capndx+PCI_AGP_STATUS, &bridge->mode);
+ sis_get_driver(bridge);
+
+ pci_set_drvdata(pdev, bridge);
+ return agp_add_bridge(bridge);
+}
+
+static void __devexit agp_sis_remove(struct pci_dev *pdev)
+{
+ struct agp_bridge_data *bridge = pci_get_drvdata(pdev);
+
+ agp_remove_bridge(bridge);
+ agp_put_bridge(bridge);
+}
+
+static struct pci_device_id agp_sis_pci_table[] = {
+ {
+ .class = (PCI_CLASS_BRIDGE_HOST << 8),
+ .class_mask = ~0,
+ .vendor = PCI_VENDOR_ID_SI,
+ .device = PCI_ANY_ID,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ },
+ { }
+};
+
+MODULE_DEVICE_TABLE(pci, agp_sis_pci_table);
+
+static struct pci_driver agp_sis_pci_driver = {
+ .name = "agpgart-sis",
+ .id_table = agp_sis_pci_table,
+ .probe = agp_sis_probe,
+ .remove = agp_sis_remove,
+};
+
+static int __init agp_sis_init(void)
+{
+ if (agp_off)
+ return -EINVAL;
+ return pci_register_driver(&agp_sis_pci_driver);
+}
+
+static void __exit agp_sis_cleanup(void)
+{
+ pci_unregister_driver(&agp_sis_pci_driver);
+}
+
+module_init(agp_sis_init);
+module_exit(agp_sis_cleanup);
+
+module_param(agp_sis_force_delay, bool, 0);
+MODULE_PARM_DESC(agp_sis_force_delay,"forces sis delay hack");
+module_param(agp_sis_agp_spec, int, 0);
+MODULE_PARM_DESC(agp_sis_agp_spec,"0=force sis init, 1=force generic agp3 init, default: autodetect");
+MODULE_LICENSE("GPL and additional rights");
diff --git a/drivers/char/agp/sworks-agp.c b/drivers/char/agp/sworks-agp.c
new file mode 100644
index 00000000000..bb338d9134e
--- /dev/null
+++ b/drivers/char/agp/sworks-agp.c
@@ -0,0 +1,556 @@
+/*
+ * Serverworks AGPGART routines.
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/agp_backend.h>
+#include "agp.h"
+
+#define SVWRKS_COMMAND 0x04
+#define SVWRKS_APSIZE 0x10
+#define SVWRKS_MMBASE 0x14
+#define SVWRKS_CACHING 0x4b
+#define SVWRKS_AGP_ENABLE 0x60
+#define SVWRKS_FEATURE 0x68
+
+#define SVWRKS_SIZE_MASK 0xfe000000
+
+/* Memory mapped registers */
+#define SVWRKS_GART_CACHE 0x02
+#define SVWRKS_GATTBASE 0x04
+#define SVWRKS_TLBFLUSH 0x10
+#define SVWRKS_POSTFLUSH 0x14
+#define SVWRKS_DIRFLUSH 0x0c
+
+
+struct serverworks_page_map {
+ unsigned long *real;
+ unsigned long __iomem *remapped;
+};
+
+static struct _serverworks_private {
+ struct pci_dev *svrwrks_dev; /* device one */
+ volatile u8 __iomem *registers;
+ struct serverworks_page_map **gatt_pages;
+ int num_tables;
+ struct serverworks_page_map scratch_dir;
+
+ int gart_addr_ofs;
+ int mm_addr_ofs;
+} serverworks_private;
+
+static int serverworks_create_page_map(struct serverworks_page_map *page_map)
+{
+ int i;
+
+ page_map->real = (unsigned long *) __get_free_page(GFP_KERNEL);
+ if (page_map->real == NULL) {
+ return -ENOMEM;
+ }
+ SetPageReserved(virt_to_page(page_map->real));
+ global_cache_flush();
+ page_map->remapped = ioremap_nocache(virt_to_phys(page_map->real),
+ PAGE_SIZE);
+ if (page_map->remapped == NULL) {
+ ClearPageReserved(virt_to_page(page_map->real));
+ free_page((unsigned long) page_map->real);
+ page_map->real = NULL;
+ return -ENOMEM;
+ }
+ global_cache_flush();
+
+ for(i = 0; i < PAGE_SIZE / sizeof(unsigned long); i++)
+ writel(agp_bridge->scratch_page, page_map->remapped+i);
+
+ return 0;
+}
+
+static void serverworks_free_page_map(struct serverworks_page_map *page_map)
+{
+ iounmap(page_map->remapped);
+ ClearPageReserved(virt_to_page(page_map->real));
+ free_page((unsigned long) page_map->real);
+}
+
+static void serverworks_free_gatt_pages(void)
+{
+ int i;
+ struct serverworks_page_map **tables;
+ struct serverworks_page_map *entry;
+
+ tables = serverworks_private.gatt_pages;
+ for(i = 0; i < serverworks_private.num_tables; i++) {
+ entry = tables[i];
+ if (entry != NULL) {
+ if (entry->real != NULL) {
+ serverworks_free_page_map(entry);
+ }
+ kfree(entry);
+ }
+ }
+ kfree(tables);
+}
+
+static int serverworks_create_gatt_pages(int nr_tables)
+{
+ struct serverworks_page_map **tables;
+ struct serverworks_page_map *entry;
+ int retval = 0;
+ int i;
+
+ tables = kmalloc((nr_tables + 1) * sizeof(struct serverworks_page_map *),
+ GFP_KERNEL);
+ if (tables == NULL) {
+ return -ENOMEM;
+ }
+ memset(tables, 0, sizeof(struct serverworks_page_map *) * (nr_tables + 1));
+ for (i = 0; i < nr_tables; i++) {
+ entry = kmalloc(sizeof(struct serverworks_page_map), GFP_KERNEL);
+ if (entry == NULL) {
+ retval = -ENOMEM;
+ break;
+ }
+ memset(entry, 0, sizeof(struct serverworks_page_map));
+ tables[i] = entry;
+ retval = serverworks_create_page_map(entry);
+ if (retval != 0) break;
+ }
+ serverworks_private.num_tables = nr_tables;
+ serverworks_private.gatt_pages = tables;
+
+ if (retval != 0) serverworks_free_gatt_pages();
+
+ return retval;
+}
+
+#define SVRWRKS_GET_GATT(addr) (serverworks_private.gatt_pages[\
+ GET_PAGE_DIR_IDX(addr)]->remapped)
+
+#ifndef GET_PAGE_DIR_OFF
+#define GET_PAGE_DIR_OFF(addr) (addr >> 22)
+#endif
+
+#ifndef GET_PAGE_DIR_IDX
+#define GET_PAGE_DIR_IDX(addr) (GET_PAGE_DIR_OFF(addr) - \
+ GET_PAGE_DIR_OFF(agp_bridge->gart_bus_addr))
+#endif
+
+#ifndef GET_GATT_OFF
+#define GET_GATT_OFF(addr) ((addr & 0x003ff000) >> 12)
+#endif
+
+static int serverworks_create_gatt_table(struct agp_bridge_data *bridge)
+{
+ struct aper_size_info_lvl2 *value;
+ struct serverworks_page_map page_dir;
+ int retval;
+ u32 temp;
+ int i;
+
+ value = A_SIZE_LVL2(agp_bridge->current_size);
+ retval = serverworks_create_page_map(&page_dir);
+ if (retval != 0) {
+ return retval;
+ }
+ retval = serverworks_create_page_map(&serverworks_private.scratch_dir);
+ if (retval != 0) {
+ serverworks_free_page_map(&page_dir);
+ return retval;
+ }
+ /* Create a fake scratch directory */
+ for(i = 0; i < 1024; i++) {
+ writel(agp_bridge->scratch_page, serverworks_private.scratch_dir.remapped+i);
+ writel(virt_to_phys(serverworks_private.scratch_dir.real) | 1, page_dir.remapped+i);
+ }
+
+ retval = serverworks_create_gatt_pages(value->num_entries / 1024);
+ if (retval != 0) {
+ serverworks_free_page_map(&page_dir);
+ serverworks_free_page_map(&serverworks_private.scratch_dir);
+ return retval;
+ }
+
+ agp_bridge->gatt_table_real = (u32 *)page_dir.real;
+ agp_bridge->gatt_table = (u32 __iomem *)page_dir.remapped;
+ agp_bridge->gatt_bus_addr = virt_to_phys(page_dir.real);
+
+ /* Get the address for the gart region.
+ * This is a bus address even on the alpha, b/c its
+ * used to program the agp master not the cpu
+ */
+
+ pci_read_config_dword(agp_bridge->dev,serverworks_private.gart_addr_ofs,&temp);
+ agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+
+ /* Calculate the agp offset */
+
+ for(i = 0; i < value->num_entries / 1024; i++)
+ writel(virt_to_phys(serverworks_private.gatt_pages[i]->real)|1, page_dir.remapped+i);
+
+ return 0;
+}
+
+static int serverworks_free_gatt_table(struct agp_bridge_data *bridge)
+{
+ struct serverworks_page_map page_dir;
+
+ page_dir.real = (unsigned long *)agp_bridge->gatt_table_real;
+ page_dir.remapped = (unsigned long __iomem *)agp_bridge->gatt_table;
+
+ serverworks_free_gatt_pages();
+ serverworks_free_page_map(&page_dir);
+ serverworks_free_page_map(&serverworks_private.scratch_dir);
+ return 0;
+}
+
+static int serverworks_fetch_size(void)
+{
+ int i;
+ u32 temp;
+ u32 temp2;
+ struct aper_size_info_lvl2 *values;
+
+ values = A_SIZE_LVL2(agp_bridge->driver->aperture_sizes);
+ pci_read_config_dword(agp_bridge->dev,serverworks_private.gart_addr_ofs,&temp);
+ pci_write_config_dword(agp_bridge->dev,serverworks_private.gart_addr_ofs,
+ SVWRKS_SIZE_MASK);
+ pci_read_config_dword(agp_bridge->dev,serverworks_private.gart_addr_ofs,&temp2);
+ pci_write_config_dword(agp_bridge->dev,serverworks_private.gart_addr_ofs,temp);
+ temp2 &= SVWRKS_SIZE_MASK;
+
+ for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++) {
+ if (temp2 == values[i].size_value) {
+ agp_bridge->previous_size =
+ agp_bridge->current_size = (void *) (values + i);
+
+ agp_bridge->aperture_size_idx = i;
+ return values[i].size;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * This routine could be implemented by taking the addresses
+ * written to the GATT, and flushing them individually. However
+ * currently it just flushes the whole table. Which is probably
+ * more efficent, since agp_memory blocks can be a large number of
+ * entries.
+ */
+static void serverworks_tlbflush(struct agp_memory *temp)
+{
+ writeb(1, serverworks_private.registers+SVWRKS_POSTFLUSH);
+ while (readb(serverworks_private.registers+SVWRKS_POSTFLUSH) == 1)
+ cpu_relax();
+
+ writel(1, serverworks_private.registers+SVWRKS_DIRFLUSH);
+ while(readl(serverworks_private.registers+SVWRKS_DIRFLUSH) == 1)
+ cpu_relax();
+}
+
+static int serverworks_configure(void)
+{
+ struct aper_size_info_lvl2 *current_size;
+ u32 temp;
+ u8 enable_reg;
+ u16 cap_reg;
+
+ current_size = A_SIZE_LVL2(agp_bridge->current_size);
+
+ /* Get the memory mapped registers */
+ pci_read_config_dword(agp_bridge->dev, serverworks_private.mm_addr_ofs, &temp);
+ temp = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+ serverworks_private.registers = (volatile u8 __iomem *) ioremap(temp, 4096);
+ if (!serverworks_private.registers) {
+ printk (KERN_ERR PFX "Unable to ioremap() memory.\n");
+ return -ENOMEM;
+ }
+
+ writeb(0xA, serverworks_private.registers+SVWRKS_GART_CACHE);
+ readb(serverworks_private.registers+SVWRKS_GART_CACHE); /* PCI Posting. */
+
+ writel(agp_bridge->gatt_bus_addr, serverworks_private.registers+SVWRKS_GATTBASE);
+ readl(serverworks_private.registers+SVWRKS_GATTBASE); /* PCI Posting. */
+
+ cap_reg = readw(serverworks_private.registers+SVWRKS_COMMAND);
+ cap_reg &= ~0x0007;
+ cap_reg |= 0x4;
+ writew(cap_reg, serverworks_private.registers+SVWRKS_COMMAND);
+ readw(serverworks_private.registers+SVWRKS_COMMAND);
+
+ pci_read_config_byte(serverworks_private.svrwrks_dev,SVWRKS_AGP_ENABLE, &enable_reg);
+ enable_reg |= 0x1; /* Agp Enable bit */
+ pci_write_config_byte(serverworks_private.svrwrks_dev,SVWRKS_AGP_ENABLE, enable_reg);
+ serverworks_tlbflush(NULL);
+
+ agp_bridge->capndx = pci_find_capability(serverworks_private.svrwrks_dev, PCI_CAP_ID_AGP);
+
+ /* Fill in the mode register */
+ pci_read_config_dword(serverworks_private.svrwrks_dev,
+ agp_bridge->capndx+PCI_AGP_STATUS, &agp_bridge->mode);
+
+ pci_read_config_byte(agp_bridge->dev, SVWRKS_CACHING, &enable_reg);
+ enable_reg &= ~0x3;
+ pci_write_config_byte(agp_bridge->dev, SVWRKS_CACHING, enable_reg);
+
+ pci_read_config_byte(agp_bridge->dev, SVWRKS_FEATURE, &enable_reg);
+ enable_reg |= (1<<6);
+ pci_write_config_byte(agp_bridge->dev,SVWRKS_FEATURE, enable_reg);
+
+ return 0;
+}
+
+static void serverworks_cleanup(void)
+{
+ iounmap((void __iomem *) serverworks_private.registers);
+}
+
+static int serverworks_insert_memory(struct agp_memory *mem,
+ off_t pg_start, int type)
+{
+ int i, j, num_entries;
+ unsigned long __iomem *cur_gatt;
+ unsigned long addr;
+
+ num_entries = A_SIZE_LVL2(agp_bridge->current_size)->num_entries;
+
+ if (type != 0 || mem->type != 0) {
+ return -EINVAL;
+ }
+ if ((pg_start + mem->page_count) > num_entries) {
+ return -EINVAL;
+ }
+
+ j = pg_start;
+ while (j < (pg_start + mem->page_count)) {
+ addr = (j * PAGE_SIZE) + agp_bridge->gart_bus_addr;
+ cur_gatt = SVRWRKS_GET_GATT(addr);
+ if (!PGE_EMPTY(agp_bridge, readl(cur_gatt+GET_GATT_OFF(addr))))
+ return -EBUSY;
+ j++;
+ }
+
+ if (mem->is_flushed == FALSE) {
+ global_cache_flush();
+ mem->is_flushed = TRUE;
+ }
+
+ for (i = 0, j = pg_start; i < mem->page_count; i++, j++) {
+ addr = (j * PAGE_SIZE) + agp_bridge->gart_bus_addr;
+ cur_gatt = SVRWRKS_GET_GATT(addr);
+ writel(agp_bridge->driver->mask_memory(agp_bridge, mem->memory[i], mem->type), cur_gatt+GET_GATT_OFF(addr));
+ }
+ serverworks_tlbflush(mem);
+ return 0;
+}
+
+static int serverworks_remove_memory(struct agp_memory *mem, off_t pg_start,
+ int type)
+{
+ int i;
+ unsigned long __iomem *cur_gatt;
+ unsigned long addr;
+
+ if (type != 0 || mem->type != 0) {
+ return -EINVAL;
+ }
+
+ global_cache_flush();
+ serverworks_tlbflush(mem);
+
+ for (i = pg_start; i < (mem->page_count + pg_start); i++) {
+ addr = (i * PAGE_SIZE) + agp_bridge->gart_bus_addr;
+ cur_gatt = SVRWRKS_GET_GATT(addr);
+ writel(agp_bridge->scratch_page, cur_gatt+GET_GATT_OFF(addr));
+ }
+
+ serverworks_tlbflush(mem);
+ return 0;
+}
+
+static struct gatt_mask serverworks_masks[] =
+{
+ {.mask = 1, .type = 0}
+};
+
+static struct aper_size_info_lvl2 serverworks_sizes[7] =
+{
+ {2048, 524288, 0x80000000},
+ {1024, 262144, 0xc0000000},
+ {512, 131072, 0xe0000000},
+ {256, 65536, 0xf0000000},
+ {128, 32768, 0xf8000000},
+ {64, 16384, 0xfc000000},
+ {32, 8192, 0xfe000000}
+};
+
+static void serverworks_agp_enable(struct agp_bridge_data *bridge, u32 mode)
+{
+ u32 command;
+
+ pci_read_config_dword(serverworks_private.svrwrks_dev,
+ bridge->capndx + PCI_AGP_STATUS,
+ &command);
+
+ command = agp_collect_device_status(bridge, mode, command);
+
+ command &= ~0x10; /* disable FW */
+ command &= ~0x08;
+
+ command |= 0x100;
+
+ pci_write_config_dword(serverworks_private.svrwrks_dev,
+ bridge->capndx + PCI_AGP_COMMAND,
+ command);
+
+ agp_device_command(command, 0);
+}
+
+struct agp_bridge_driver sworks_driver = {
+ .owner = THIS_MODULE,
+ .aperture_sizes = serverworks_sizes,
+ .size_type = LVL2_APER_SIZE,
+ .num_aperture_sizes = 7,
+ .configure = serverworks_configure,
+ .fetch_size = serverworks_fetch_size,
+ .cleanup = serverworks_cleanup,
+ .tlb_flush = serverworks_tlbflush,
+ .mask_memory = agp_generic_mask_memory,
+ .masks = serverworks_masks,
+ .agp_enable = serverworks_agp_enable,
+ .cache_flush = global_cache_flush,
+ .create_gatt_table = serverworks_create_gatt_table,
+ .free_gatt_table = serverworks_free_gatt_table,
+ .insert_memory = serverworks_insert_memory,
+ .remove_memory = serverworks_remove_memory,
+ .alloc_by_type = agp_generic_alloc_by_type,
+ .free_by_type = agp_generic_free_by_type,
+ .agp_alloc_page = agp_generic_alloc_page,
+ .agp_destroy_page = agp_generic_destroy_page,
+};
+
+static int __devinit agp_serverworks_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct agp_bridge_data *bridge;
+ struct pci_dev *bridge_dev;
+ u32 temp, temp2;
+ u8 cap_ptr = 0;
+
+ /* Everything is on func 1 here so we are hardcoding function one */
+ bridge_dev = pci_find_slot((unsigned int)pdev->bus->number,
+ PCI_DEVFN(0, 1));
+ if (!bridge_dev) {
+ printk(KERN_INFO PFX "Detected a Serverworks chipset "
+ "but could not find the secondary device.\n");
+ return -ENODEV;
+ }
+
+ cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP);
+
+ switch (pdev->device) {
+ case 0x0006:
+ /* ServerWorks CNB20HE
+ Fail silently.*/
+ printk (KERN_ERR PFX "Detected ServerWorks CNB20HE chipset: No AGP present.\n");
+ return -ENODEV;
+
+ case PCI_DEVICE_ID_SERVERWORKS_HE:
+ case PCI_DEVICE_ID_SERVERWORKS_LE:
+ case 0x0007:
+ break;
+
+ default:
+ if (cap_ptr)
+ printk(KERN_ERR PFX "Unsupported Serverworks chipset "
+ "(device id: %04x)\n", pdev->device);
+ return -ENODEV;
+ }
+
+ serverworks_private.svrwrks_dev = bridge_dev;
+ serverworks_private.gart_addr_ofs = 0x10;
+
+ pci_read_config_dword(pdev, SVWRKS_APSIZE, &temp);
+ if (temp & PCI_BASE_ADDRESS_MEM_TYPE_64) {
+ pci_read_config_dword(pdev, SVWRKS_APSIZE + 4, &temp2);
+ if (temp2 != 0) {
+ printk(KERN_INFO PFX "Detected 64 bit aperture address, "
+ "but top bits are not zero. Disabling agp\n");
+ return -ENODEV;
+ }
+ serverworks_private.mm_addr_ofs = 0x18;
+ } else
+ serverworks_private.mm_addr_ofs = 0x14;
+
+ pci_read_config_dword(pdev, serverworks_private.mm_addr_ofs, &temp);
+ if (temp & PCI_BASE_ADDRESS_MEM_TYPE_64) {
+ pci_read_config_dword(pdev,
+ serverworks_private.mm_addr_ofs + 4, &temp2);
+ if (temp2 != 0) {
+ printk(KERN_INFO PFX "Detected 64 bit MMIO address, "
+ "but top bits are not zero. Disabling agp\n");
+ return -ENODEV;
+ }
+ }
+
+ bridge = agp_alloc_bridge();
+ if (!bridge)
+ return -ENOMEM;
+
+ bridge->driver = &sworks_driver;
+ bridge->dev_private_data = &serverworks_private,
+ bridge->dev = pdev;
+
+ pci_set_drvdata(pdev, bridge);
+ return agp_add_bridge(bridge);
+}
+
+static void __devexit agp_serverworks_remove(struct pci_dev *pdev)
+{
+ struct agp_bridge_data *bridge = pci_get_drvdata(pdev);
+
+ agp_remove_bridge(bridge);
+ agp_put_bridge(bridge);
+}
+
+static struct pci_device_id agp_serverworks_pci_table[] = {
+ {
+ .class = (PCI_CLASS_BRIDGE_HOST << 8),
+ .class_mask = ~0,
+ .vendor = PCI_VENDOR_ID_SERVERWORKS,
+ .device = PCI_ANY_ID,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ },
+ { }
+};
+
+MODULE_DEVICE_TABLE(pci, agp_serverworks_pci_table);
+
+static struct pci_driver agp_serverworks_pci_driver = {
+ .name = "agpgart-serverworks",
+ .id_table = agp_serverworks_pci_table,
+ .probe = agp_serverworks_probe,
+ .remove = agp_serverworks_remove,
+};
+
+static int __init agp_serverworks_init(void)
+{
+ if (agp_off)
+ return -EINVAL;
+ return pci_register_driver(&agp_serverworks_pci_driver);
+}
+
+static void __exit agp_serverworks_cleanup(void)
+{
+ pci_unregister_driver(&agp_serverworks_pci_driver);
+}
+
+module_init(agp_serverworks_init);
+module_exit(agp_serverworks_cleanup);
+
+MODULE_LICENSE("GPL and additional rights");
+
diff --git a/drivers/char/agp/uninorth-agp.c b/drivers/char/agp/uninorth-agp.c
new file mode 100644
index 00000000000..0f248239b4b
--- /dev/null
+++ b/drivers/char/agp/uninorth-agp.c
@@ -0,0 +1,647 @@
+/*
+ * UniNorth AGPGART routines.
+ */
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/pagemap.h>
+#include <linux/agp_backend.h>
+#include <linux/delay.h>
+#include <asm/uninorth.h>
+#include <asm/pci-bridge.h>
+#include <asm/prom.h>
+#include "agp.h"
+
+/*
+ * NOTES for uninorth3 (G5 AGP) supports :
+ *
+ * There maybe also possibility to have bigger cache line size for
+ * agp (see pmac_pci.c and look for cache line). Need to be investigated
+ * by someone.
+ *
+ * PAGE size are hardcoded but this may change, see asm/page.h.
+ *
+ * Jerome Glisse <j.glisse@gmail.com>
+ */
+static int uninorth_rev;
+static int is_u3;
+
+static int uninorth_fetch_size(void)
+{
+ int i;
+ u32 temp;
+ struct aper_size_info_32 *values;
+
+ pci_read_config_dword(agp_bridge->dev, UNI_N_CFG_GART_BASE, &temp);
+ temp &= ~(0xfffff000);
+ values = A_SIZE_32(agp_bridge->driver->aperture_sizes);
+
+ for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++) {
+ if (temp == values[i].size_value) {
+ agp_bridge->previous_size =
+ agp_bridge->current_size = (void *) (values + i);
+ agp_bridge->aperture_size_idx = i;
+ return values[i].size;
+ }
+ }
+
+ agp_bridge->previous_size =
+ agp_bridge->current_size = (void *) (values + 1);
+ agp_bridge->aperture_size_idx = 1;
+ return values[1].size;
+
+ return 0;
+}
+
+static void uninorth_tlbflush(struct agp_memory *mem)
+{
+ u32 ctrl = UNI_N_CFG_GART_ENABLE;
+
+ if (is_u3)
+ ctrl |= U3_N_CFG_GART_PERFRD;
+ pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL,
+ ctrl | UNI_N_CFG_GART_INVAL);
+ pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL, ctrl);
+
+ if (uninorth_rev <= 0x30) {
+ pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL,
+ ctrl | UNI_N_CFG_GART_2xRESET);
+ pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL,
+ ctrl);
+ }
+}
+
+static void uninorth_cleanup(void)
+{
+ u32 tmp;
+
+ pci_read_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL, &tmp);
+ if (!(tmp & UNI_N_CFG_GART_ENABLE))
+ return;
+ tmp |= UNI_N_CFG_GART_INVAL;
+ pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL, tmp);
+ pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL, 0);
+
+ if (uninorth_rev <= 0x30) {
+ pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL,
+ UNI_N_CFG_GART_2xRESET);
+ pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL,
+ 0);
+ }
+}
+
+static int uninorth_configure(void)
+{
+ struct aper_size_info_32 *current_size;
+
+ current_size = A_SIZE_32(agp_bridge->current_size);
+
+ printk(KERN_INFO PFX "configuring for size idx: %d\n",
+ current_size->size_value);
+
+ /* aperture size and gatt addr */
+ pci_write_config_dword(agp_bridge->dev,
+ UNI_N_CFG_GART_BASE,
+ (agp_bridge->gatt_bus_addr & 0xfffff000)
+ | current_size->size_value);
+
+ /* HACK ALERT
+ * UniNorth seem to be buggy enough not to handle properly when
+ * the AGP aperture isn't mapped at bus physical address 0
+ */
+ agp_bridge->gart_bus_addr = 0;
+#ifdef CONFIG_PPC64
+ /* Assume U3 or later on PPC64 systems */
+ /* high 4 bits of GART physical address go in UNI_N_CFG_AGP_BASE */
+ pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_AGP_BASE,
+ (agp_bridge->gatt_bus_addr >> 32) & 0xf);
+#else
+ pci_write_config_dword(agp_bridge->dev,
+ UNI_N_CFG_AGP_BASE, agp_bridge->gart_bus_addr);
+#endif
+
+ if (is_u3) {
+ pci_write_config_dword(agp_bridge->dev,
+ UNI_N_CFG_GART_DUMMY_PAGE,
+ agp_bridge->scratch_page_real >> 12);
+ }
+
+ return 0;
+}
+
+static int uninorth_insert_memory(struct agp_memory *mem, off_t pg_start,
+ int type)
+{
+ int i, j, num_entries;
+ void *temp;
+
+ temp = agp_bridge->current_size;
+ num_entries = A_SIZE_32(temp)->num_entries;
+
+ if (type != 0 || mem->type != 0)
+ /* We know nothing of memory types */
+ return -EINVAL;
+ if ((pg_start + mem->page_count) > num_entries)
+ return -EINVAL;
+
+ j = pg_start;
+
+ while (j < (pg_start + mem->page_count)) {
+ if (agp_bridge->gatt_table[j])
+ return -EBUSY;
+ j++;
+ }
+
+ for (i = 0, j = pg_start; i < mem->page_count; i++, j++) {
+ agp_bridge->gatt_table[j] =
+ cpu_to_le32((mem->memory[i] & 0xFFFFF000UL) | 0x1UL);
+ flush_dcache_range((unsigned long)__va(mem->memory[i]),
+ (unsigned long)__va(mem->memory[i])+0x1000);
+ }
+ (void)in_le32((volatile u32*)&agp_bridge->gatt_table[pg_start]);
+ mb();
+ flush_dcache_range((unsigned long)&agp_bridge->gatt_table[pg_start],
+ (unsigned long)&agp_bridge->gatt_table[pg_start + mem->page_count]);
+
+ uninorth_tlbflush(mem);
+ return 0;
+}
+
+static int u3_insert_memory(struct agp_memory *mem, off_t pg_start, int type)
+{
+ int i, num_entries;
+ void *temp;
+ u32 *gp;
+
+ temp = agp_bridge->current_size;
+ num_entries = A_SIZE_32(temp)->num_entries;
+
+ if (type != 0 || mem->type != 0)
+ /* We know nothing of memory types */
+ return -EINVAL;
+ if ((pg_start + mem->page_count) > num_entries)
+ return -EINVAL;
+
+ gp = (u32 *) &agp_bridge->gatt_table[pg_start];
+ for (i = 0; i < mem->page_count; ++i) {
+ if (gp[i]) {
+ printk("u3_insert_memory: entry 0x%x occupied (%x)\n",
+ i, gp[i]);
+ return -EBUSY;
+ }
+ }
+
+ for (i = 0; i < mem->page_count; i++) {
+ gp[i] = (mem->memory[i] >> PAGE_SHIFT) | 0x80000000UL;
+ flush_dcache_range((unsigned long)__va(mem->memory[i]),
+ (unsigned long)__va(mem->memory[i])+0x1000);
+ }
+ mb();
+ flush_dcache_range((unsigned long)gp, (unsigned long) &gp[i]);
+ uninorth_tlbflush(mem);
+
+ return 0;
+}
+
+int u3_remove_memory(struct agp_memory *mem, off_t pg_start, int type)
+{
+ size_t i;
+ u32 *gp;
+
+ if (type != 0 || mem->type != 0)
+ /* We know nothing of memory types */
+ return -EINVAL;
+
+ gp = (u32 *) &agp_bridge->gatt_table[pg_start];
+ for (i = 0; i < mem->page_count; ++i)
+ gp[i] = 0;
+ mb();
+ flush_dcache_range((unsigned long)gp, (unsigned long) &gp[i]);
+ uninorth_tlbflush(mem);
+
+ return 0;
+}
+
+static void uninorth_agp_enable(struct agp_bridge_data *bridge, u32 mode)
+{
+ u32 command, scratch, status;
+ int timeout;
+
+ pci_read_config_dword(bridge->dev,
+ bridge->capndx + PCI_AGP_STATUS,
+ &status);
+
+ command = agp_collect_device_status(bridge, mode, status);
+ command |= PCI_AGP_COMMAND_AGP;
+
+ if (uninorth_rev == 0x21) {
+ /*
+ * Darwin disable AGP 4x on this revision, thus we
+ * may assume it's broken. This is an AGP2 controller.
+ */
+ command &= ~AGPSTAT2_4X;
+ }
+
+ if ((uninorth_rev >= 0x30) && (uninorth_rev <= 0x33)) {
+ /*
+ * We need to to set REQ_DEPTH to 7 for U3 versions 1.0, 2.1,
+ * 2.2 and 2.3, Darwin do so.
+ */
+ if ((command >> AGPSTAT_RQ_DEPTH_SHIFT) > 7)
+ command = (command & ~AGPSTAT_RQ_DEPTH)
+ | (7 << AGPSTAT_RQ_DEPTH_SHIFT);
+ }
+
+ uninorth_tlbflush(NULL);
+
+ timeout = 0;
+ do {
+ pci_write_config_dword(bridge->dev,
+ bridge->capndx + PCI_AGP_COMMAND,
+ command);
+ pci_read_config_dword(bridge->dev,
+ bridge->capndx + PCI_AGP_COMMAND,
+ &scratch);
+ } while ((scratch & PCI_AGP_COMMAND_AGP) == 0 && ++timeout < 1000);
+ if ((scratch & PCI_AGP_COMMAND_AGP) == 0)
+ printk(KERN_ERR PFX "failed to write UniNorth AGP command reg\n");
+
+ if (uninorth_rev >= 0x30) {
+ /* This is an AGP V3 */
+ agp_device_command(command, (status & AGPSTAT_MODE_3_0));
+ } else {
+ /* AGP V2 */
+ agp_device_command(command, 0);
+ }
+
+ uninorth_tlbflush(NULL);
+}
+
+#ifdef CONFIG_PM
+static int agp_uninorth_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ u32 cmd;
+ u8 agp;
+ struct pci_dev *device = NULL;
+
+ if (state != PMSG_SUSPEND)
+ return 0;
+
+ /* turn off AGP on the video chip, if it was enabled */
+ for_each_pci_dev(device) {
+ /* Don't touch the bridge yet, device first */
+ if (device == pdev)
+ continue;
+ /* Only deal with devices on the same bus here, no Mac has a P2P
+ * bridge on the AGP port, and mucking around the entire PCI
+ * tree is source of problems on some machines because of a bug
+ * in some versions of pci_find_capability() when hitting a dead
+ * device
+ */
+ if (device->bus != pdev->bus)
+ continue;
+ agp = pci_find_capability(device, PCI_CAP_ID_AGP);
+ if (!agp)
+ continue;
+ pci_read_config_dword(device, agp + PCI_AGP_COMMAND, &cmd);
+ if (!(cmd & PCI_AGP_COMMAND_AGP))
+ continue;
+ printk("uninorth-agp: disabling AGP on device %s\n",
+ pci_name(device));
+ cmd &= ~PCI_AGP_COMMAND_AGP;
+ pci_write_config_dword(device, agp + PCI_AGP_COMMAND, cmd);
+ }
+
+ /* turn off AGP on the bridge */
+ agp = pci_find_capability(pdev, PCI_CAP_ID_AGP);
+ pci_read_config_dword(pdev, agp + PCI_AGP_COMMAND, &cmd);
+ if (cmd & PCI_AGP_COMMAND_AGP) {
+ printk("uninorth-agp: disabling AGP on bridge %s\n",
+ pci_name(pdev));
+ cmd &= ~PCI_AGP_COMMAND_AGP;
+ pci_write_config_dword(pdev, agp + PCI_AGP_COMMAND, cmd);
+ }
+ /* turn off the GART */
+ uninorth_cleanup();
+
+ return 0;
+}
+
+static int agp_uninorth_resume(struct pci_dev *pdev)
+{
+ return 0;
+}
+#endif
+
+static int uninorth_create_gatt_table(struct agp_bridge_data *bridge)
+{
+ char *table;
+ char *table_end;
+ int size;
+ int page_order;
+ int num_entries;
+ int i;
+ void *temp;
+ struct page *page;
+
+ /* We can't handle 2 level gatt's */
+ if (bridge->driver->size_type == LVL2_APER_SIZE)
+ return -EINVAL;
+
+ table = NULL;
+ i = bridge->aperture_size_idx;
+ temp = bridge->current_size;
+ size = page_order = num_entries = 0;
+
+ do {
+ size = A_SIZE_32(temp)->size;
+ page_order = A_SIZE_32(temp)->page_order;
+ num_entries = A_SIZE_32(temp)->num_entries;
+
+ table = (char *) __get_free_pages(GFP_KERNEL, page_order);
+
+ if (table == NULL) {
+ i++;
+ bridge->current_size = A_IDX32(bridge);
+ } else {
+ bridge->aperture_size_idx = i;
+ }
+ } while (!table && (i < bridge->driver->num_aperture_sizes));
+
+ if (table == NULL)
+ return -ENOMEM;
+
+ table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1);
+
+ for (page = virt_to_page(table); page <= virt_to_page(table_end); page++)
+ SetPageReserved(page);
+
+ bridge->gatt_table_real = (u32 *) table;
+ bridge->gatt_table = (u32 *)table;
+ bridge->gatt_bus_addr = virt_to_phys(table);
+
+ for (i = 0; i < num_entries; i++)
+ bridge->gatt_table[i] = 0;
+
+ flush_dcache_range((unsigned long)table, (unsigned long)table_end);
+
+ return 0;
+}
+
+static int uninorth_free_gatt_table(struct agp_bridge_data *bridge)
+{
+ int page_order;
+ char *table, *table_end;
+ void *temp;
+ struct page *page;
+
+ temp = bridge->current_size;
+ page_order = A_SIZE_32(temp)->page_order;
+
+ /* Do not worry about freeing memory, because if this is
+ * called, then all agp memory is deallocated and removed
+ * from the table.
+ */
+
+ table = (char *) bridge->gatt_table_real;
+ table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1);
+
+ for (page = virt_to_page(table); page <= virt_to_page(table_end); page++)
+ ClearPageReserved(page);
+
+ free_pages((unsigned long) bridge->gatt_table_real, page_order);
+
+ return 0;
+}
+
+void null_cache_flush(void)
+{
+ mb();
+}
+
+/* Setup function */
+
+static struct aper_size_info_32 uninorth_sizes[7] =
+{
+#if 0 /* Not sure uninorth supports that high aperture sizes */
+ {256, 65536, 6, 64},
+ {128, 32768, 5, 32},
+ {64, 16384, 4, 16},
+#endif
+ {32, 8192, 3, 8},
+ {16, 4096, 2, 4},
+ {8, 2048, 1, 2},
+ {4, 1024, 0, 1}
+};
+
+/*
+ * Not sure that u3 supports that high aperture sizes but it
+ * would strange if it did not :)
+ */
+static struct aper_size_info_32 u3_sizes[8] =
+{
+ {512, 131072, 7, 128},
+ {256, 65536, 6, 64},
+ {128, 32768, 5, 32},
+ {64, 16384, 4, 16},
+ {32, 8192, 3, 8},
+ {16, 4096, 2, 4},
+ {8, 2048, 1, 2},
+ {4, 1024, 0, 1}
+};
+
+struct agp_bridge_driver uninorth_agp_driver = {
+ .owner = THIS_MODULE,
+ .aperture_sizes = (void *)uninorth_sizes,
+ .size_type = U32_APER_SIZE,
+ .num_aperture_sizes = 4,
+ .configure = uninorth_configure,
+ .fetch_size = uninorth_fetch_size,
+ .cleanup = uninorth_cleanup,
+ .tlb_flush = uninorth_tlbflush,
+ .mask_memory = agp_generic_mask_memory,
+ .masks = NULL,
+ .cache_flush = null_cache_flush,
+ .agp_enable = uninorth_agp_enable,
+ .create_gatt_table = uninorth_create_gatt_table,
+ .free_gatt_table = uninorth_free_gatt_table,
+ .insert_memory = uninorth_insert_memory,
+ .remove_memory = agp_generic_remove_memory,
+ .alloc_by_type = agp_generic_alloc_by_type,
+ .free_by_type = agp_generic_free_by_type,
+ .agp_alloc_page = agp_generic_alloc_page,
+ .agp_destroy_page = agp_generic_destroy_page,
+ .cant_use_aperture = 1,
+};
+
+struct agp_bridge_driver u3_agp_driver = {
+ .owner = THIS_MODULE,
+ .aperture_sizes = (void *)u3_sizes,
+ .size_type = U32_APER_SIZE,
+ .num_aperture_sizes = 8,
+ .configure = uninorth_configure,
+ .fetch_size = uninorth_fetch_size,
+ .cleanup = uninorth_cleanup,
+ .tlb_flush = uninorth_tlbflush,
+ .mask_memory = agp_generic_mask_memory,
+ .masks = NULL,
+ .cache_flush = null_cache_flush,
+ .agp_enable = uninorth_agp_enable,
+ .create_gatt_table = uninorth_create_gatt_table,
+ .free_gatt_table = uninorth_free_gatt_table,
+ .insert_memory = u3_insert_memory,
+ .remove_memory = u3_remove_memory,
+ .alloc_by_type = agp_generic_alloc_by_type,
+ .free_by_type = agp_generic_free_by_type,
+ .agp_alloc_page = agp_generic_alloc_page,
+ .agp_destroy_page = agp_generic_destroy_page,
+ .cant_use_aperture = 1,
+ .needs_scratch_page = 1,
+};
+
+static struct agp_device_ids uninorth_agp_device_ids[] __devinitdata = {
+ {
+ .device_id = PCI_DEVICE_ID_APPLE_UNI_N_AGP,
+ .chipset_name = "UniNorth",
+ },
+ {
+ .device_id = PCI_DEVICE_ID_APPLE_UNI_N_AGP_P,
+ .chipset_name = "UniNorth/Pangea",
+ },
+ {
+ .device_id = PCI_DEVICE_ID_APPLE_UNI_N_AGP15,
+ .chipset_name = "UniNorth 1.5",
+ },
+ {
+ .device_id = PCI_DEVICE_ID_APPLE_UNI_N_AGP2,
+ .chipset_name = "UniNorth 2",
+ },
+ {
+ .device_id = PCI_DEVICE_ID_APPLE_U3_AGP,
+ .chipset_name = "U3",
+ },
+ {
+ .device_id = PCI_DEVICE_ID_APPLE_U3L_AGP,
+ .chipset_name = "U3L",
+ },
+ {
+ .device_id = PCI_DEVICE_ID_APPLE_U3H_AGP,
+ .chipset_name = "U3H",
+ },
+};
+
+static int __devinit agp_uninorth_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct agp_device_ids *devs = uninorth_agp_device_ids;
+ struct agp_bridge_data *bridge;
+ struct device_node *uninorth_node;
+ u8 cap_ptr;
+ int j;
+
+ cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP);
+ if (cap_ptr == 0)
+ return -ENODEV;
+
+ /* probe for known chipsets */
+ for (j = 0; devs[j].chipset_name != NULL; ++j) {
+ if (pdev->device == devs[j].device_id) {
+ printk(KERN_INFO PFX "Detected Apple %s chipset\n",
+ devs[j].chipset_name);
+ goto found;
+ }
+ }
+
+ printk(KERN_ERR PFX "Unsupported Apple chipset (device id: %04x).\n",
+ pdev->device);
+ return -ENODEV;
+
+ found:
+ /* Set revision to 0 if we could not read it. */
+ uninorth_rev = 0;
+ is_u3 = 0;
+ /* Locate core99 Uni-N */
+ uninorth_node = of_find_node_by_name(NULL, "uni-n");
+ /* Locate G5 u3 */
+ if (uninorth_node == NULL) {
+ is_u3 = 1;
+ uninorth_node = of_find_node_by_name(NULL, "u3");
+ }
+ if (uninorth_node) {
+ int *revprop = (int *)
+ get_property(uninorth_node, "device-rev", NULL);
+ if (revprop != NULL)
+ uninorth_rev = *revprop & 0x3f;
+ of_node_put(uninorth_node);
+ }
+
+ bridge = agp_alloc_bridge();
+ if (!bridge)
+ return -ENOMEM;
+
+ if (is_u3)
+ bridge->driver = &u3_agp_driver;
+ else
+ bridge->driver = &uninorth_agp_driver;
+
+ bridge->dev = pdev;
+ bridge->capndx = cap_ptr;
+ bridge->flags = AGP_ERRATA_FASTWRITES;
+
+ /* Fill in the mode register */
+ pci_read_config_dword(pdev, cap_ptr+PCI_AGP_STATUS, &bridge->mode);
+
+ pci_set_drvdata(pdev, bridge);
+ return agp_add_bridge(bridge);
+}
+
+static void __devexit agp_uninorth_remove(struct pci_dev *pdev)
+{
+ struct agp_bridge_data *bridge = pci_get_drvdata(pdev);
+
+ agp_remove_bridge(bridge);
+ agp_put_bridge(bridge);
+}
+
+static struct pci_device_id agp_uninorth_pci_table[] = {
+ {
+ .class = (PCI_CLASS_BRIDGE_HOST << 8),
+ .class_mask = ~0,
+ .vendor = PCI_VENDOR_ID_APPLE,
+ .device = PCI_ANY_ID,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ },
+ { }
+};
+
+MODULE_DEVICE_TABLE(pci, agp_uninorth_pci_table);
+
+static struct pci_driver agp_uninorth_pci_driver = {
+ .name = "agpgart-uninorth",
+ .id_table = agp_uninorth_pci_table,
+ .probe = agp_uninorth_probe,
+ .remove = agp_uninorth_remove,
+#ifdef CONFIG_PM
+ .suspend = agp_uninorth_suspend,
+ .resume = agp_uninorth_resume,
+#endif
+};
+
+static int __init agp_uninorth_init(void)
+{
+ if (agp_off)
+ return -EINVAL;
+ return pci_register_driver(&agp_uninorth_pci_driver);
+}
+
+static void __exit agp_uninorth_cleanup(void)
+{
+ pci_unregister_driver(&agp_uninorth_pci_driver);
+}
+
+module_init(agp_uninorth_init);
+module_exit(agp_uninorth_cleanup);
+
+MODULE_AUTHOR("Ben Herrenschmidt & Paul Mackerras");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/agp/via-agp.c b/drivers/char/agp/via-agp.c
new file mode 100644
index 00000000000..e1451dd9b6a
--- /dev/null
+++ b/drivers/char/agp/via-agp.c
@@ -0,0 +1,548 @@
+/*
+ * VIA AGPGART routines.
+ */
+
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/agp_backend.h>
+#include "agp.h"
+
+static struct pci_device_id agp_via_pci_table[];
+
+#define VIA_GARTCTRL 0x80
+#define VIA_APSIZE 0x84
+#define VIA_ATTBASE 0x88
+
+#define VIA_AGP3_GARTCTRL 0x90
+#define VIA_AGP3_APSIZE 0x94
+#define VIA_AGP3_ATTBASE 0x98
+#define VIA_AGPSEL 0xfd
+
+static int via_fetch_size(void)
+{
+ int i;
+ u8 temp;
+ struct aper_size_info_8 *values;
+
+ values = A_SIZE_8(agp_bridge->driver->aperture_sizes);
+ pci_read_config_byte(agp_bridge->dev, VIA_APSIZE, &temp);
+ for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++) {
+ if (temp == values[i].size_value) {
+ agp_bridge->previous_size =
+ agp_bridge->current_size = (void *) (values + i);
+ agp_bridge->aperture_size_idx = i;
+ return values[i].size;
+ }
+ }
+ printk(KERN_ERR PFX "Unknown aperture size from AGP bridge (0x%x)\n", temp);
+ return 0;
+}
+
+
+static int via_configure(void)
+{
+ u32 temp;
+ struct aper_size_info_8 *current_size;
+
+ current_size = A_SIZE_8(agp_bridge->current_size);
+ /* aperture size */
+ pci_write_config_byte(agp_bridge->dev, VIA_APSIZE,
+ current_size->size_value);
+ /* address to map too */
+ pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp);
+ agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+
+ /* GART control register */
+ pci_write_config_dword(agp_bridge->dev, VIA_GARTCTRL, 0x0000000f);
+
+ /* attbase - aperture GATT base */
+ pci_write_config_dword(agp_bridge->dev, VIA_ATTBASE,
+ (agp_bridge->gatt_bus_addr & 0xfffff000) | 3);
+ return 0;
+}
+
+
+static void via_cleanup(void)
+{
+ struct aper_size_info_8 *previous_size;
+
+ previous_size = A_SIZE_8(agp_bridge->previous_size);
+ pci_write_config_byte(agp_bridge->dev, VIA_APSIZE,
+ previous_size->size_value);
+ /* Do not disable by writing 0 to VIA_ATTBASE, it screws things up
+ * during reinitialization.
+ */
+}
+
+
+static void via_tlbflush(struct agp_memory *mem)
+{
+ u32 temp;
+
+ pci_read_config_dword(agp_bridge->dev, VIA_GARTCTRL, &temp);
+ temp |= (1<<7);
+ pci_write_config_dword(agp_bridge->dev, VIA_GARTCTRL, temp);
+ temp &= ~(1<<7);
+ pci_write_config_dword(agp_bridge->dev, VIA_GARTCTRL, temp);
+}
+
+
+static struct aper_size_info_8 via_generic_sizes[9] =
+{
+ {256, 65536, 6, 0},
+ {128, 32768, 5, 128},
+ {64, 16384, 4, 192},
+ {32, 8192, 3, 224},
+ {16, 4096, 2, 240},
+ {8, 2048, 1, 248},
+ {4, 1024, 0, 252},
+ {2, 512, 0, 254},
+ {1, 256, 0, 255}
+};
+
+
+static int via_fetch_size_agp3(void)
+{
+ int i;
+ u16 temp;
+ struct aper_size_info_16 *values;
+
+ values = A_SIZE_16(agp_bridge->driver->aperture_sizes);
+ pci_read_config_word(agp_bridge->dev, VIA_AGP3_APSIZE, &temp);
+ temp &= 0xfff;
+
+ for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++) {
+ if (temp == values[i].size_value) {
+ agp_bridge->previous_size =
+ agp_bridge->current_size = (void *) (values + i);
+ agp_bridge->aperture_size_idx = i;
+ return values[i].size;
+ }
+ }
+ return 0;
+}
+
+
+static int via_configure_agp3(void)
+{
+ u32 temp;
+ struct aper_size_info_16 *current_size;
+
+ current_size = A_SIZE_16(agp_bridge->current_size);
+
+ /* address to map too */
+ pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp);
+ agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+
+ /* attbase - aperture GATT base */
+ pci_write_config_dword(agp_bridge->dev, VIA_AGP3_ATTBASE,
+ agp_bridge->gatt_bus_addr & 0xfffff000);
+
+ /* 1. Enable GTLB in RX90<7>, all AGP aperture access needs to fetch
+ * translation table first.
+ * 2. Enable AGP aperture in RX91<0>. This bit controls the enabling of the
+ * graphics AGP aperture for the AGP3.0 port.
+ */
+ pci_read_config_dword(agp_bridge->dev, VIA_AGP3_GARTCTRL, &temp);
+ pci_write_config_dword(agp_bridge->dev, VIA_AGP3_GARTCTRL, temp | (3<<7));
+ return 0;
+}
+
+
+static void via_cleanup_agp3(void)
+{
+ struct aper_size_info_16 *previous_size;
+
+ previous_size = A_SIZE_16(agp_bridge->previous_size);
+ pci_write_config_byte(agp_bridge->dev, VIA_APSIZE, previous_size->size_value);
+}
+
+
+static void via_tlbflush_agp3(struct agp_memory *mem)
+{
+ u32 temp;
+
+ pci_read_config_dword(agp_bridge->dev, VIA_AGP3_GARTCTRL, &temp);
+ pci_write_config_dword(agp_bridge->dev, VIA_AGP3_GARTCTRL, temp & ~(1<<7));
+ pci_write_config_dword(agp_bridge->dev, VIA_AGP3_GARTCTRL, temp);
+}
+
+
+struct agp_bridge_driver via_agp3_driver = {
+ .owner = THIS_MODULE,
+ .aperture_sizes = agp3_generic_sizes,
+ .size_type = U8_APER_SIZE,
+ .num_aperture_sizes = 10,
+ .configure = via_configure_agp3,
+ .fetch_size = via_fetch_size_agp3,
+ .cleanup = via_cleanup_agp3,
+ .tlb_flush = via_tlbflush_agp3,
+ .mask_memory = agp_generic_mask_memory,
+ .masks = NULL,
+ .agp_enable = agp_generic_enable,
+ .cache_flush = global_cache_flush,
+ .create_gatt_table = agp_generic_create_gatt_table,
+ .free_gatt_table = agp_generic_free_gatt_table,
+ .insert_memory = agp_generic_insert_memory,
+ .remove_memory = agp_generic_remove_memory,
+ .alloc_by_type = agp_generic_alloc_by_type,
+ .free_by_type = agp_generic_free_by_type,
+ .agp_alloc_page = agp_generic_alloc_page,
+ .agp_destroy_page = agp_generic_destroy_page,
+};
+
+struct agp_bridge_driver via_driver = {
+ .owner = THIS_MODULE,
+ .aperture_sizes = via_generic_sizes,
+ .size_type = U8_APER_SIZE,
+ .num_aperture_sizes = 9,
+ .configure = via_configure,
+ .fetch_size = via_fetch_size,
+ .cleanup = via_cleanup,
+ .tlb_flush = via_tlbflush,
+ .mask_memory = agp_generic_mask_memory,
+ .masks = NULL,
+ .agp_enable = agp_generic_enable,
+ .cache_flush = global_cache_flush,
+ .create_gatt_table = agp_generic_create_gatt_table,
+ .free_gatt_table = agp_generic_free_gatt_table,
+ .insert_memory = agp_generic_insert_memory,
+ .remove_memory = agp_generic_remove_memory,
+ .alloc_by_type = agp_generic_alloc_by_type,
+ .free_by_type = agp_generic_free_by_type,
+ .agp_alloc_page = agp_generic_alloc_page,
+ .agp_destroy_page = agp_generic_destroy_page,
+};
+
+static struct agp_device_ids via_agp_device_ids[] __devinitdata =
+{
+ {
+ .device_id = PCI_DEVICE_ID_VIA_82C597_0,
+ .chipset_name = "Apollo VP3",
+ },
+
+ {
+ .device_id = PCI_DEVICE_ID_VIA_82C598_0,
+ .chipset_name = "Apollo MVP3",
+ },
+
+ {
+ .device_id = PCI_DEVICE_ID_VIA_8501_0,
+ .chipset_name = "Apollo MVP4",
+ },
+
+ /* VT8601 */
+ {
+ .device_id = PCI_DEVICE_ID_VIA_8601_0,
+ .chipset_name = "Apollo ProMedia/PLE133Ta",
+ },
+
+ /* VT82C693A / VT28C694T */
+ {
+ .device_id = PCI_DEVICE_ID_VIA_82C691_0,
+ .chipset_name = "Apollo Pro 133",
+ },
+
+ {
+ .device_id = PCI_DEVICE_ID_VIA_8371_0,
+ .chipset_name = "KX133",
+ },
+
+ /* VT8633 */
+ {
+ .device_id = PCI_DEVICE_ID_VIA_8633_0,
+ .chipset_name = "Pro 266",
+ },
+
+ {
+ .device_id = PCI_DEVICE_ID_VIA_XN266,
+ .chipset_name = "Apollo Pro266",
+ },
+
+ /* VT8361 */
+ {
+ .device_id = PCI_DEVICE_ID_VIA_8361,
+ .chipset_name = "KLE133",
+ },
+
+ /* VT8365 / VT8362 */
+ {
+ .device_id = PCI_DEVICE_ID_VIA_8363_0,
+ .chipset_name = "Twister-K/KT133x/KM133",
+ },
+
+ /* VT8753A */
+ {
+ .device_id = PCI_DEVICE_ID_VIA_8753_0,
+ .chipset_name = "P4X266",
+ },
+
+ /* VT8366 */
+ {
+ .device_id = PCI_DEVICE_ID_VIA_8367_0,
+ .chipset_name = "KT266/KY266x/KT333",
+ },
+
+ /* VT8633 (for CuMine/ Celeron) */
+ {
+ .device_id = PCI_DEVICE_ID_VIA_8653_0,
+ .chipset_name = "Pro266T",
+ },
+
+ /* KM266 / PM266 */
+ {
+ .device_id = PCI_DEVICE_ID_VIA_XM266,
+ .chipset_name = "PM266/KM266",
+ },
+
+ /* CLE266 */
+ {
+ .device_id = PCI_DEVICE_ID_VIA_862X_0,
+ .chipset_name = "CLE266",
+ },
+
+ {
+ .device_id = PCI_DEVICE_ID_VIA_8377_0,
+ .chipset_name = "KT400/KT400A/KT600",
+ },
+
+ /* VT8604 / VT8605 / VT8603
+ * (Apollo Pro133A chipset with S3 Savage4) */
+ {
+ .device_id = PCI_DEVICE_ID_VIA_8605_0,
+ .chipset_name = "ProSavage PM133/PL133/PN133"
+ },
+
+ /* P4M266x/P4N266 */
+ {
+ .device_id = PCI_DEVICE_ID_VIA_8703_51_0,
+ .chipset_name = "P4M266x/P4N266",
+ },
+
+ /* VT8754 */
+ {
+ .device_id = PCI_DEVICE_ID_VIA_8754C_0,
+ .chipset_name = "PT800",
+ },
+
+ /* P4X600 */
+ {
+ .device_id = PCI_DEVICE_ID_VIA_8763_0,
+ .chipset_name = "P4X600"
+ },
+
+ /* KM400 */
+ {
+ .device_id = PCI_DEVICE_ID_VIA_8378_0,
+ .chipset_name = "KM400/KM400A",
+ },
+
+ /* PT880 */
+ {
+ .device_id = PCI_DEVICE_ID_VIA_PT880,
+ .chipset_name = "PT880",
+ },
+
+ /* PT890 */
+ {
+ .device_id = PCI_DEVICE_ID_VIA_8783_0,
+ .chipset_name = "PT890",
+ },
+
+ /* PM800/PN800/PM880/PN880 */
+ {
+ .device_id = PCI_DEVICE_ID_VIA_PX8X0_0,
+ .chipset_name = "PM800/PN800/PM880/PN880",
+ },
+ /* KT880 */
+ {
+ .device_id = PCI_DEVICE_ID_VIA_3269_0,
+ .chipset_name = "KT880",
+ },
+ /* KTxxx/Px8xx */
+ {
+ .device_id = PCI_DEVICE_ID_VIA_83_87XX_1,
+ .chipset_name = "VT83xx/VT87xx/KTxxx/Px8xx",
+ },
+ /* P4M800 */
+ {
+ .device_id = PCI_DEVICE_ID_VIA_3296_0,
+ .chipset_name = "P4M800",
+ },
+
+ { }, /* dummy final entry, always present */
+};
+
+
+/*
+ * VIA's AGP3 chipsets do magick to put the AGP bridge compliant
+ * with the same standards version as the graphics card.
+ */
+static void check_via_agp3 (struct agp_bridge_data *bridge)
+{
+ u8 reg;
+
+ pci_read_config_byte(bridge->dev, VIA_AGPSEL, &reg);
+ /* Check AGP 2.0 compatibility mode. */
+ if ((reg & (1<<1))==0)
+ bridge->driver = &via_agp3_driver;
+}
+
+
+static int __devinit agp_via_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct agp_device_ids *devs = via_agp_device_ids;
+ struct agp_bridge_data *bridge;
+ int j = 0;
+ u8 cap_ptr;
+
+ cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP);
+ if (!cap_ptr)
+ return -ENODEV;
+
+ j = ent - agp_via_pci_table;
+ printk (KERN_INFO PFX "Detected VIA %s chipset\n", devs[j].chipset_name);
+
+ bridge = agp_alloc_bridge();
+ if (!bridge)
+ return -ENOMEM;
+
+ bridge->dev = pdev;
+ bridge->capndx = cap_ptr;
+ bridge->driver = &via_driver;
+
+ /*
+ * Garg, there are KT400s with KT266 IDs.
+ */
+ if (pdev->device == PCI_DEVICE_ID_VIA_8367_0) {
+ /* Is there a KT400 subsystem ? */
+ if (pdev->subsystem_device == PCI_DEVICE_ID_VIA_8377_0) {
+ printk(KERN_INFO PFX "Found KT400 in disguise as a KT266.\n");
+ check_via_agp3(bridge);
+ }
+ }
+
+ /* If this is an AGP3 bridge, check which mode its in and adjust. */
+ get_agp_version(bridge);
+ if (bridge->major_version >= 3)
+ check_via_agp3(bridge);
+
+ /* Fill in the mode register */
+ pci_read_config_dword(pdev,
+ bridge->capndx+PCI_AGP_STATUS, &bridge->mode);
+
+ pci_set_drvdata(pdev, bridge);
+ return agp_add_bridge(bridge);
+}
+
+static void __devexit agp_via_remove(struct pci_dev *pdev)
+{
+ struct agp_bridge_data *bridge = pci_get_drvdata(pdev);
+
+ agp_remove_bridge(bridge);
+ agp_put_bridge(bridge);
+}
+
+#ifdef CONFIG_PM
+
+static int agp_via_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ pci_save_state (pdev);
+ pci_set_power_state (pdev, PCI_D3hot);
+
+ return 0;
+}
+
+static int agp_via_resume(struct pci_dev *pdev)
+{
+ struct agp_bridge_data *bridge = pci_get_drvdata(pdev);
+
+ pci_set_power_state (pdev, PCI_D0);
+ pci_restore_state(pdev);
+
+ if (bridge->driver == &via_agp3_driver)
+ return via_configure_agp3();
+ else if (bridge->driver == &via_driver)
+ return via_configure();
+
+ return 0;
+}
+
+#endif /* CONFIG_PM */
+
+/* must be the same order as name table above */
+static struct pci_device_id agp_via_pci_table[] = {
+#define ID(x) \
+ { \
+ .class = (PCI_CLASS_BRIDGE_HOST << 8), \
+ .class_mask = ~0, \
+ .vendor = PCI_VENDOR_ID_VIA, \
+ .device = x, \
+ .subvendor = PCI_ANY_ID, \
+ .subdevice = PCI_ANY_ID, \
+ }
+ ID(PCI_DEVICE_ID_VIA_82C597_0),
+ ID(PCI_DEVICE_ID_VIA_82C598_0),
+ ID(PCI_DEVICE_ID_VIA_8501_0),
+ ID(PCI_DEVICE_ID_VIA_8601_0),
+ ID(PCI_DEVICE_ID_VIA_82C691_0),
+ ID(PCI_DEVICE_ID_VIA_8371_0),
+ ID(PCI_DEVICE_ID_VIA_8633_0),
+ ID(PCI_DEVICE_ID_VIA_XN266),
+ ID(PCI_DEVICE_ID_VIA_8361),
+ ID(PCI_DEVICE_ID_VIA_8363_0),
+ ID(PCI_DEVICE_ID_VIA_8753_0),
+ ID(PCI_DEVICE_ID_VIA_8367_0),
+ ID(PCI_DEVICE_ID_VIA_8653_0),
+ ID(PCI_DEVICE_ID_VIA_XM266),
+ ID(PCI_DEVICE_ID_VIA_862X_0),
+ ID(PCI_DEVICE_ID_VIA_8377_0),
+ ID(PCI_DEVICE_ID_VIA_8605_0),
+ ID(PCI_DEVICE_ID_VIA_8703_51_0),
+ ID(PCI_DEVICE_ID_VIA_8754C_0),
+ ID(PCI_DEVICE_ID_VIA_8763_0),
+ ID(PCI_DEVICE_ID_VIA_8378_0),
+ ID(PCI_DEVICE_ID_VIA_PT880),
+ ID(PCI_DEVICE_ID_VIA_8783_0),
+ ID(PCI_DEVICE_ID_VIA_PX8X0_0),
+ ID(PCI_DEVICE_ID_VIA_3269_0),
+ ID(PCI_DEVICE_ID_VIA_83_87XX_1),
+ ID(PCI_DEVICE_ID_VIA_3296_0),
+ { }
+};
+
+MODULE_DEVICE_TABLE(pci, agp_via_pci_table);
+
+
+static struct pci_driver agp_via_pci_driver = {
+ .name = "agpgart-via",
+ .id_table = agp_via_pci_table,
+ .probe = agp_via_probe,
+ .remove = agp_via_remove,
+#ifdef CONFIG_PM
+ .suspend = agp_via_suspend,
+ .resume = agp_via_resume,
+#endif
+};
+
+
+static int __init agp_via_init(void)
+{
+ if (agp_off)
+ return -EINVAL;
+ return pci_register_driver(&agp_via_pci_driver);
+}
+
+static void __exit agp_via_cleanup(void)
+{
+ pci_unregister_driver(&agp_via_pci_driver);
+}
+
+module_init(agp_via_init);
+module_exit(agp_via_cleanup);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Dave Jones <davej@codemonkey.org.uk>");