Commit b31450b7 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

agpgart: Split agpgart code into separate files.

The majority of this work was done by Dave Jones, I merely converted the
driver to the "new" pci api.
parent 182147c1
CONFIG_AGP
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.
This driver is available as a module. If you want to compile it as
a module, say M here and read <file:Documentation/modules.txt>. The
module will be called agpgart.o.
CONFIG_AGP_INTEL
This option gives you AGP support for the GLX component of the
XFree86 4.x on Intel 440LX/BX/GX, 815, 820, 830, 840, 845, 850 and 860 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_I810
This option gives you AGP support for the Xserver on the Intel 810
815 and 830m chipset boards for their on-board integrated graphics. This
is required to do any useful video modes with these boards.
CONFIG_AGP_I460
This option gives you AGP GART support for the Intel 460GX chipset
for IA64 processors.
CONFIG_AGP_VIA
This option gives you AGP support for the GLX component of the
XFree86 4.x on VIA MPV3/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_AMD
This option gives you AGP support for the GLX component of the
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_SIS
This option gives you AGP support for the GLX component of the "soon
to be released" 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
Say Y here to support the Serverworks AGP card. See
<http://www.serverworks.com/> for product descriptions and images.
CONFIG_AGP_ALI
This option gives you AGP support for the GLX component of the
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_HP_ZX1
This option gives you AGP GART support for the HP ZX1 chipset
for IA64 processors.
dep_tristate '/dev/agpgart (AGP Support)' CONFIG_AGP $CONFIG_DRM_AGP
if [ "$CONFIG_AGP" != "n" ]; then
bool ' Intel 440LX/BX/GX and I815/I820/I830M/I830MP/I840/I845/I850/I860 support' CONFIG_AGP_INTEL
bool ' Intel I810/I815/I830M (on-board) support' CONFIG_AGP_I810
bool ' VIA chipset support' CONFIG_AGP_VIA
bool ' AMD Irongate, 761, and 762 support' CONFIG_AGP_AMD
bool ' Generic SiS support' CONFIG_AGP_SIS
bool ' ALI chipset support' CONFIG_AGP_ALI
bool ' Serverworks LE/HE support' CONFIG_AGP_SWORKS
if [ "$CONFIG_IA64" = "y" ]; then
bool ' Intel 460GX support' CONFIG_AGP_I460
bool ' HP ZX1 AGP support' CONFIG_AGP_HP_ZX1
fi
fi
......@@ -5,7 +5,18 @@
export-objs := agpgart_be.o
agpgart-objs := agpgart_fe.o agpgart_be.o
agpgart-y := agpgart_fe.o agpgart_be.o
agpgart-$(CONFIG_AGP_INTEL) += agpgart_be-i8x0.o
agpgart-$(CONFIG_AGP_I810) += agpgart_be-i810.o
agpgart-$(CONFIG_AGP_VIA) += agpgart_be-via.o
agpgart-$(CONFIG_AGP_AMD) += agpgart_be-amd.o
agpgart-$(CONFIG_AGP_SIS) += agpgart_be-sis.o
agpgart-$(CONFIG_AGP_ALI) += agpgart_be-ali.o
agpgart-$(CONFIG_AGP_SWORKS) += agpgart_be-sworks.o
agpgart-$(CONFIG_AGP_I460) += agpgart_be-i460.o
agpgart-$(CONFIG_AGP_HP_ZX1) += agpgart_be-hp.o
agpgart-objs := $(agpgart-y)
obj-$(CONFIG_AGP) += agpgart.o
......
......@@ -27,6 +27,67 @@
#ifndef _AGP_BACKEND_PRIV_H
#define _AGP_BACKEND_PRIV_H 1
#include <asm/agp.h> /* for flush_agp_cache() */
extern struct agp_bridge_data agp_bridge;
/* Generic routines. */
void agp_generic_agp_enable(u32 mode);
int agp_generic_create_gatt_table(void);
int agp_generic_free_gatt_table(void);
agp_memory *agp_create_memory(int scratch_pages);
int agp_generic_insert_memory(agp_memory * mem, off_t pg_start, int type);
int agp_generic_remove_memory(agp_memory * mem, off_t pg_start, int type);
agp_memory *agp_generic_alloc_by_type(size_t page_count, int type);
void agp_generic_free_by_type(agp_memory * curr);
void *agp_generic_alloc_page(void);
void agp_generic_destroy_page(void *addr);
int agp_generic_suspend(void);
void agp_generic_resume(void);
void agp_free_key(int key);
/* chipset specific init routines. */
int __init ali_generic_setup (struct pci_dev *pdev);
int __init amd_irongate_setup (struct pci_dev *pdev);
int __init hp_zx1_setup (struct pci_dev *pdev);
int __init intel_i460_setup (struct pci_dev *pdev);
int __init intel_generic_setup (struct pci_dev *pdev);
int __init intel_i810_setup(struct pci_dev *i810_dev);
int __init intel_815_setup(struct pci_dev *pdev);
int __init intel_i830_setup(struct pci_dev *i830_dev);
int __init intel_820_setup (struct pci_dev *pdev);
int __init intel_830mp_setup (struct pci_dev *pdev);
int __init intel_840_setup (struct pci_dev *pdev);
int __init intel_845_setup (struct pci_dev *pdev);
int __init intel_850_setup (struct pci_dev *pdev);
int __init intel_860_setup (struct pci_dev *pdev);
int __init serverworks_setup (struct pci_dev *pdev);
int __init sis_generic_setup (struct pci_dev *pdev);
int __init via_generic_setup (struct pci_dev *pdev);
#define AGPGART_MODULE_NAME "agpgart"
#define PFX AGPGART_MODULE_NAME ": "
#ifdef CONFIG_SMP
static void ipi_handler(void *null)
{
flush_agp_cache();
}
static void __attribute__((unused)) global_cache_flush(void)
{
if (smp_call_function(ipi_handler, NULL, 1, 1) != 0)
panic(PFX "timed out waiting for the other CPUs!\n");
flush_agp_cache();
}
#else
static void global_cache_flush(void)
{
flush_agp_cache();
}
#endif /* !CONFIG_SMP */
enum aper_size_type {
U8_APER_SIZE,
U16_APER_SIZE,
......@@ -35,55 +96,55 @@ enum aper_size_type {
FIXED_APER_SIZE
};
typedef struct _gatt_mask {
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 */
} gatt_mask;
};
typedef struct _aper_size_info_8 {
struct aper_size_info_8 {
int size;
int num_entries;
int page_order;
u8 size_value;
} aper_size_info_8;
};
typedef struct _aper_size_info_16 {
struct aper_size_info_16 {
int size;
int num_entries;
int page_order;
u16 size_value;
} aper_size_info_16;
};
typedef struct _aper_size_info_32 {
struct aper_size_info_32 {
int size;
int num_entries;
int page_order;
u32 size_value;
} aper_size_info_32;
};
typedef struct _aper_size_info_lvl2 {
struct aper_size_info_lvl2 {
int size;
int num_entries;
u32 size_value;
} aper_size_info_lvl2;
};
typedef struct _aper_size_info_fixed {
struct aper_size_info_fixed {
int size;
int num_entries;
int page_order;
} aper_size_info_fixed;
};
struct agp_bridge_data {
agp_version *version;
struct agp_version *version;
void *aperture_sizes;
void *previous_size;
void *current_size;
void *dev_private_data;
struct pci_dev *dev;
gatt_mask *masks;
struct gatt_mask *masks;
unsigned long *gatt_table;
unsigned long *gatt_table_real;
unsigned long scratch_page;
......@@ -125,26 +186,26 @@ struct agp_bridge_data {
};
#define OUTREG64(mmap, addr, val) __raw_writeq((val), (mmap)+(addr))
#define OUTREG32(mmap, addr, val) __raw_writel((val), (mmap)+(addr))
#define OUTREG16(mmap, addr, val) __raw_writew((val), (mmap)+(addr))
#define OUTREG8(mmap, addr, val) __raw_writeb((val), (mmap)+(addr))
#define OUTREG64(mmap, addr, val) __raw_writeq((val), (mmap)+(addr))
#define OUTREG32(mmap, addr, val) __raw_writel((val), (mmap)+(addr))
#define OUTREG16(mmap, addr, val) __raw_writew((val), (mmap)+(addr))
#define OUTREG8(mmap, addr, val) __raw_writeb((val), (mmap)+(addr))
#define INREG64(mmap, addr) __raw_readq((mmap)+(addr))
#define INREG32(mmap, addr) __raw_readl((mmap)+(addr))
#define INREG16(mmap, addr) __raw_readw((mmap)+(addr))
#define INREG8(mmap, addr) __raw_readb((mmap)+(addr))
#define INREG64(mmap, addr) __raw_readq((mmap)+(addr))
#define INREG32(mmap, addr) __raw_readl((mmap)+(addr))
#define INREG16(mmap, addr) __raw_readw((mmap)+(addr))
#define INREG8(mmap, addr) __raw_readb((mmap)+(addr))
#define KB(x) ((x) * 1024)
#define MB(x) (KB (KB (x)))
#define GB(x) (MB (KB (x)))
#define KB(x) ((x) * 1024)
#define MB(x) (KB (KB (x)))
#define GB(x) (MB (KB (x)))
#define CACHE_FLUSH agp_bridge.cache_flush
#define A_SIZE_8(x) ((aper_size_info_8 *) x)
#define A_SIZE_16(x) ((aper_size_info_16 *) x)
#define A_SIZE_32(x) ((aper_size_info_32 *) x)
#define A_SIZE_LVL2(x) ((aper_size_info_lvl2 *) x)
#define A_SIZE_FIX(x) ((aper_size_info_fixed *) 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() (A_SIZE_8(agp_bridge.aperture_sizes) + i)
#define A_IDX16() (A_SIZE_16(agp_bridge.aperture_sizes) + i)
#define A_IDX32() (A_SIZE_32(agp_bridge.aperture_sizes) + i)
......@@ -152,91 +213,88 @@ struct agp_bridge_data {
#define A_IDXFIX() (A_SIZE_FIX(agp_bridge.aperture_sizes) + i)
#define MAXKEY (4096 * 32)
#define AGPGART_MODULE_NAME "agpgart"
#define PFX AGPGART_MODULE_NAME ": "
#define PGE_EMPTY(p) (!(p) || (p) == (unsigned long) agp_bridge.scratch_page)
#define PGE_EMPTY(p) (!(p) || (p) == (unsigned long) agp_bridge.scratch_page)
#ifndef PCI_DEVICE_ID_VIA_82C691_0
#define PCI_DEVICE_ID_VIA_82C691_0 0x0691
#define PCI_DEVICE_ID_VIA_82C691_0 0x0691
#endif
#ifndef PCI_DEVICE_ID_VIA_8371_0
#define PCI_DEVICE_ID_VIA_8371_0 0x0391
#define PCI_DEVICE_ID_VIA_8371_0 0x0391
#endif
#ifndef PCI_DEVICE_ID_VIA_8363_0
#define PCI_DEVICE_ID_VIA_8363_0 0x0305
#define PCI_DEVICE_ID_VIA_8363_0 0x0305
#endif
#ifndef PCI_DEVICE_ID_VIA_82C694X_0
#define PCI_DEVICE_ID_VIA_82C694X_0 0x0605
#define PCI_DEVICE_ID_VIA_82C694X_0 0x0605
#endif
#ifndef PCI_DEVICE_ID_INTEL_810_0
#define PCI_DEVICE_ID_INTEL_810_0 0x7120
#define PCI_DEVICE_ID_INTEL_810_0 0x7120
#endif
#ifndef PCI_DEVICE_ID_INTEL_845_G_0
#define PCI_DEVICE_ID_INTEL_845_G_0 0x2560
#endif
#ifndef PCI_DEVICE_ID_INTEL_845_G_1
#define PCI_DEVICE_ID_INTEL_845_G_1 0x2562
#define PCI_DEVICE_ID_INTEL_845_G_1 0x2562
#endif
#ifndef PCI_DEVICE_ID_INTEL_830_M_0
#define PCI_DEVICE_ID_INTEL_830_M_0 0x3575
#endif
#ifndef PCI_DEVICE_ID_INTEL_830_M_1
#define PCI_DEVICE_ID_INTEL_830_M_1 0x3577
#define PCI_DEVICE_ID_INTEL_830_M_1 0x3577
#endif
#ifndef PCI_DEVICE_ID_INTEL_820_0
#define PCI_DEVICE_ID_INTEL_820_0 0x2500
#define PCI_DEVICE_ID_INTEL_820_0 0x2500
#endif
#ifndef PCI_DEVICE_ID_INTEL_820_UP_0
#define PCI_DEVICE_ID_INTEL_820_UP_0 0x2501
#define PCI_DEVICE_ID_INTEL_820_UP_0 0x2501
#endif
#ifndef PCI_DEVICE_ID_INTEL_840_0
#define PCI_DEVICE_ID_INTEL_840_0 0x1a21
#define PCI_DEVICE_ID_INTEL_840_0 0x1a21
#endif
#ifndef PCI_DEVICE_ID_INTEL_845_0
#define PCI_DEVICE_ID_INTEL_845_0 0x1a30
#define PCI_DEVICE_ID_INTEL_845_0 0x1a30
#endif
#ifndef PCI_DEVICE_ID_INTEL_850_0
#define PCI_DEVICE_ID_INTEL_850_0 0x2530
#define PCI_DEVICE_ID_INTEL_850_0 0x2530
#endif
#ifndef PCI_DEVICE_ID_INTEL_860_0
#define PCI_DEVICE_ID_INTEL_860_0 0x2531
#define PCI_DEVICE_ID_INTEL_860_0 0x2531
#endif
#ifndef PCI_DEVICE_ID_INTEL_810_DC100_0
#define PCI_DEVICE_ID_INTEL_810_DC100_0 0x7122
#define PCI_DEVICE_ID_INTEL_810_DC100_0 0x7122
#endif
#ifndef PCI_DEVICE_ID_INTEL_810_E_0
#define PCI_DEVICE_ID_INTEL_810_E_0 0x7124
#define PCI_DEVICE_ID_INTEL_810_E_0 0x7124
#endif
#ifndef PCI_DEVICE_ID_INTEL_82443GX_0
#define PCI_DEVICE_ID_INTEL_82443GX_0 0x71a0
#define PCI_DEVICE_ID_INTEL_82443GX_0 0x71a0
#endif
#ifndef PCI_DEVICE_ID_INTEL_810_1
#define PCI_DEVICE_ID_INTEL_810_1 0x7121
#define PCI_DEVICE_ID_INTEL_810_1 0x7121
#endif
#ifndef PCI_DEVICE_ID_INTEL_810_DC100_1
#define PCI_DEVICE_ID_INTEL_810_DC100_1 0x7123
#define PCI_DEVICE_ID_INTEL_810_DC100_1 0x7123
#endif
#ifndef PCI_DEVICE_ID_INTEL_810_E_1
#define PCI_DEVICE_ID_INTEL_810_E_1 0x7125
#define PCI_DEVICE_ID_INTEL_810_E_1 0x7125
#endif
#ifndef PCI_DEVICE_ID_INTEL_815_0
#define PCI_DEVICE_ID_INTEL_815_0 0x1130
#define PCI_DEVICE_ID_INTEL_815_0 0x1130
#endif
#ifndef PCI_DEVICE_ID_INTEL_815_1
#define PCI_DEVICE_ID_INTEL_815_1 0x1132
#define PCI_DEVICE_ID_INTEL_815_1 0x1132
#endif
#ifndef PCI_DEVICE_ID_INTEL_82443GX_1
#define PCI_DEVICE_ID_INTEL_82443GX_1 0x71a1
#define PCI_DEVICE_ID_INTEL_82443GX_1 0x71a1
#endif
#ifndef PCI_DEVICE_ID_INTEL_460GX
#define PCI_DEVICE_ID_INTEL_460GX 0x84ea
#define PCI_DEVICE_ID_INTEL_460GX 0x84ea
#endif
#ifndef PCI_DEVICE_ID_AMD_IRONGATE_0
#define PCI_DEVICE_ID_AMD_IRONGATE_0 0x7006
#define PCI_DEVICE_ID_AMD_IRONGATE_0 0x7006
#endif
#ifndef PCI_DEVICE_ID_AMD_761_0
#define PCI_DEVICE_ID_AMD_761_0 0x700e
#define PCI_DEVICE_ID_AMD_761_0 0x700e
#endif
#ifndef PCI_DEVICE_ID_AMD_762_0
#define PCI_DEVICE_ID_AMD_762_0 0x700C
......@@ -270,12 +328,12 @@ struct agp_bridge_data {
#endif
/* intel register */
#define INTEL_APBASE 0x10
#define INTEL_APSIZE 0xb4
#define INTEL_ATTBASE 0xb8
#define INTEL_AGPCTRL 0xb0
#define INTEL_NBXCFG 0x50
#define INTEL_ERRSTS 0x91
#define INTEL_APBASE 0x10
#define INTEL_APSIZE 0xb4
#define INTEL_ATTBASE 0xb8
#define INTEL_AGPCTRL 0xb0
#define INTEL_NBXCFG 0x50
#define INTEL_ERRSTS 0x91
/* Intel 460GX Registers */
#define INTEL_I460_APBASE 0x10
......@@ -287,116 +345,120 @@ struct agp_bridge_data {
#define INTEL_I460_GATT_COHERENT (1UL << 25)
/* 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)
#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
#define INTEL_I830_ERRSTS 0x92
/* 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
#define INTEL_I820_RDCR 0x51
#define INTEL_I820_ERRSTS 0xc8
/* intel i840 registers */
#define INTEL_I840_MCHCFG 0x50
#define INTEL_I840_ERRSTS 0xc8
#define INTEL_I840_MCHCFG 0x50
#define INTEL_I840_ERRSTS 0xc8
/* intel i845 registers */
#define INTEL_I845_AGPM 0x51
#define INTEL_I845_ERRSTS 0xc8
#define INTEL_I845_AGPM 0x51
#define INTEL_I845_ERRSTS 0xc8
/* intel i850 registers */
#define INTEL_I850_MCHCFG 0x50
#define INTEL_I850_ERRSTS 0xc8
#define INTEL_I850_MCHCFG 0x50
#define INTEL_I850_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
#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
/* VIA register */
#define VIA_APBASE 0x10
#define VIA_GARTCTRL 0x80
#define VIA_APSIZE 0x84
#define VIA_ATTBASE 0x88
#define VIA_APBASE 0x10
#define VIA_GARTCTRL 0x80
#define VIA_APSIZE 0x84
#define VIA_ATTBASE 0x88
/* SiS registers */
#define SIS_APBASE 0x10
#define SIS_ATTBASE 0x90
#define SIS_APSIZE 0x94
#define SIS_TLBCNTRL 0x97
#define SIS_TLBFLUSH 0x98
#define SIS_APBASE 0x10
#define SIS_ATTBASE 0x90
#define SIS_APSIZE 0x94
#define SIS_TLBCNTRL 0x97
#define SIS_TLBFLUSH 0x98
/* AMD registers */
#define AMD_APBASE 0x10
#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) */
#define AMD_APBASE 0x10
#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) */
/* ALi registers */
#define ALI_APBASE 0x10
#define ALI_AGPCTRL 0xb8
#define ALI_ATTBASE 0xbc
#define ALI_TLBCTRL 0xc0
#define ALI_TAGCTRL 0xc4
#define ALI_CACHE_FLUSH_CTRL 0xD0
#define ALI_APBASE 0x10
#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
#define ALI_CACHE_FLUSH_EN 0x100
/* Serverworks Registers */
#define SVWRKS_APSIZE 0x10
#define SVWRKS_SIZE_MASK 0xfe000000
#define SVWRKS_APSIZE 0x10
#define SVWRKS_SIZE_MASK 0xfe000000
#define SVWRKS_MMBASE 0x14
#define SVWRKS_CACHING 0x4b
#define SVWRKS_FEATURE 0x68
#define SVWRKS_MMBASE 0x14
#define SVWRKS_CACHING 0x4b
#define SVWRKS_FEATURE 0x68
/* func 1 registers */
#define SVWRKS_AGP_ENABLE 0x60
#define SVWRKS_COMMAND 0x04
#define SVWRKS_AGP_ENABLE 0x60
#define SVWRKS_COMMAND 0x04
/* Memory mapped registers */
#define SVWRKS_GART_CACHE 0x02
#define SVWRKS_GATTBASE 0x04
#define SVWRKS_TLBFLUSH 0x10
#define SVWRKS_POSTFLUSH 0x14
#define SVWRKS_DIRFLUSH 0x0c
#define SVWRKS_GART_CACHE 0x02
#define SVWRKS_GATTBASE 0x04
#define SVWRKS_TLBFLUSH 0x10
#define SVWRKS_POSTFLUSH 0x14
#define SVWRKS_DIRFLUSH 0x0c
/* HP ZX1 SBA registers */
#define HP_ZX1_CTRL 0x200
......
/*
* AGPGART module version 0.99
* 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/types.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/agp_backend.h>
#include "agp.h"
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.aperture_sizes);
for (i = 0; i < agp_bridge.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(agp_memory * mem)
{
u32 temp;
pci_read_config_dword(agp_bridge.dev, ALI_TLBCTRL, &temp);
// clear tag
pci_write_config_dword(agp_bridge.dev, ALI_TAGCTRL,
((temp & 0xfffffff0) | 0x00000001|0x00000002));
}
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 */
/*
* Question: Jeff, ALi's patch deletes this:
*
* pci_read_config_dword(agp_bridge.dev, ALI_TLBCTRL, &temp);
* pci_write_config_dword(agp_bridge.dev, ALI_TLBCTRL,
* ((temp & 0xffffff00) | 0x00000010));
*
* and replaces it with the following, which seems to duplicate the
* next couple of lines below it. I suspect this was an oversight,
* but you might want to check up on this?
*/
pci_read_config_dword(agp_bridge.dev, ALI_APBASE, &temp);
agp_bridge.gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
/* address to map to */
pci_read_config_dword(agp_bridge.dev, ALI_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 unsigned long ali_mask_memory(unsigned long addr, int type)
{
/* Memory type is ignored */
return addr | agp_bridge.masks[0].mask;
}
static void ali_cache_flush(void)
{
global_cache_flush();
if (agp_bridge.type == ALI_M1541) {
int i, page_count;
u32 temp;
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 *ali_alloc_page(void)
{
void *adr = agp_generic_alloc_page();
u32 temp;
if (adr == 0)
return 0;
if (agp_bridge.type == ALI_M1541) {
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(adr)) |
ALI_CACHE_FLUSH_EN ));
}
return adr;
}
static void ali_destroy_page(void * addr)
{
u32 temp;
if (addr == NULL)
return;
global_cache_flush();
if (agp_bridge.type == ALI_M1541) {
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 gatt_mask ali_generic_masks[] =
{
{mask: 0x00000000, type: 0}
};
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}
};
int __init ali_generic_setup (struct pci_dev *pdev)
{
agp_bridge.masks = ali_generic_masks;
agp_bridge.num_of_masks = 1;
agp_bridge.aperture_sizes = (void *) ali_generic_sizes;
agp_bridge.size_type = U32_APER_SIZE;
agp_bridge.num_aperture_sizes = 7;
agp_bridge.dev_private_data = NULL;
agp_bridge.needs_scratch_page = FALSE;
agp_bridge.configure = ali_configure;
agp_bridge.fetch_size = ali_fetch_size;
agp_bridge.cleanup = ali_cleanup;
agp_bridge.tlb_flush = ali_tlbflush;
agp_bridge.mask_memory = ali_mask_memory;
agp_bridge.agp_enable = agp_generic_agp_enable;
agp_bridge.cache_flush = ali_cache_flush;
agp_bridge.create_gatt_table = agp_generic_create_gatt_table;
agp_bridge.free_gatt_table = agp_generic_free_gatt_table;
agp_bridge.insert_memory = agp_generic_insert_memory;
agp_bridge.remove_memory = agp_generic_remove_memory;
agp_bridge.alloc_by_type = agp_generic_alloc_by_type;
agp_bridge.free_by_type = agp_generic_free_by_type;
agp_bridge.agp_alloc_page = ali_alloc_page;
agp_bridge.agp_destroy_page = ali_destroy_page;
agp_bridge.suspend = agp_generic_suspend;
agp_bridge.resume = agp_generic_resume;
agp_bridge.cant_use_aperture = 0;
return 0;
(void) pdev; /* unused */
}
/*
* AGPGART module version 0.99
* 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/module.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/agp_backend.h>
#include "agp.h"
struct amd_page_map {
unsigned long *real;
unsigned long *remapped;
};
static struct _amd_irongate_private {
volatile u8 *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));
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;
}
CACHE_FLUSH();
for(i = 0; i < PAGE_SIZE / sizeof(unsigned long); i++) {
page_map->remapped[i] = agp_bridge.scratch_page;
}
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);
}
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(void)
{
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 = page_dir.real;
agp_bridge.gatt_table = 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, AMD_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) {
page_dir.remapped[GET_PAGE_DIR_OFF(addr)] =
virt_to_phys(amd_irongate_private.gatt_pages[i]->real);
page_dir.remapped[GET_PAGE_DIR_OFF(addr)] |= 0x00000001;
}
return 0;
}
static int amd_free_gatt_table(void)
{
struct amd_page_map page_dir;
page_dir.real = agp_bridge.gatt_table_real;
page_dir.remapped = 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.aperture_sizes);
for (i = 0; i < agp_bridge.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 *) ioremap(temp, 4096);
/* Write out the address of the gatt table */
OUTREG32(amd_irongate_private.registers, AMD_ATTBASE,
agp_bridge.gatt_bus_addr);
/* 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 = INREG16(amd_irongate_private.registers, AMD_GARTENABLE);
enable_reg = (enable_reg | 0x0004);
OUTREG16(amd_irongate_private.registers, AMD_GARTENABLE, enable_reg);
/* Write out the size register */
pci_read_config_dword(agp_bridge.dev, AMD_APSIZE, &temp);
temp = (((temp & ~(0x0000000e)) | current_size->size_value)
| 0x00000001);
pci_write_config_dword(agp_bridge.dev, AMD_APSIZE, temp);
/* Flush the tlb */
OUTREG32(amd_irongate_private.registers, AMD_TLBFLUSH, 0x00000001);
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 = INREG16(amd_irongate_private.registers, AMD_GARTENABLE);
enable_reg = (enable_reg & ~(0x0004));
OUTREG16(amd_irongate_private.registers, AMD_GARTENABLE, enable_reg);
/* 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 *) 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(agp_memory * temp)
{
OUTREG32(amd_irongate_private.registers, AMD_TLBFLUSH, 0x00000001);
}
static unsigned long amd_irongate_mask_memory(unsigned long addr, int type)
{
/* Only type 0 is supported by the irongate */
return addr | agp_bridge.masks[0].mask;
}
static int amd_insert_memory(agp_memory * mem,
off_t pg_start, int type)
{
int i, j, num_entries;
unsigned long *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(cur_gatt[GET_GATT_OFF(addr)])) {
return -EBUSY;
}
j++;
}
if (mem->is_flushed == FALSE) {
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);
cur_gatt[GET_GATT_OFF(addr)] = mem->memory[i];
}
agp_bridge.tlb_flush(mem);
return 0;
}
static int amd_remove_memory(agp_memory * mem, off_t pg_start,
int type)
{
int i;
unsigned long *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);
cur_gatt[GET_GATT_OFF(addr)] =
(unsigned long) agp_bridge.scratch_page;
}
agp_bridge.tlb_flush(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: 0x00000001, type: 0}
};
int __init amd_irongate_setup (struct pci_dev *pdev)
{
agp_bridge.masks = amd_irongate_masks;
agp_bridge.num_of_masks = 1;
agp_bridge.aperture_sizes = (void *) amd_irongate_sizes;
agp_bridge.size_type = LVL2_APER_SIZE;
agp_bridge.num_aperture_sizes = 7;
agp_bridge.dev_private_data = (void *) &amd_irongate_private;
agp_bridge.needs_scratch_page = FALSE;
agp_bridge.configure = amd_irongate_configure;
agp_bridge.fetch_size = amd_irongate_fetch_size;
agp_bridge.cleanup = amd_irongate_cleanup;
agp_bridge.tlb_flush = amd_irongate_tlbflush;
agp_bridge.mask_memory = amd_irongate_mask_memory;
agp_bridge.agp_enable = agp_generic_agp_enable;
agp_bridge.cache_flush = global_cache_flush;
agp_bridge.create_gatt_table = amd_create_gatt_table;
agp_bridge.free_gatt_table = amd_free_gatt_table;
agp_bridge.insert_memory = amd_insert_memory;
agp_bridge.remove_memory = amd_remove_memory;
agp_bridge.alloc_by_type = agp_generic_alloc_by_type;
agp_bridge.free_by_type = agp_generic_free_by_type;
agp_bridge.agp_alloc_page = agp_generic_alloc_page;
agp_bridge.agp_destroy_page = agp_generic_destroy_page;
agp_bridge.suspend = agp_generic_suspend;
agp_bridge.resume = agp_generic_resume;
agp_bridge.cant_use_aperture = 0;
return 0;
(void) pdev; /* unused */
}
/*
* AGPGART module version 0.99
* 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/module.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/agp_backend.h>
#include "agp.h"
#ifndef log2
#define log2(x) ffz(~(x))
#endif
#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)
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 {
struct pci_dev *ioc;
volatile u8 *registers;
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 = INREG64(hp->registers, 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 = 0;
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 = INREG64(hp->registers, 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(INREG64(hp->registers, 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) {
hp->gatt = 0;
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(u8 ioc_rev)
{
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(void)
{
struct _hp_private *hp = &hp_private;
struct pci_dev *ioc;
int i;
u8 ioc_rev;
ioc = pci_find_device(PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_ZX1_IOC, NULL);
if (!ioc) {
printk(KERN_ERR PFX "Detected HP ZX1 AGP bridge but no IOC\n");
return -ENODEV;
}
hp->ioc = ioc;
pci_read_config_byte(ioc, PCI_REVISION_ID, &ioc_rev);
for (i = 0; i < PCI_NUM_RESOURCES; i++) {
if (pci_resource_flags(ioc, i) == IORESOURCE_MEM) {
hp->registers = (u8 *) ioremap(pci_resource_start(ioc,
i),
pci_resource_len(ioc, i));
break;
}
}
if (!hp->registers) {
printk(KERN_ERR PFX "Detected HP ZX1 AGP bridge but no CSRs\n");
return -ENODEV;
}
/*
* If the IOTLB is currently disabled, we can take it over.
* Otherwise, we have to share with sba_iommu.
*/
hp->io_pdir_owner = (INREG64(hp->registers, HP_ZX1_IBASE) & 0x1) == 0;
if (hp->io_pdir_owner)
return hp_zx1_ioc_owner(ioc_rev);
return hp_zx1_ioc_shared();
}
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 = pci_find_capability(agp_bridge.dev, PCI_CAP_ID_AGP);
pci_read_config_dword(agp_bridge.dev,
agp_bridge.capndx + PCI_AGP_STATUS, &agp_bridge.mode);
if (hp->io_pdir_owner) {
OUTREG64(hp->registers, HP_ZX1_PDIR_BASE,
virt_to_phys(hp->io_pdir));
OUTREG64(hp->registers, HP_ZX1_TCNFG, hp->io_tlb_ps);
OUTREG64(hp->registers, HP_ZX1_IMASK, ~(HP_ZX1_IOVA_SIZE - 1));
OUTREG64(hp->registers, HP_ZX1_IBASE, hp->iova_base | 0x1);
OUTREG64(hp->registers, HP_ZX1_PCOM,
hp->iova_base | log2(HP_ZX1_IOVA_SIZE));
INREG64(hp->registers, HP_ZX1_PCOM);
}
return 0;
}
static void hp_zx1_cleanup(void)
{
struct _hp_private *hp = &hp_private;
if (hp->io_pdir_owner)
OUTREG64(hp->registers, HP_ZX1_IBASE, 0);
iounmap((void *) hp->registers);
}
static void hp_zx1_tlbflush(agp_memory * mem)
{
struct _hp_private *hp = &hp_private;
OUTREG64(hp->registers, HP_ZX1_PCOM,
hp->gart_base | log2(hp->gart_size));
INREG64(hp->registers, HP_ZX1_PCOM);
}
static int hp_zx1_create_gatt_table(void)
{
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 = 0;
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(void)
{
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(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) {
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.mask_memory(paddr, type);
}
}
agp_bridge.tlb_flush(mem);
return 0;
}
static int hp_zx1_remove_memory(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.tlb_flush(mem);
return 0;
}
static unsigned long hp_zx1_mask_memory(unsigned long addr, int type)
{
return HP_ZX1_PDIR_VALID_BIT | addr;
}
static unsigned long hp_zx1_unmask_memory(unsigned long addr)
{
return addr & ~(HP_ZX1_PDIR_VALID_BIT);
}
int __init hp_zx1_setup (struct pci_dev *pdev)
{
agp_bridge.masks = hp_zx1_masks;
agp_bridge.num_of_masks = 1;
agp_bridge.dev_private_data = NULL;
agp_bridge.size_type = FIXED_APER_SIZE;
agp_bridge.needs_scratch_page = FALSE;
agp_bridge.configure = hp_zx1_configure;
agp_bridge.fetch_size = hp_zx1_fetch_size;
agp_bridge.cleanup = hp_zx1_cleanup;
agp_bridge.tlb_flush = hp_zx1_tlbflush;
agp_bridge.mask_memory = hp_zx1_mask_memory;
agp_bridge.unmask_memory = hp_zx1_unmask_memory;
agp_bridge.agp_enable = agp_generic_agp_enable;
agp_bridge.cache_flush = global_cache_flush;
agp_bridge.create_gatt_table = hp_zx1_create_gatt_table;
agp_bridge.free_gatt_table = hp_zx1_free_gatt_table;
agp_bridge.insert_memory = hp_zx1_insert_memory;
agp_bridge.remove_memory = hp_zx1_remove_memory;
agp_bridge.alloc_by_type = agp_generic_alloc_by_type;
agp_bridge.free_by_type = agp_generic_free_by_type;
agp_bridge.agp_alloc_page = agp_generic_alloc_page;
agp_bridge.agp_destroy_page = agp_generic_destroy_page;
agp_bridge.cant_use_aperture = 1;
return hp_zx1_ioc_init();
(void) pdev; /* unused */
}
/*
* AGPGART module version 0.99
* 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/module.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/agp_backend.h>
#include "agp.h"
/* BIOS configures the chipset so that one of two apbase registers are used */
static u8 intel_i460_dynamic_apbase = 0x10;
/* 460 supports multiple GART page sizes, so GART pageshift is dynamic */
static u8 intel_i460_pageshift = 12;
static u32 intel_i460_pagesize;
/* Keep track of which is larger, chipset or kernel page size. */
static u32 intel_i460_cpk = 1;
/* Structure for tracking partial use of 4MB GART pages */
static u32 **i460_pg_detail = NULL;
static u32 *i460_pg_count = NULL;
#define I460_CPAGES_PER_KPAGE (PAGE_SIZE >> intel_i460_pageshift)
#define I460_KPAGES_PER_CPAGE ((1 << intel_i460_pageshift) >> 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)
#define log2(x) ffz(~(x))
static inline void intel_i460_read_back (volatile u32 *entry)
{
/*
* The 460 spec says we have to read the last location written to
* make sure that all writes have taken effect
*/
*entry;
}
static int intel_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);
intel_i460_pageshift = (temp & I460_4M_PS) ? 22 : 12;
intel_i460_pagesize = 1UL << intel_i460_pageshift;
values = A_SIZE_8(agp_bridge.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 ((intel_i460_pageshift == 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)
intel_i460_dynamic_apbase = INTEL_I460_BAPBASE;
else
intel_i460_dynamic_apbase = INTEL_I460_APBASE;
for (i = 0; i < agp_bridge.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) >> (intel_i460_pageshift - 12);
values[i].page_order = log2((sizeof(u32)*values[i].num_entries) >> PAGE_SHIFT);
}
for (i = 0; i < agp_bridge.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 intel_i460_tlb_flush(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 intel_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 intel_i460_cleanup(void)
{
struct aper_size_info_8 *previous_size;
previous_size = A_SIZE_8(agp_bridge.previous_size);
intel_i460_write_agpsiz(previous_size->size_value);
if (intel_i460_cpk == 0) {
vfree(i460_pg_detail);
vfree(i460_pg_count);
}
}
/* Control bits for Out-Of-GART coherency and Burst Write Combining */
#define I460_GXBCTL_OOG (1UL << 0)
#define I460_GXBCTL_BWC (1UL << 2)
static int intel_i460_configure(void)
{
union {
u32 small[2];
u64 large;
} temp;
u8 scratch;
int i;
struct aper_size_info_8 *current_size;
temp.large = 0;
current_size = A_SIZE_8(agp_bridge.current_size);
intel_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, intel_i460_dynamic_apbase, &(temp.small[0]));
pci_read_config_dword(agp_bridge.dev, intel_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_CPAGES_PER_KPAGE >= 1) {
intel_i460_cpk = 1;
} else {
intel_i460_cpk = 0;
i460_pg_detail = vmalloc(sizeof(*i460_pg_detail) * current_size->num_entries);
i460_pg_count = vmalloc(sizeof(*i460_pg_count) * current_size->num_entries);
for (i = 0; i < current_size->num_entries; i++) {
i460_pg_count[i] = 0;
i460_pg_detail[i] = NULL;
}
}
return 0;
}
static int intel_i460_create_gatt_table(void)
{
char *table;
int i;
int page_order;
int num_entries;
void *temp;
/*
* Load up the fixed address of the GART SRAMS which hold our
* GATT table.
*/
table = (char *) __va(INTEL_I460_ATTBASE);
temp = agp_bridge.current_size;
page_order = A_SIZE_8(temp)->page_order;
num_entries = A_SIZE_8(temp)->num_entries;
agp_bridge.gatt_table_real = (u32 *) table;
agp_bridge.gatt_table = ioremap_nocache(virt_to_phys(table),
(PAGE_SIZE * (1 << page_order)));
agp_bridge.gatt_bus_addr = virt_to_phys(agp_bridge.gatt_table_real);
for (i = 0; i < num_entries; i++) {
agp_bridge.gatt_table[i] = 0;
}
intel_i460_read_back(agp_bridge.gatt_table + i - 1);
return 0;
}
static int intel_i460_free_gatt_table(void)
{
int num_entries;
int i;
void *temp;
temp = agp_bridge.current_size;
num_entries = A_SIZE_8(temp)->num_entries;
for (i = 0; i < num_entries; i++) {
agp_bridge.gatt_table[i] = 0;
}
intel_i460_read_back(agp_bridge.gatt_table + i - 1);
iounmap(agp_bridge.gatt_table);
return 0;
}
/* These functions are called when PAGE_SIZE exceeds the GART page size */
static int intel_i460_insert_memory_cpk(agp_memory * mem, off_t pg_start, int type)
{
int i, j, k, num_entries;
void *temp;
unsigned long paddr;
/*
* The rest of the kernel will compute page offsets in terms of
* PAGE_SIZE.
*/
pg_start = I460_CPAGES_PER_KPAGE * pg_start;
temp = agp_bridge.current_size;
num_entries = A_SIZE_8(temp)->num_entries;
if ((pg_start + I460_CPAGES_PER_KPAGE * mem->page_count) > num_entries) {
printk(KERN_ERR PFX "Looks like we're out of AGP memory\n");
return -EINVAL;
}
j = pg_start;
while (j < (pg_start + I460_CPAGES_PER_KPAGE * mem->page_count)) {
if (!PGE_EMPTY(agp_bridge.gatt_table[j])) {
return -EBUSY;
}
j++;
}
#if 0
/* not necessary since 460 GART is operated in coherent mode... */
if (mem->is_flushed == FALSE) {
CACHE_FLUSH();
mem->is_flushed = TRUE;
}
#endif
for (i = 0, j = pg_start; i < mem->page_count; i++) {
paddr = mem->memory[i];
for (k = 0; k < I460_CPAGES_PER_KPAGE; k++, j++, paddr += intel_i460_pagesize)
agp_bridge.gatt_table[j] = (u32) agp_bridge.mask_memory(paddr, mem->type);
}
intel_i460_read_back(agp_bridge.gatt_table + j - 1);
return 0;
}
static int intel_i460_remove_memory_cpk(agp_memory * mem, off_t pg_start, int type)
{
int i;
pg_start = I460_CPAGES_PER_KPAGE * pg_start;
for (i = pg_start; i < (pg_start + I460_CPAGES_PER_KPAGE * mem->page_count); i++)
agp_bridge.gatt_table[i] = 0;
intel_i460_read_back(agp_bridge.gatt_table + i - 1);
return 0;
}
/*
* These functions are called when the 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 structures i460_pg_count
* and i460_pg_detail track partial allocation of the large GART pages to
* work around this issue.
*
* i460_pg_count[pg_num] tracks the number of kernel pages in use within
* GART page pg_num. i460_pg_detail[pg_num] is an array containing a
* psuedo-GART entry for each of the aforementioned kernel pages. The whole
* of i460_pg_detail is equivalent to a giant GATT with page size equal to
* that of the kernel.
*/
static void *intel_i460_alloc_large_page(int pg_num)
{
int i;
void *bp, *bp_end;
struct page *page;
i460_pg_detail[pg_num] = (void *) vmalloc(sizeof(u32) * I460_KPAGES_PER_CPAGE);
if (i460_pg_detail[pg_num] == NULL) {
printk(KERN_ERR PFX "Out of memory, we're in trouble...\n");
return NULL;
}
for (i = 0; i < I460_KPAGES_PER_CPAGE; i++)
i460_pg_detail[pg_num][i] = 0;
bp = (void *) __get_free_pages(GFP_KERNEL, intel_i460_pageshift - PAGE_SHIFT);
if (bp == NULL) {
printk(KERN_ERR PFX "Couldn't alloc 4M GART page...\n");
return NULL;
}
bp_end = bp + ((PAGE_SIZE * (1 << (intel_i460_pageshift - PAGE_SHIFT))) - 1);
for (page = virt_to_page(bp); page <= virt_to_page(bp_end); page++) {
atomic_inc(&agp_bridge.current_memory_agp);
}
return bp;
}
static void intel_i460_free_large_page(int pg_num, unsigned long addr)
{
struct page *page;
void *bp, *bp_end;
bp = (void *) __va(addr);
bp_end = bp + (PAGE_SIZE * (1 << (intel_i460_pageshift - PAGE_SHIFT)));
vfree(i460_pg_detail[pg_num]);
i460_pg_detail[pg_num] = NULL;
for (page = virt_to_page(bp); page < virt_to_page(bp_end); page++) {
atomic_dec(&agp_bridge.current_memory_agp);
}
free_pages((unsigned long) bp, intel_i460_pageshift - PAGE_SHIFT);
}
static int intel_i460_insert_memory_kpc(agp_memory * mem, off_t pg_start, int type)
{
int i, pg, start_pg, end_pg, start_offset, end_offset, idx;
int num_entries;
void *temp;
unsigned long paddr;
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_pg = pg_start / I460_KPAGES_PER_CPAGE;
start_offset = pg_start % I460_KPAGES_PER_CPAGE;
end_pg = (pg_start + mem->page_count - 1) / I460_KPAGES_PER_CPAGE;
end_offset = (pg_start + mem->page_count - 1) % I460_KPAGES_PER_CPAGE;
if (end_pg > 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 (pg = start_pg; pg <= end_pg; pg++) {
/* Allocate new GART pages if necessary */
if (i460_pg_detail[pg] == NULL) {
temp = intel_i460_alloc_large_page(pg);
if (temp == NULL)
return -ENOMEM;
agp_bridge.gatt_table[pg] = agp_bridge.mask_memory((unsigned long) temp,
0);
intel_i460_read_back(agp_bridge.gatt_table + pg);
}
for (idx = ((pg == start_pg) ? start_offset : 0);
idx < ((pg == end_pg) ? (end_offset + 1) : I460_KPAGES_PER_CPAGE);
idx++)
{
if (i460_pg_detail[pg][idx] != 0)
return -EBUSY;
}
}
#if 0
/* not necessary since 460 GART is operated in coherent mode... */
if (mem->is_flushed == FALSE) {
CACHE_FLUSH();
mem->is_flushed = TRUE;
}
#endif
for (pg = start_pg, i = 0; pg <= end_pg; pg++) {
paddr = agp_bridge.unmask_memory(agp_bridge.gatt_table[pg]);
for (idx = ((pg == start_pg) ? start_offset : 0);
idx < ((pg == end_pg) ? (end_offset + 1) : I460_KPAGES_PER_CPAGE);
idx++, i++)
{
mem->memory[i] = paddr + (idx * PAGE_SIZE);
i460_pg_detail[pg][idx] = agp_bridge.mask_memory(mem->memory[i],
mem->type);
i460_pg_count[pg]++;
}
}
return 0;
}
static int intel_i460_remove_memory_kpc(agp_memory * mem, off_t pg_start, int type)
{
int i, pg, start_pg, end_pg, start_offset, end_offset, idx;
int num_entries;
void *temp;
unsigned long paddr;
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_pg = pg_start / I460_KPAGES_PER_CPAGE;
start_offset = pg_start % I460_KPAGES_PER_CPAGE;
end_pg = (pg_start + mem->page_count - 1) / I460_KPAGES_PER_CPAGE;
end_offset = (pg_start + mem->page_count - 1) % I460_KPAGES_PER_CPAGE;
for (i = 0, pg = start_pg; pg <= end_pg; pg++) {
for (idx = ((pg == start_pg) ? start_offset : 0);
idx < ((pg == end_pg) ? (end_offset + 1) : I460_KPAGES_PER_CPAGE);
idx++, i++)
{
mem->memory[i] = 0;
i460_pg_detail[pg][idx] = 0;
i460_pg_count[pg]--;
}
/* Free GART pages if they are unused */
if (i460_pg_count[pg] == 0) {
paddr = agp_bridge.unmask_memory(agp_bridge.gatt_table[pg]);
agp_bridge.gatt_table[pg] = agp_bridge.scratch_page;
intel_i460_read_back(agp_bridge.gatt_table + pg);
intel_i460_free_large_page(pg, paddr);
}
}
return 0;
}
/* Dummy routines to call the approriate {cpk,kpc} function */
static int intel_i460_insert_memory(agp_memory * mem, off_t pg_start, int type)
{
if (intel_i460_cpk)
return intel_i460_insert_memory_cpk(mem, pg_start, type);
else
return intel_i460_insert_memory_kpc(mem, pg_start, type);
}
static int intel_i460_remove_memory(agp_memory * mem, off_t pg_start, int type)
{
if (intel_i460_cpk)
return intel_i460_remove_memory_cpk(mem, pg_start, type);
else
return intel_i460_remove_memory_kpc(mem, pg_start, type);
}
/*
* If the kernel page size is smaller that the chipset 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). Consequently, don't allocate or free anything
* if i460_cpk (meaning chipset pages per kernel page) isn't set.
*
* 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 * intel_i460_alloc_page(void)
{
if (intel_i460_cpk)
return agp_generic_alloc_page();
/* Returning NULL would cause problems */
/* AK: really dubious code. */
return (void *)~0UL;
}
static void intel_i460_destroy_page(void *page)
{
if (intel_i460_cpk)
agp_generic_destroy_page(page);
}
static struct gatt_mask intel_i460_masks[] =
{
{
mask: INTEL_I460_GATT_VALID | INTEL_I460_GATT_COHERENT,
type: 0
}
};
static unsigned long intel_i460_mask_memory(unsigned long addr, int type)
{
/* Make sure the returned address is a valid GATT entry */
return (agp_bridge.masks[0].mask
| (((addr & ~((1 << intel_i460_pageshift) - 1)) & 0xffffff000) >> 12));
}
static unsigned long intel_i460_unmask_memory(unsigned long addr)
{
/* Turn a GATT entry into a physical address */
return ((addr & 0xffffff) << 12);
}
static struct aper_size_info_8 intel_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}
};
int __init intel_i460_setup (struct pci_dev *pdev __attribute__((unused)))
{
agp_bridge.masks = intel_i460_masks;
agp_bridge.aperture_sizes = (void *) intel_i460_sizes;
agp_bridge.size_type = U8_APER_SIZE;
agp_bridge.num_aperture_sizes = 3;
agp_bridge.dev_private_data = NULL;
agp_bridge.needs_scratch_page = FALSE;
agp_bridge.configure = intel_i460_configure;
agp_bridge.fetch_size = intel_i460_fetch_size;
agp_bridge.cleanup = intel_i460_cleanup;
agp_bridge.tlb_flush = intel_i460_tlb_flush;
agp_bridge.mask_memory = intel_i460_mask_memory;
agp_bridge.unmask_memory = intel_i460_unmask_memory;
agp_bridge.agp_enable = agp_generic_agp_enable;
agp_bridge.cache_flush = global_cache_flush;
agp_bridge.create_gatt_table = intel_i460_create_gatt_table;
agp_bridge.free_gatt_table = intel_i460_free_gatt_table;
agp_bridge.insert_memory = intel_i460_insert_memory;
agp_bridge.remove_memory = intel_i460_remove_memory;
agp_bridge.alloc_by_type = agp_generic_alloc_by_type;
agp_bridge.free_by_type = agp_generic_free_by_type;
agp_bridge.agp_alloc_page = intel_i460_alloc_page;
agp_bridge.agp_destroy_page = intel_i460_destroy_page;
agp_bridge.suspend = agp_generic_suspend;
agp_bridge.resume = agp_generic_resume;
agp_bridge.cant_use_aperture = 1;
return 0;
}
/*
* AGPGART module version 0.99
* 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/module.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/agp_backend.h>
#include "agp.h"
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 *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.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 =
(volatile u8 *) ioremap(temp, 128 * 4096);
if ((INREG32(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);
OUTREG32(intel_i810_private.registers, I810_PGETBL_CTL,
agp_bridge.gatt_bus_addr | I810_PGETBL_ENABLED);
CACHE_FLUSH();
if (agp_bridge.needs_scratch_page == TRUE) {
for (i = 0; i < current_size->num_entries; i++) {
OUTREG32(intel_i810_private.registers,
I810_PTE_BASE + (i * 4),
agp_bridge.scratch_page);
}
}
return 0;
}
static void intel_i810_cleanup(void)
{
OUTREG32(intel_i810_private.registers, I810_PGETBL_CTL, 0);
iounmap((void *) intel_i810_private.registers);
}
static void intel_i810_tlbflush(agp_memory * mem)
{
return;
}
static void intel_i810_agp_enable(u32 mode)
{
return;
}
static int intel_i810_insert_entries(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.gatt_table[j])) {
return -EBUSY;
}
}
if (type != 0 || mem->type != 0) {
if ((type == AGP_DCACHE_MEMORY) &&
(mem->type == AGP_DCACHE_MEMORY)) {
/* special insert */
CACHE_FLUSH();
for (i = pg_start;
i < (pg_start + mem->page_count); i++) {
OUTREG32(intel_i810_private.registers,
I810_PTE_BASE + (i * 4),
(i * 4096) | I810_PTE_LOCAL |
I810_PTE_VALID);
}
CACHE_FLUSH();
agp_bridge.tlb_flush(mem);
return 0;
}
if((type == AGP_PHYS_MEMORY) &&
(mem->type == AGP_PHYS_MEMORY)) {
goto insert;
}
return -EINVAL;
}
insert:
CACHE_FLUSH();
for (i = 0, j = pg_start; i < mem->page_count; i++, j++) {
OUTREG32(intel_i810_private.registers,
I810_PTE_BASE + (j * 4), mem->memory[i]);
}
CACHE_FLUSH();
agp_bridge.tlb_flush(mem);
return 0;
}
static int intel_i810_remove_entries(agp_memory * mem, off_t pg_start,
int type)
{
int i;
for (i = pg_start; i < (mem->page_count + pg_start); i++) {
OUTREG32(intel_i810_private.registers,
I810_PTE_BASE + (i * 4),
agp_bridge.scratch_page);
}
CACHE_FLUSH();
agp_bridge.tlb_flush(mem);
return 0;
}
static agp_memory *intel_i810_alloc_by_type(size_t pg_count, int type)
{
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);
MOD_INC_USE_COUNT;
return new;
}
if(type == AGP_PHYS_MEMORY) {
void *addr;
/* The I810 requires a physical address to program
* it's mouse pointer into hardware. However the
* Xserver still writes to it through the agp
* aperture
*/
if (pg_count != 1) {
return NULL;
}
new = agp_create_memory(1);
if (new == NULL) {
return NULL;
}
MOD_INC_USE_COUNT;
addr = agp_bridge.agp_alloc_page();
if (addr == NULL) {
/* Free this structure */
agp_free_memory(new);
return NULL;
}
new->memory[0] = agp_bridge.mask_memory(virt_to_phys(addr), type);
new->page_count = 1;
new->num_scratch_pages = 1;
new->type = AGP_PHYS_MEMORY;
new->physical = virt_to_phys((void *) new->memory[0]);
return new;
}
return NULL;
}
static void intel_i810_free_by_type(agp_memory * curr)
{
agp_free_key(curr->key);
if(curr->type == AGP_PHYS_MEMORY) {
agp_bridge.agp_destroy_page(
phys_to_virt(curr->memory[0]));
vfree(curr->memory);
}
kfree(curr);
MOD_DEC_USE_COUNT;
}
static unsigned long intel_i810_mask_memory(unsigned long addr, int type)
{
/* Type checking must be done elsewhere */
return addr | agp_bridge.masks[type].mask;
}
int __init intel_i810_setup(struct pci_dev *i810_dev)
{
intel_i810_private.i810_dev = i810_dev;
agp_bridge.masks = intel_i810_masks;
agp_bridge.num_of_masks = 2;
agp_bridge.aperture_sizes = (void *) intel_i810_sizes;
agp_bridge.size_type = FIXED_APER_SIZE;
agp_bridge.num_aperture_sizes = 2;
agp_bridge.dev_private_data = (void *) &intel_i810_private;
agp_bridge.needs_scratch_page = TRUE;
agp_bridge.configure = intel_i810_configure;
agp_bridge.fetch_size = intel_i810_fetch_size;
agp_bridge.cleanup = intel_i810_cleanup;
agp_bridge.tlb_flush = intel_i810_tlbflush;
agp_bridge.mask_memory = intel_i810_mask_memory;
agp_bridge.agp_enable = intel_i810_agp_enable;
agp_bridge.cache_flush = global_cache_flush;
agp_bridge.create_gatt_table = agp_generic_create_gatt_table;
agp_bridge.free_gatt_table = agp_generic_free_gatt_table;
agp_bridge.insert_memory = intel_i810_insert_entries;
agp_bridge.remove_memory = intel_i810_remove_entries;
agp_bridge.alloc_by_type = intel_i810_alloc_by_type;
agp_bridge.free_by_type = intel_i810_free_by_type;
agp_bridge.agp_alloc_page = agp_generic_alloc_page;
agp_bridge.agp_destroy_page = agp_generic_destroy_page;
agp_bridge.suspend = agp_generic_suspend;
agp_bridge.resume = agp_generic_resume;
agp_bridge.cant_use_aperture = 0;
return 0;
}
static struct aper_size_info_fixed intel_i830_sizes[] =
{
{128, 32768, 5},
/* The 64M mode still requires a 128k gatt */
{64, 16384, 5}
};
static struct _intel_i830_private {
struct pci_dev *i830_dev; /* device one */
volatile u8 *registers;
int gtt_entries;
} intel_i830_private;
static void intel_i830_init_gtt_entries(void)
{
u16 gmch_ctrl;
int gtt_entries;
u8 rdct;
static const int ddt[4] = { 0, 16, 32, 64 };
pci_read_config_word(agp_bridge.dev,I830_GMCH_CTRL,&gmch_ctrl);
switch (gmch_ctrl & I830_GMCH_GMS_MASK) {
case I830_GMCH_GMS_STOLEN_512:
gtt_entries = KB(512) - KB(132);
printk(KERN_INFO PFX "detected %dK stolen memory.\n",gtt_entries / KB(1));
break;
case I830_GMCH_GMS_STOLEN_1024:
gtt_entries = MB(1) - KB(132);
printk(KERN_INFO PFX "detected %dK stolen memory.\n",gtt_entries / KB(1));
break;
case I830_GMCH_GMS_STOLEN_8192:
gtt_entries = MB(8) - KB(132);
printk(KERN_INFO PFX "detected %dK stolen memory.\n",gtt_entries / KB(1));
break;
case I830_GMCH_GMS_LOCAL:
rdct = INREG8(intel_i830_private.registers,I830_RDRAM_CHANNEL_TYPE);
gtt_entries = (I830_RDRAM_ND(rdct) + 1) * MB(ddt[I830_RDRAM_DDT(rdct)]);
printk(KERN_INFO PFX "detected %dK local memory.\n",gtt_entries / KB(1));
break;
default:
printk(KERN_INFO PFX "no video memory detected.\n");
gtt_entries = 0;
break;
}
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(void)
{
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 = 0;
pci_read_config_dword(intel_i830_private.i830_dev,I810_MMADDR,&temp);
temp &= 0xfff80000;
intel_i830_private.registers = (volatile u8 *) ioremap(temp,128 * 4096);
if (!intel_i830_private.registers) return (-ENOMEM);
temp = INREG32(intel_i830_private.registers,I810_PGETBL_CTL) & 0xfffff000;
CACHE_FLUSH();
/* 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(void)
{
return(0);
}
static int intel_i830_fetch_size(void)
{
u16 gmch_ctrl;
struct aper_size_info_fixed *values;
pci_read_config_word(agp_bridge.dev,I830_GMCH_CTRL,&gmch_ctrl);
values = A_SIZE_FIX(agp_bridge.aperture_sizes);
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;
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);
OUTREG32(intel_i830_private.registers,I810_PGETBL_CTL,agp_bridge.gatt_bus_addr | I810_PGETBL_ENABLED);
CACHE_FLUSH();
if (agp_bridge.needs_scratch_page == TRUE)
for (i = intel_i830_private.gtt_entries; i < current_size->num_entries; i++)
OUTREG32(intel_i830_private.registers,I810_PTE_BASE + (i * 4),agp_bridge.scratch_page);
return (0);
}
static void intel_i830_cleanup(void)
{
iounmap((void *) intel_i830_private.registers);
}
static int intel_i830_insert_entries(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 "pg_start == 0x%.8lx,intel_i830_private.gtt_entries == 0x%.8x\n",
pg_start,intel_i830_private.gtt_entries);
printk ("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);
CACHE_FLUSH();
for (i = 0, j = pg_start; i < mem->page_count; i++, j++)
OUTREG32(intel_i830_private.registers,I810_PTE_BASE + (j * 4),mem->memory[i]);
CACHE_FLUSH();
agp_bridge.tlb_flush(mem);
return(0);
}
static int intel_i830_remove_entries(agp_memory *mem,off_t pg_start,int type)
{
int i;
CACHE_FLUSH ();
if (pg_start < intel_i830_private.gtt_entries) {
printk ("Trying to disable local/stolen memory\n");
return (-EINVAL);
}
for (i = pg_start; i < (mem->page_count + pg_start); i++)
OUTREG32(intel_i830_private.registers,I810_PTE_BASE + (i * 4),agp_bridge.scratch_page);
CACHE_FLUSH();
agp_bridge.tlb_flush(mem);
return (0);
}
static agp_memory *intel_i830_alloc_by_type(size_t pg_count,int type)
{
agp_memory *nw;
/* always return NULL for now */
if (type == AGP_DCACHE_MEMORY) return(NULL);
if (type == AGP_PHYS_MEMORY) {
void *addr;
/* The i830 requires a physical address to program
* it's mouse pointer into hardware. However the
* Xserver still writes to it through the agp
* aperture
*/
if (pg_count != 1) return(NULL);
nw = agp_create_memory(1);
if (nw == NULL) return(NULL);
MOD_INC_USE_COUNT;
addr = agp_bridge.agp_alloc_page();
if (addr == NULL) {
/* free this structure */
agp_free_memory(nw);
return(NULL);
}
nw->memory[0] = agp_bridge.mask_memory(virt_to_phys(addr),type);
nw->page_count = 1;
nw->num_scratch_pages = 1;
nw->type = AGP_PHYS_MEMORY;
nw->physical = virt_to_phys(addr);
return(nw);
}
return(NULL);
}
int __init intel_i830_setup(struct pci_dev *i830_dev)
{
intel_i830_private.i830_dev = i830_dev;
agp_bridge.masks = intel_i810_masks;
agp_bridge.num_of_masks = 3;
agp_bridge.aperture_sizes = (void *) intel_i830_sizes;
agp_bridge.size_type = FIXED_APER_SIZE;
agp_bridge.num_aperture_sizes = 2;
agp_bridge.dev_private_data = (void *) &intel_i830_private;
agp_bridge.needs_scratch_page = TRUE;
agp_bridge.configure = intel_i830_configure;
agp_bridge.fetch_size = intel_i830_fetch_size;
agp_bridge.cleanup = intel_i830_cleanup;
agp_bridge.tlb_flush = intel_i810_tlbflush;
agp_bridge.mask_memory = intel_i810_mask_memory;
agp_bridge.agp_enable = intel_i810_agp_enable;
agp_bridge.cache_flush = global_cache_flush;
agp_bridge.create_gatt_table = intel_i830_create_gatt_table;
agp_bridge.free_gatt_table = intel_i830_free_gatt_table;
agp_bridge.insert_memory = intel_i830_insert_entries;
agp_bridge.remove_memory = intel_i830_remove_entries;
agp_bridge.alloc_by_type = intel_i830_alloc_by_type;
agp_bridge.free_by_type = intel_i810_free_by_type;
agp_bridge.agp_alloc_page = agp_generic_alloc_page;
agp_bridge.agp_destroy_page = agp_generic_destroy_page;
agp_bridge.suspend = agp_generic_suspend;
agp_bridge.resume = agp_generic_resume;
agp_bridge.cant_use_aperture = 0;
return(0);
}
/*
* AGPGART module version 0.99
* 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/module.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/agp_backend.h>
#include "agp.h"
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.aperture_sizes);
for (i = 0; i < agp_bridge.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)
{
int i;
u8 temp;
struct aper_size_info_8 *values;
pci_read_config_byte(agp_bridge.dev, INTEL_APSIZE, &temp);
/* Intel 815 chipsets have a _weird_ APSIZE register with only
* one non-reserved bit, so mask the others out ... */
if (agp_bridge.type == INTEL_I815)
temp &= (1 << 3)
values = A_SIZE_8(agp_bridge.aperture_sizes);
for (i = 0; i < agp_bridge.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 intel_tlbflush(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(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, INTEL_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;
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, INTEL_APBASE, &temp);
agp_bridge.gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
/* 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)
panic("gatt bus addr too high");
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(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, INTEL_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, INTEL_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, INTEL_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, INTEL_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, INTEL_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, INTEL_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 unsigned long intel_mask_memory(unsigned long addr, int type)
{
/* Memory type is ignored */
return addr | agp_bridge.masks[0].mask;
}
static void intel_resume(void)
{
intel_configure();
}
/* 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}
};
int __init intel_generic_setup (struct pci_dev *pdev)
{
agp_bridge.masks = intel_generic_masks;
agp_bridge.num_of_masks = 1;
agp_bridge.aperture_sizes = (void *) intel_generic_sizes;
agp_bridge.size_type = U16_APER_SIZE;
agp_bridge.num_aperture_sizes = 7;
agp_bridge.dev_private_data = NULL;
agp_bridge.needs_scratch_page = FALSE;
agp_bridge.configure = intel_configure;
agp_bridge.fetch_size = intel_fetch_size;
agp_bridge.cleanup = intel_cleanup;
agp_bridge.tlb_flush = intel_tlbflush;
agp_bridge.mask_memory = intel_mask_memory;
agp_bridge.agp_enable = agp_generic_agp_enable;
agp_bridge.cache_flush = global_cache_flush;
agp_bridge.create_gatt_table = agp_generic_create_gatt_table;
agp_bridge.free_gatt_table = agp_generic_free_gatt_table;
agp_bridge.insert_memory = agp_generic_insert_memory;
agp_bridge.remove_memory = agp_generic_remove_memory;
agp_bridge.alloc_by_type = agp_generic_alloc_by_type;
agp_bridge.free_by_type = agp_generic_free_by_type;
agp_bridge.agp_alloc_page = agp_generic_alloc_page;
agp_bridge.agp_destroy_page = agp_generic_destroy_page;
agp_bridge.suspend = agp_generic_suspend;
agp_bridge.resume = intel_resume;
agp_bridge.cant_use_aperture = 0;
return 0;
(void) pdev; /* unused */
}
int __init intel_815_setup (struct pci_dev *pdev)
{
agp_bridge.masks = intel_generic_masks;
agp_bridge.num_of_masks = 1;
agp_bridge.aperture_sizes = (void *) intel_815_sizes;
agp_bridge.size_type = U8_APER_SIZE;
agp_bridge.num_aperture_sizes = 2;
agp_bridge.dev_private_data = NULL;
agp_bridge.needs_scratch_page = FALSE;
agp_bridge.configure = intel_815_configure;
agp_bridge.fetch_size = intel_8xx_fetch_size;
agp_bridge.cleanup = intel_8xx_cleanup;
agp_bridge.tlb_flush = intel_8xx_tlbflush;
agp_bridge.mask_memory = intel_mask_memory;
agp_bridge.agp_enable = agp_generic_agp_enable;
agp_bridge.cache_flush = global_cache_flush;
agp_bridge.create_gatt_table = agp_generic_create_gatt_table;
agp_bridge.free_gatt_table = agp_generic_free_gatt_table;
agp_bridge.insert_memory = agp_generic_insert_memory;
agp_bridge.remove_memory = agp_generic_remove_memory;
agp_bridge.alloc_by_type = agp_generic_alloc_by_type;
agp_bridge.free_by_type = agp_generic_free_by_type;
agp_bridge.agp_alloc_page = agp_generic_alloc_page;
agp_bridge.agp_destroy_page = agp_generic_destroy_page;
agp_bridge.suspend = agp_generic_suspend;
agp_bridge.resume = agp_generic_resume;
agp_bridge.cant_use_aperture = 0;
return 0;
}
int __init intel_820_setup (struct pci_dev *pdev)
{
agp_bridge.masks = intel_generic_masks;
agp_bridge.num_of_masks = 1;
agp_bridge.aperture_sizes = (void *) intel_8xx_sizes;
agp_bridge.size_type = U8_APER_SIZE;
agp_bridge.num_aperture_sizes = 7;
agp_bridge.dev_private_data = NULL;
agp_bridge.needs_scratch_page = FALSE;
agp_bridge.configure = intel_820_configure;
agp_bridge.fetch_size = intel_8xx_fetch_size;
agp_bridge.cleanup = intel_820_cleanup;
agp_bridge.tlb_flush = intel_820_tlbflush;
agp_bridge.mask_memory = intel_mask_memory;
agp_bridge.agp_enable = agp_generic_agp_enable;
agp_bridge.cache_flush = global_cache_flush;
agp_bridge.create_gatt_table = agp_generic_create_gatt_table;
agp_bridge.free_gatt_table = agp_generic_free_gatt_table;
agp_bridge.insert_memory = agp_generic_insert_memory;
agp_bridge.remove_memory = agp_generic_remove_memory;
agp_bridge.alloc_by_type = agp_generic_alloc_by_type;
agp_bridge.free_by_type = agp_generic_free_by_type;
agp_bridge.agp_alloc_page = agp_generic_alloc_page;
agp_bridge.agp_destroy_page = agp_generic_destroy_page;
agp_bridge.suspend = agp_generic_suspend;
agp_bridge.resume = agp_generic_resume;
agp_bridge.cant_use_aperture = 0;
return 0;
(void) pdev; /* unused */
}
int __init intel_830mp_setup (struct pci_dev *pdev)
{
agp_bridge.masks = intel_generic_masks;
agp_bridge.num_of_masks = 1;
agp_bridge.aperture_sizes = (void *) intel_830mp_sizes;
agp_bridge.size_type = U8_APER_SIZE;
agp_bridge.num_aperture_sizes = 4;
agp_bridge.dev_private_data = NULL;
agp_bridge.needs_scratch_page = FALSE;
agp_bridge.configure = intel_830mp_configure;
agp_bridge.fetch_size = intel_8xx_fetch_size;
agp_bridge.cleanup = intel_8xx_cleanup;
agp_bridge.tlb_flush = intel_8xx_tlbflush;
agp_bridge.mask_memory = intel_mask_memory;
agp_bridge.agp_enable = agp_generic_agp_enable;
agp_bridge.cache_flush = global_cache_flush;
agp_bridge.create_gatt_table = agp_generic_create_gatt_table;
agp_bridge.free_gatt_table = agp_generic_free_gatt_table;
agp_bridge.insert_memory = agp_generic_insert_memory;
agp_bridge.remove_memory = agp_generic_remove_memory;
agp_bridge.alloc_by_type = agp_generic_alloc_by_type;
agp_bridge.free_by_type = agp_generic_free_by_type;
agp_bridge.agp_alloc_page = agp_generic_alloc_page;
agp_bridge.agp_destroy_page = agp_generic_destroy_page;
agp_bridge.suspend = agp_generic_suspend;
agp_bridge.resume = agp_generic_resume;
agp_bridge.cant_use_aperture = 0;
return 0;
(void) pdev; /* unused */
}
int __init intel_840_setup (struct pci_dev *pdev)
{
agp_bridge.masks = intel_generic_masks;
agp_bridge.num_of_masks = 1;
agp_bridge.aperture_sizes = (void *) intel_8xx_sizes;
agp_bridge.size_type = U8_APER_SIZE;
agp_bridge.num_aperture_sizes = 7;
agp_bridge.dev_private_data = NULL;
agp_bridge.needs_scratch_page = FALSE;
agp_bridge.configure = intel_840_configure;
agp_bridge.fetch_size = intel_8xx_fetch_size;
agp_bridge.cleanup = intel_8xx_cleanup;
agp_bridge.tlb_flush = intel_8xx_tlbflush;
agp_bridge.mask_memory = intel_mask_memory;
agp_bridge.agp_enable = agp_generic_agp_enable;
agp_bridge.cache_flush = global_cache_flush;
agp_bridge.create_gatt_table = agp_generic_create_gatt_table;
agp_bridge.free_gatt_table = agp_generic_free_gatt_table;
agp_bridge.insert_memory = agp_generic_insert_memory;
agp_bridge.remove_memory = agp_generic_remove_memory;
agp_bridge.alloc_by_type = agp_generic_alloc_by_type;
agp_bridge.free_by_type = agp_generic_free_by_type;
agp_bridge.agp_alloc_page = agp_generic_alloc_page;
agp_bridge.agp_destroy_page = agp_generic_destroy_page;
agp_bridge.suspend = agp_generic_suspend;
agp_bridge.resume = agp_generic_resume;
agp_bridge.cant_use_aperture = 0;
return 0;
(void) pdev; /* unused */
}
int __init intel_845_setup (struct pci_dev *pdev)
{
agp_bridge.masks = intel_generic_masks;
agp_bridge.num_of_masks = 1;
agp_bridge.aperture_sizes = (void *) intel_8xx_sizes;
agp_bridge.size_type = U8_APER_SIZE;
agp_bridge.num_aperture_sizes = 7;
agp_bridge.dev_private_data = NULL;
agp_bridge.needs_scratch_page = FALSE;
agp_bridge.configure = intel_845_configure;
agp_bridge.fetch_size = intel_8xx_fetch_size;
agp_bridge.cleanup = intel_8xx_cleanup;
agp_bridge.tlb_flush = intel_8xx_tlbflush;
agp_bridge.mask_memory = intel_mask_memory;
agp_bridge.agp_enable = agp_generic_agp_enable;
agp_bridge.cache_flush = global_cache_flush;
agp_bridge.create_gatt_table = agp_generic_create_gatt_table;
agp_bridge.free_gatt_table = agp_generic_free_gatt_table;
agp_bridge.insert_memory = agp_generic_insert_memory;
agp_bridge.remove_memory = agp_generic_remove_memory;
agp_bridge.alloc_by_type = agp_generic_alloc_by_type;
agp_bridge.free_by_type = agp_generic_free_by_type;
agp_bridge.agp_alloc_page = agp_generic_alloc_page;
agp_bridge.agp_destroy_page = agp_generic_destroy_page;
agp_bridge.suspend = agp_generic_suspend;
agp_bridge.resume = agp_generic_resume;
agp_bridge.cant_use_aperture = 0;
return 0;
(void) pdev; /* unused */
}
int __init intel_850_setup (struct pci_dev *pdev)
{
agp_bridge.masks = intel_generic_masks;
agp_bridge.num_of_masks = 1;
agp_bridge.aperture_sizes = (void *) intel_8xx_sizes;
agp_bridge.size_type = U8_APER_SIZE;
agp_bridge.num_aperture_sizes = 7;
agp_bridge.dev_private_data = NULL;
agp_bridge.needs_scratch_page = FALSE;
agp_bridge.configure = intel_850_configure;
agp_bridge.fetch_size = intel_8xx_fetch_size;
agp_bridge.cleanup = intel_8xx_cleanup;
agp_bridge.tlb_flush = intel_8xx_tlbflush;
agp_bridge.mask_memory = intel_mask_memory;
agp_bridge.agp_enable = agp_generic_agp_enable;
agp_bridge.cache_flush = global_cache_flush;
agp_bridge.create_gatt_table = agp_generic_create_gatt_table;
agp_bridge.free_gatt_table = agp_generic_free_gatt_table;
agp_bridge.insert_memory = agp_generic_insert_memory;
agp_bridge.remove_memory = agp_generic_remove_memory;
agp_bridge.alloc_by_type = agp_generic_alloc_by_type;
agp_bridge.free_by_type = agp_generic_free_by_type;
agp_bridge.agp_alloc_page = agp_generic_alloc_page;
agp_bridge.agp_destroy_page = agp_generic_destroy_page;
agp_bridge.suspend = agp_generic_suspend;
agp_bridge.resume = agp_generic_resume;
agp_bridge.cant_use_aperture = 0;
return 0;
(void) pdev; /* unused */
}
int __init intel_860_setup (struct pci_dev *pdev)
{
agp_bridge.masks = intel_generic_masks;
agp_bridge.num_of_masks = 1;
agp_bridge.aperture_sizes = (void *) intel_8xx_sizes;
agp_bridge.size_type = U8_APER_SIZE;
agp_bridge.num_aperture_sizes = 7;
agp_bridge.dev_private_data = NULL;
agp_bridge.needs_scratch_page = FALSE;
agp_bridge.configure = intel_860_configure;
agp_bridge.fetch_size = intel_8xx_fetch_size;
agp_bridge.cleanup = intel_8xx_cleanup;
agp_bridge.tlb_flush = intel_8xx_tlbflush;
agp_bridge.mask_memory = intel_mask_memory;
agp_bridge.agp_enable = agp_generic_agp_enable;
agp_bridge.cache_flush = global_cache_flush;
agp_bridge.create_gatt_table = agp_generic_create_gatt_table;
agp_bridge.free_gatt_table = agp_generic_free_gatt_table;
agp_bridge.insert_memory = agp_generic_insert_memory;
agp_bridge.remove_memory = agp_generic_remove_memory;
agp_bridge.alloc_by_type = agp_generic_alloc_by_type;
agp_bridge.free_by_type = agp_generic_free_by_type;
agp_bridge.agp_alloc_page = agp_generic_alloc_page;
agp_bridge.agp_destroy_page = agp_generic_destroy_page;
agp_bridge.suspend = agp_generic_suspend;
agp_bridge.resume = agp_generic_resume;
agp_bridge.cant_use_aperture = 0;
return 0;
(void) pdev; /* unused */
}
/*
* AGPGART module version 0.99
* 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/module.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/agp_backend.h>
#include "agp.h"
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.aperture_sizes);
for (i = 0; i < agp_bridge.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(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, SIS_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 unsigned long sis_mask_memory(unsigned long addr, int type)
{
/* Memory type is ignored */
return addr | agp_bridge.masks[0].mask;
}
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}
};
static struct gatt_mask sis_generic_masks[] =
{
{mask: 0x00000000, type: 0}
};
int __init sis_generic_setup (struct pci_dev *pdev)
{
agp_bridge.masks = sis_generic_masks;
agp_bridge.num_of_masks = 1;
agp_bridge.aperture_sizes = (void *) sis_generic_sizes;
agp_bridge.size_type = U8_APER_SIZE;
agp_bridge.num_aperture_sizes = 7;
agp_bridge.dev_private_data = NULL;
agp_bridge.needs_scratch_page = FALSE;
agp_bridge.configure = sis_configure;
agp_bridge.fetch_size = sis_fetch_size;
agp_bridge.cleanup = sis_cleanup;
agp_bridge.tlb_flush = sis_tlbflush;
agp_bridge.mask_memory = sis_mask_memory;
agp_bridge.agp_enable = agp_generic_agp_enable;
agp_bridge.cache_flush = global_cache_flush;
agp_bridge.create_gatt_table = agp_generic_create_gatt_table;
agp_bridge.free_gatt_table = agp_generic_free_gatt_table;
agp_bridge.insert_memory = agp_generic_insert_memory;
agp_bridge.remove_memory = agp_generic_remove_memory;
agp_bridge.alloc_by_type = agp_generic_alloc_by_type;
agp_bridge.free_by_type = agp_generic_free_by_type;
agp_bridge.agp_alloc_page = agp_generic_alloc_page;
agp_bridge.agp_destroy_page = agp_generic_destroy_page;
agp_bridge.suspend = agp_generic_suspend;
agp_bridge.resume = agp_generic_resume;
agp_bridge.cant_use_aperture = 0;
return 0;
}
/*
* AGPGART module version 0.99
* 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/module.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/agp_backend.h>
#include "agp.h"
struct serverworks_page_map {
unsigned long *real;
unsigned long *remapped;
};
static struct _serverworks_private {
struct pci_dev *svrwrks_dev; /* device one */
volatile u8 *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));
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;
}
CACHE_FLUSH();
for(i = 0; i < PAGE_SIZE / sizeof(unsigned long); i++) {
page_map->remapped[i] = agp_bridge.scratch_page;
}
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(void)
{
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++) {
serverworks_private.scratch_dir.remapped[i] = (unsigned long) agp_bridge.scratch_page;
page_dir.remapped[i] =
virt_to_phys(serverworks_private.scratch_dir.real);
page_dir.remapped[i] |= 0x00000001;
}
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 = page_dir.real;
agp_bridge.gatt_table = 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++) {
page_dir.remapped[i] =
virt_to_phys(serverworks_private.gatt_pages[i]->real);
page_dir.remapped[i] |= 0x00000001;
}
return 0;
}
static int serverworks_free_gatt_table(void)
{
struct serverworks_page_map page_dir;
page_dir.real = agp_bridge.gatt_table_real;
page_dir.remapped = 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.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.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;
}
static int serverworks_configure(void)
{
struct aper_size_info_lvl2 *current_size;
u32 temp;
u8 enable_reg;
u8 cap_ptr;
u32 cap_id;
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 *) ioremap(temp, 4096);
OUTREG8(serverworks_private.registers, SVWRKS_GART_CACHE, 0x0a);
OUTREG32(serverworks_private.registers, SVWRKS_GATTBASE,
agp_bridge.gatt_bus_addr);
cap_reg = INREG16(serverworks_private.registers, SVWRKS_COMMAND);
cap_reg &= ~0x0007;
cap_reg |= 0x4;
OUTREG16(serverworks_private.registers, SVWRKS_COMMAND, cap_reg);
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);
agp_bridge.tlb_flush(NULL);
pci_read_config_byte(serverworks_private.svrwrks_dev, 0x34, &cap_ptr);
if (cap_ptr != 0x00) {
do {
pci_read_config_dword(serverworks_private.svrwrks_dev,
cap_ptr, &cap_id);
if ((cap_id & 0xff) != 0x02)
cap_ptr = (cap_id >> 8) & 0xff;
}
while (((cap_id & 0xff) != 0x02) && (cap_ptr != 0x00));
}
agp_bridge.capndx = cap_ptr;
/* Fill in the mode register */
pci_read_config_dword(serverworks_private.svrwrks_dev,
agp_bridge.capndx + 4,
&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 *) serverworks_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 serverworks_tlbflush(agp_memory * temp)
{
unsigned long end;
OUTREG8(serverworks_private.registers, SVWRKS_POSTFLUSH, 0x01);
end = jiffies + 3*HZ;
while(INREG8(serverworks_private.registers,
SVWRKS_POSTFLUSH) == 0x01) {
if((signed)(end - jiffies) <= 0) {
printk(KERN_ERR "Posted write buffer flush took more"
"then 3 seconds\n");
}
}
OUTREG32(serverworks_private.registers, SVWRKS_DIRFLUSH, 0x00000001);
end = jiffies + 3*HZ;
while(INREG32(serverworks_private.registers,
SVWRKS_DIRFLUSH) == 0x00000001) {
if((signed)(end - jiffies) <= 0) {
printk(KERN_ERR "TLB flush took more"
"then 3 seconds\n");
}
}
}
static unsigned long serverworks_mask_memory(unsigned long addr, int type)
{
/* Only type 0 is supported by the serverworks chipsets */
return addr | agp_bridge.masks[0].mask;
}
static int serverworks_insert_memory(agp_memory * mem,
off_t pg_start, int type)
{
int i, j, num_entries;
unsigned long *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(cur_gatt[GET_GATT_OFF(addr)])) {
return -EBUSY;
}
j++;
}
if (mem->is_flushed == FALSE) {
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);
cur_gatt[GET_GATT_OFF(addr)] = mem->memory[i];
}
agp_bridge.tlb_flush(mem);
return 0;
}
static int serverworks_remove_memory(agp_memory * mem, off_t pg_start,
int type)
{
int i;
unsigned long *cur_gatt;
unsigned long addr;
if (type != 0 || mem->type != 0) {
return -EINVAL;
}
CACHE_FLUSH();
agp_bridge.tlb_flush(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);
cur_gatt[GET_GATT_OFF(addr)] =
(unsigned long) agp_bridge.scratch_page;
}
agp_bridge.tlb_flush(mem);
return 0;
}
static struct gatt_mask serverworks_masks[] =
{
{mask: 0x00000001, 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(u32 mode)
{
struct pci_dev *device = NULL;
u32 command, scratch, cap_id;
u8 cap_ptr;
pci_read_config_dword(serverworks_private.svrwrks_dev,
agp_bridge.capndx + 4,
&command);
/*
* PASS1: go throu all devices that claim to be
* AGP devices and collect their data.
*/
pci_for_each_dev(device) {
cap_ptr = pci_find_capability(device, PCI_CAP_ID_AGP);
if (cap_ptr != 0x00) {
do {
pci_read_config_dword(device,
cap_ptr, &cap_id);
if ((cap_id & 0xff) != 0x02)
cap_ptr = (cap_id >> 8) & 0xff;
}
while (((cap_id & 0xff) != 0x02) && (cap_ptr != 0x00));
}
if (cap_ptr != 0x00) {
/*
* Ok, here we have a AGP device. Disable impossible
* settings, and adjust the readqueue to the minimum.
*/
pci_read_config_dword(device, cap_ptr + 4, &scratch);
/* adjust RQ depth */
command =
((command & ~0xff000000) |
min_t(u32, (mode & 0xff000000),
min_t(u32, (command & 0xff000000),
(scratch & 0xff000000))));
/* disable SBA if it's not supported */
if (!((command & 0x00000200) &&
(scratch & 0x00000200) &&
(mode & 0x00000200)))
command &= ~0x00000200;
/* disable FW */
command &= ~0x00000010;
command &= ~0x00000008;
if (!((command & 4) &&
(scratch & 4) &&
(mode & 4)))
command &= ~0x00000004;
if (!((command & 2) &&
(scratch & 2) &&
(mode & 2)))
command &= ~0x00000002;
if (!((command & 1) &&
(scratch & 1) &&
(mode & 1)))
command &= ~0x00000001;
}
}
/*
* PASS2: Figure out the 4X/2X/1X setting and enable the
* target (our motherboard chipset).
*/
if (command & 4) {
command &= ~3; /* 4X */
}
if (command & 2) {
command &= ~5; /* 2X */
}
if (command & 1) {
command &= ~6; /* 1X */
}
command |= 0x00000100;
pci_write_config_dword(serverworks_private.svrwrks_dev,
agp_bridge.capndx + 8,
command);
/*
* PASS3: Go throu all AGP devices and update the
* command registers.
*/
pci_for_each_dev(device) {
cap_ptr = pci_find_capability(device, PCI_CAP_ID_AGP);
if (cap_ptr != 0x00)
pci_write_config_dword(device, cap_ptr + 8, command);
}
}
int __init serverworks_setup (struct pci_dev *pdev)
{
u32 temp;
u32 temp2;
serverworks_private.svrwrks_dev = pdev;
agp_bridge.masks = serverworks_masks;
agp_bridge.num_of_masks = 1;
agp_bridge.aperture_sizes = (void *) serverworks_sizes;
agp_bridge.size_type = LVL2_APER_SIZE;
agp_bridge.num_aperture_sizes = 7;
agp_bridge.dev_private_data = (void *) &serverworks_private;
agp_bridge.needs_scratch_page = TRUE;
agp_bridge.configure = serverworks_configure;
agp_bridge.fetch_size = serverworks_fetch_size;
agp_bridge.cleanup = serverworks_cleanup;
agp_bridge.tlb_flush = serverworks_tlbflush;
agp_bridge.mask_memory = serverworks_mask_memory;
agp_bridge.agp_enable = serverworks_agp_enable;
agp_bridge.cache_flush = global_cache_flush;
agp_bridge.create_gatt_table = serverworks_create_gatt_table;
agp_bridge.free_gatt_table = serverworks_free_gatt_table;
agp_bridge.insert_memory = serverworks_insert_memory;
agp_bridge.remove_memory = serverworks_remove_memory;
agp_bridge.alloc_by_type = agp_generic_alloc_by_type;
agp_bridge.free_by_type = agp_generic_free_by_type;
agp_bridge.agp_alloc_page = agp_generic_alloc_page;
agp_bridge.agp_destroy_page = agp_generic_destroy_page;
agp_bridge.suspend = agp_generic_suspend;
agp_bridge.resume = agp_generic_resume;
agp_bridge.cant_use_aperture = 0;
pci_read_config_dword(agp_bridge.dev,
SVWRKS_APSIZE,
&temp);
serverworks_private.gart_addr_ofs = 0x10;
if(temp & PCI_BASE_ADDRESS_MEM_TYPE_64) {
pci_read_config_dword(agp_bridge.dev,
SVWRKS_APSIZE + 4,
&temp2);
if(temp2 != 0) {
printk("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(agp_bridge.dev,
serverworks_private.mm_addr_ofs,
&temp);
if(temp & PCI_BASE_ADDRESS_MEM_TYPE_64) {
pci_read_config_dword(agp_bridge.dev,
serverworks_private.mm_addr_ofs + 4,
&temp2);
if(temp2 != 0) {
printk("Detected 64 bit MMIO address, but top "
"bits are not zero. Disabling agp\n");
return -ENODEV;
}
}
return 0;
}
/*
* AGPGART module version 0.99
* 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/types.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/agp_backend.h>
#include "agp.h"
static int via_fetch_size(void)
{
int i;
u8 temp;
struct aper_size_info_8 *values;
values = A_SIZE_8(agp_bridge.aperture_sizes);
pci_read_config_byte(agp_bridge.dev, VIA_APSIZE, &temp);
for (i = 0; i < agp_bridge.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(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, VIA_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(agp_memory * mem)
{
pci_write_config_dword(agp_bridge.dev, VIA_GARTCTRL, 0x0000008f);
pci_write_config_dword(agp_bridge.dev, VIA_GARTCTRL, 0x0000000f);
}
static unsigned long via_mask_memory(unsigned long addr, int type)
{
/* Memory type is ignored */
return addr | agp_bridge.masks[0].mask;
}
static struct aper_size_info_8 via_generic_sizes[7] =
{
{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}
};
static struct gatt_mask via_generic_masks[] =
{
{mask: 0x00000000, type: 0}
};
int __init via_generic_setup (struct pci_dev *pdev)
{
agp_bridge.masks = via_generic_masks;
agp_bridge.num_of_masks = 1;
agp_bridge.aperture_sizes = (void *) via_generic_sizes;
agp_bridge.size_type = U8_APER_SIZE;
agp_bridge.num_aperture_sizes = 7;
agp_bridge.dev_private_data = NULL;
agp_bridge.needs_scratch_page = FALSE;
agp_bridge.configure = via_configure;
agp_bridge.fetch_size = via_fetch_size;
agp_bridge.cleanup = via_cleanup;
agp_bridge.tlb_flush = via_tlbflush;
agp_bridge.mask_memory = via_mask_memory;
agp_bridge.agp_enable = agp_generic_agp_enable;
agp_bridge.cache_flush = global_cache_flush;
agp_bridge.create_gatt_table = agp_generic_create_gatt_table;
agp_bridge.free_gatt_table = agp_generic_free_gatt_table;
agp_bridge.insert_memory = agp_generic_insert_memory;
agp_bridge.remove_memory = agp_generic_remove_memory;
agp_bridge.alloc_by_type = agp_generic_alloc_by_type;
agp_bridge.free_by_type = agp_generic_free_by_type;
agp_bridge.agp_alloc_page = agp_generic_alloc_page;
agp_bridge.agp_destroy_page = agp_generic_destroy_page;
agp_bridge.suspend = agp_generic_suspend;
agp_bridge.resume = agp_generic_resume;
agp_bridge.cant_use_aperture = 0;
return 0;
(void) pdev; /* unused */
}
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -24,29 +24,16 @@
*
*/
#define __NO_VERSION__
#include <linux/version.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/mman.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/pagemap.h>
#include <linux/miscdevice.h>
#include <linux/agp_backend.h>
#include <linux/agpgart.h>
#include <linux/smp_lock.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/page.h>
#include <asm/mman.h>
#include "agp.h"
......
......@@ -81,13 +81,13 @@ enum chipset_type {
HP_ZX1,
};
typedef struct _agp_version {
struct agp_version {
u16 major;
u16 minor;
} agp_version;
};
typedef struct _agp_kern_info {
agp_version version;
struct agp_version version;
struct pci_dev *device;
enum chipset_type chipset;
unsigned long mode;
......
......@@ -53,13 +53,13 @@
#include <linux/types.h>
#include <asm/types.h>
typedef struct _agp_version {
struct agp_version {
__u16 major;
__u16 minor;
} agp_version;
};
typedef struct _agp_info {
agp_version version; /* version of the driver */
struct agp_version version; /* version of the driver */
__u32 bridge_id; /* bridge vendor/device */
__u32 agp_mode; /* mode info of bridge */
off_t aper_base; /* base of aperture */
......@@ -117,7 +117,7 @@ typedef struct _agp_unbind {
#define AGP_LOCK_INIT() sema_init(&(agp_fe.agp_mutex), 1)
#ifndef _AGP_BACKEND_H
typedef struct _agp_version {
struct _agp_version {
u16 major;
u16 minor;
} agp_version;
......@@ -125,7 +125,7 @@ typedef struct _agp_version {
#endif
typedef struct _agp_info {
agp_version version; /* version of the driver */
struct agp_version version; /* version of the driver */
u32 bridge_id; /* bridge vendor/device */
u32 agp_mode; /* mode info of bridge */
off_t aper_base; /* base of aperture */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment