Initial commit
This commit is contained in:
parent
518a079f66
commit
068d34c94b
753
device/3dfx_driver.c
Normal file
753
device/3dfx_driver.c
Normal file
@ -0,0 +1,753 @@
|
||||
/*
|
||||
|
||||
/dev/3dfx device for 2.x kernels with MTRR settings enabled.
|
||||
|
||||
Compile with :
|
||||
|
||||
gcc -O2 -DMODULE -D__KERNEL__ -I/usr/src/linux/include -pipe
|
||||
-fno-strength-reduce -malign-loops=2 -malign-jumps=2
|
||||
-malign-functions=2 -c -o 3dfx.o 3dfx_driver.c
|
||||
|
||||
Add -D__SMP__ if you're running of an multi-processor system.
|
||||
Add -DHAVE_MTRR if you have and want MTRR settings.
|
||||
Add -g -Wall -Wstrict-prototypes -DDEBUG, if you are debugging.
|
||||
|
||||
Original by Daryll Straus.
|
||||
Port to 2.1 kernel by Jon Taylor.
|
||||
setmtrr_3dfx() added by Jens Axboe.
|
||||
Combining 2.0 and 2.1/2.2 kernels into one rpm,
|
||||
resetmtrr_3df(), and using correct pci calls for
|
||||
2.1/2.2 kernels by Carlo Wood.
|
||||
|
||||
ChangeLog
|
||||
|
||||
2000/02/04 Joseph Kain <joseph@3dfx.com>
|
||||
|
||||
* Updated Carlo Woord's email address.
|
||||
|
||||
1999/12/22 Joseph Kain <joseph@3dfx.com>
|
||||
|
||||
* Support for new VMA structure in Kernels 2.3.14 and higher.
|
||||
This patch is based on Dieter Nuetzel's work.
|
||||
|
||||
1999/11/09 Joseph Kain <joseph@3dfx.com>
|
||||
|
||||
* Made cards static to remove conflict with hisax ISDN driver.
|
||||
|
||||
1998/10/30 Carlo Wood <carlo@alinoe.com>
|
||||
|
||||
* Re-included module versions (this source was based on the
|
||||
source by Jon Taylor which was based on an older version
|
||||
by Daryll Straus. I now included the later changes made
|
||||
by Daryll in this source too).
|
||||
* Use pci_find_device for 2.1/2.2 kernels instead of the old
|
||||
pcibios_find_device. Idem pci_present().
|
||||
|
||||
1998/10/21 Carlo Wood <carlo@alinoe.com>
|
||||
|
||||
* Fixed compile bugs related to 2.0 kernels.
|
||||
|
||||
1998/10/21 Carlo Wood <carlo@alinoe.com>
|
||||
|
||||
* `struct file_operations' was changed as of kernel 2.1.118,
|
||||
fixed this module to also work with versions > 2.1.117.
|
||||
|
||||
* Machines that don't have mtrr still need <asm/uaccess.h>.
|
||||
|
||||
1998/10/20 Carlo Wood <carlo@alinoe.com>
|
||||
|
||||
* Finally fixed in init_module() the right way. This time I tested
|
||||
it too (it now really works).
|
||||
|
||||
* Added resetmtrr_3dfx() to clean up the MTRR settings when the
|
||||
module is unloaded. This is not really necessary but its clean.
|
||||
It allows to compare using this device WITH MTRR with another
|
||||
method WITHOUT MTRR, without being confused by sticky MTRR settings
|
||||
for instance.
|
||||
|
||||
1998/10/18 Carlo Wood <carlo@alinoe.com>
|
||||
|
||||
* Forgot the '== -EBUSY' in init_module(), causing the registration of
|
||||
character device to always fail. You need to reboot if you tried
|
||||
the previous version (if there is junk in `cat /proc/devices' major 107).
|
||||
|
||||
1998/10/18 Carlo Wood <carlo@alinoe.com>
|
||||
|
||||
* struct file_operations fops_3dfx was initialized wrong:
|
||||
`release_3dfx' was put on the place of `fsync', causing weird
|
||||
behaviour :).
|
||||
|
||||
* Several warning fixes.
|
||||
|
||||
* setmtrr_3dfx did not always return a value, and the value was
|
||||
ignored, effectively ignoring errors returned by mtrr_add().
|
||||
|
||||
*/
|
||||
|
||||
/* Include this first as it defines things that affect the kernel headers */
|
||||
#include <linux/autoconf.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
#ifndef KERNEL_VERSION
|
||||
#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
|
||||
#endif
|
||||
|
||||
#define KERNEL_MIN_VER(a,b,c) (LINUX_VERSION_CODE >= KERNEL_VERSION(a,b,c))
|
||||
|
||||
#if !KERNEL_MIN_VER (2, 1, 115)
|
||||
/* It might work with smaller kernels, but I never tested that */
|
||||
#error "Upgrade your kernel"
|
||||
#endif
|
||||
|
||||
#ifdef MODULE
|
||||
#include <linux/module.h>
|
||||
#if defined(CONFIG_MODVERSIONS) && !defined(MODVERSIONS)
|
||||
#define MODVERSIONS
|
||||
#endif
|
||||
#ifdef MODVERSIONS
|
||||
#include <linux/modversions.h>
|
||||
#endif
|
||||
#else
|
||||
#define MOD_INC_USE_COUNT
|
||||
#define MOD_DEC_USE_COUNT
|
||||
#endif
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/pci.h>
|
||||
#include <asm/segment.h>
|
||||
#include <asm/ioctl.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/processor.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
#ifdef HAVE_MTRR
|
||||
#include <asm/mtrr.h>
|
||||
#endif
|
||||
|
||||
#define MAJOR_3DFX 107
|
||||
#define DEVICE_VOODOO 0
|
||||
|
||||
#define PCI_VENDOR_ID_LINUX 0x0
|
||||
#define PCI_DEVICE_ID_LINUX 0x2
|
||||
#define PCI_COMMAND_LINUX 0x4
|
||||
#define PCI_REVISION_ID_LINUX 0x8
|
||||
#define PCI_BASE_ADDRESS_0_LINUX 0x10
|
||||
#define PCI_BASE_ADDRESS_1_LINUX 0x14
|
||||
#define SST1_PCI_SPECIAL1_LINUX 0x40
|
||||
#define SST1_PCI_SPECIAL2_LINUX 0x44
|
||||
#define SST1_PCI_SPECIAL3_LINUX 0x48
|
||||
#define SST1_PCI_SPECIAL4_LINUX 0x54
|
||||
|
||||
#define VGA_INPUT_STATUS_1C 0x3DA
|
||||
#define VGA_MISC_OUTPUT_READ 0x3cc
|
||||
#define VGA_MISC_OUTPUT_WRITE 0x3c2
|
||||
#define SC_INDEX 0x3c4
|
||||
#define SC_DATA 0x3c5
|
||||
|
||||
#ifndef PCI_VENDOR_ID_3DFX
|
||||
#define PCI_VENDOR_ID_3DFX 0x121a
|
||||
#endif
|
||||
|
||||
#ifndef PCI_VENDOR_ID_ALLIANCE
|
||||
#define PCI_VENDOR_ID_ALLIANCE 0x1142
|
||||
#endif
|
||||
|
||||
#ifndef PCI_DEVICE_ID_3DFX_VOODOO2
|
||||
#define PCI_DEVICE_ID_3DFX_VOODOO2 2
|
||||
#endif
|
||||
|
||||
#ifndef PCI_DEVICE_ID_ALLIANCE_AT3D
|
||||
#define PCI_DEVICE_ID_ALLIANCE_AT3D 0x643d
|
||||
#endif
|
||||
|
||||
#ifndef PCI_DEVICE_ID_3DFX_BANSHEE
|
||||
#define PCI_DEVICE_ID_3DFX_BANSHEE 3
|
||||
#endif
|
||||
|
||||
#ifndef PCI_DEVICE_ID_3DFX_VOODOO3
|
||||
#define PCI_DEVICE_ID_3DFX_VOODOO3 5
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DEBUGMSG(x) printk x
|
||||
#else
|
||||
#define DEBUGMSG(x)
|
||||
#endif
|
||||
|
||||
/* This macro is for accessing vma->vm_offset or vma->vm_pgoff depending
|
||||
* on kernel version */
|
||||
#if !KERNEL_MIN_VER(2, 3, 14)
|
||||
#define VM_OFFSET(vma) (vma->vm_offset)
|
||||
#else
|
||||
#define VM_OFFSET(vma) (vma->vm_pgoff << PAGE_SHIFT)
|
||||
#endif
|
||||
|
||||
struct pioData_t {
|
||||
short port;
|
||||
short size;
|
||||
int device;
|
||||
void *value;
|
||||
};
|
||||
|
||||
struct cardInfo_t {
|
||||
int vendor;
|
||||
int type;
|
||||
int addr0;
|
||||
int addr1;
|
||||
unsigned char bus;
|
||||
unsigned char dev;
|
||||
struct file *curFile;
|
||||
#ifdef HAVE_MTRR
|
||||
int mtrr_buf;
|
||||
int mtrr_ctl;
|
||||
#endif
|
||||
};
|
||||
|
||||
typedef struct pioData_t pioData;
|
||||
typedef struct cardInfo_t cardInfo;
|
||||
|
||||
#ifdef MODULE
|
||||
void cleanup_module(void);
|
||||
#endif
|
||||
|
||||
#define MAXCARDS 16
|
||||
|
||||
static cardInfo cards[MAXCARDS];
|
||||
static int numCards = 0;
|
||||
|
||||
static void findCardType(int vendor, int device)
|
||||
{
|
||||
struct pci_dev *dev = NULL;
|
||||
while (numCards < MAXCARDS && (dev = pci_find_device(vendor, device, dev))) {
|
||||
pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &cards[numCards].addr0);
|
||||
pci_read_config_dword(dev, PCI_BASE_ADDRESS_1, &cards[numCards].addr1);
|
||||
cards[numCards].bus = dev->bus->number;
|
||||
cards[numCards].dev = dev->devfn;
|
||||
|
||||
cards[numCards].addr0 &= ~0xF;
|
||||
cards[numCards].addr1 &= ~0xF;
|
||||
cards[numCards].vendor = vendor;
|
||||
cards[numCards].type = device;
|
||||
cards[numCards].curFile = 0;
|
||||
|
||||
DEBUGMSG(("3dfx: board vendor %d type %d located at %x/%x bus %d dev %d\n",
|
||||
vendor, device, cards[numCards].addr0, cards[numCards].addr1, cards[numCards].bus, cards[numCards].dev));
|
||||
|
||||
++numCards;
|
||||
}
|
||||
}
|
||||
|
||||
static int findCards(void)
|
||||
{
|
||||
if (!pci_present())
|
||||
return 0;
|
||||
numCards = 0;
|
||||
findCardType(PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_VOODOO);
|
||||
findCardType(PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_VOODOO2);
|
||||
findCardType(PCI_VENDOR_ID_ALLIANCE, 0x643d);
|
||||
findCardType(PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_BANSHEE);
|
||||
findCardType(PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_VOODOO3);
|
||||
return numCards;
|
||||
}
|
||||
|
||||
static int open_3dfx(struct inode *inode, struct file *file)
|
||||
{
|
||||
DEBUGMSG(("3dfx: Entering open_3dfx\n"));
|
||||
if (!numCards) {
|
||||
printk("3dfx: No 3Dfx cards found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
MOD_INC_USE_COUNT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int release_3dfx(struct inode *inode, struct file *file)
|
||||
{
|
||||
int i;
|
||||
|
||||
DEBUGMSG(("3dfx: Entering release_3dfx\n"));
|
||||
for (i = 0; i < numCards; ++i)
|
||||
if (cards[i].curFile == file)
|
||||
cards[i].curFile = 0;
|
||||
MOD_DEC_USE_COUNT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mmap_3dfx(struct file *file, struct vm_area_struct *vma)
|
||||
{
|
||||
size_t len;
|
||||
int i;
|
||||
|
||||
DEBUGMSG(("3dfx: Entering mmap_3dfx\n"));
|
||||
for (i = 0; i < numCards; ++i) {
|
||||
if ((cards[i].addr0 == VM_OFFSET(vma)) ||
|
||||
(cards[i].addr1 == VM_OFFSET(vma)))
|
||||
break;
|
||||
}
|
||||
if (i == numCards) {
|
||||
DEBUGMSG(("3dfx: Couldn't match address %lx to a card\n",
|
||||
VM_OFFSET(vma)));
|
||||
return -EPERM;
|
||||
}
|
||||
/* This one is a special case, the macro doesn't help */
|
||||
#if !KERNEL_MIN_VER(2, 3, 14)
|
||||
if ((vma->vm_offset) & ~PAGE_MASK) {
|
||||
DEBUGMSG(("3dfx: Map request not page aligned\n"));
|
||||
return -ENXIO;
|
||||
}
|
||||
#else
|
||||
if ((vma->vm_pgoff) & ~PAGE_MASK) {
|
||||
DEBUGMSG(("3dfx: Map request not page aligned\n"));
|
||||
return -ENXIO;
|
||||
}
|
||||
#endif
|
||||
len = vma->vm_end - vma->vm_start;
|
||||
if ((len < 0) || (len > 0x2000000)) {
|
||||
DEBUGMSG(("3dfx: Invalid mapping size requested\n"));
|
||||
return -EINVAL;
|
||||
}
|
||||
#if defined(__i386__)
|
||||
pgprot_val(vma->vm_page_prot) |= _PAGE_PCD;
|
||||
#endif
|
||||
if (remap_page_range(vma->vm_start, VM_OFFSET(vma), len,
|
||||
vma->vm_page_prot)) {
|
||||
DEBUGMSG(("3dfx: Page remap failed\n"));
|
||||
return -EAGAIN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int doQueryBoards(void)
|
||||
{
|
||||
return numCards;
|
||||
}
|
||||
|
||||
static int doQueryFetch(pioData *desc)
|
||||
{
|
||||
int retval;
|
||||
char retchar;
|
||||
short retword;
|
||||
int retlong;
|
||||
|
||||
if (desc->device < 0 || desc->device >= numCards)
|
||||
return -EINVAL;
|
||||
if ((retval = verify_area(VERIFY_WRITE, desc->value, desc->size)))
|
||||
return retval;
|
||||
switch (desc->port) {
|
||||
case PCI_VENDOR_ID_LINUX:
|
||||
if (desc->size != 2)
|
||||
return -EINVAL;
|
||||
copy_to_user(desc->value, &cards[desc->device].vendor, desc->size);
|
||||
return 0;
|
||||
case PCI_DEVICE_ID_LINUX:
|
||||
if (desc->size != 2)
|
||||
return -EINVAL;
|
||||
copy_to_user(desc->value, &cards[desc->device].type, desc->size);
|
||||
return 0;
|
||||
case PCI_BASE_ADDRESS_0_LINUX:
|
||||
if (desc->size != 4)
|
||||
return -EINVAL;
|
||||
copy_to_user(desc->value, &cards[desc->device].addr0, desc->size);
|
||||
return 0;
|
||||
case PCI_BASE_ADDRESS_1_LINUX:
|
||||
if (desc->size != 4)
|
||||
return -EINVAL;
|
||||
copy_to_user(desc->value, &cards[desc->device].addr1, desc->size);
|
||||
return 0;
|
||||
case SST1_PCI_SPECIAL1_LINUX:
|
||||
if (desc->size != 4)
|
||||
return -EINVAL;
|
||||
break;
|
||||
case PCI_REVISION_ID:
|
||||
if (desc->size != 1)
|
||||
return -EINVAL;
|
||||
break;
|
||||
case SST1_PCI_SPECIAL4_LINUX:
|
||||
if (desc->size != 4)
|
||||
return -EINVAL;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
switch (desc->size) {
|
||||
case 1:
|
||||
pcibios_read_config_byte(cards[desc->device].bus, cards[desc->device].dev, desc->port, &retchar);
|
||||
copy_to_user(desc->value, &retchar, 1);
|
||||
break;
|
||||
case 2:
|
||||
pcibios_read_config_word(cards[desc->device].bus, cards[desc->device].dev, desc->port, &retword);
|
||||
copy_to_user(desc->value, &retword, 2);
|
||||
break;
|
||||
case 4:
|
||||
pcibios_read_config_dword(cards[desc->device].bus, cards[desc->device].dev, desc->port, &retlong);
|
||||
copy_to_user(desc->value, &retlong, 4);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int doQueryUpdate(pioData *desc)
|
||||
{
|
||||
int retval;
|
||||
int preval;
|
||||
int mask;
|
||||
char retchar;
|
||||
short retword;
|
||||
int retlong;
|
||||
|
||||
if (desc->device < 0 || desc->device >= numCards)
|
||||
return -EINVAL;
|
||||
if ((retval = verify_area(VERIFY_WRITE, desc->value, desc->size)))
|
||||
return retval;
|
||||
switch (desc->port) {
|
||||
case PCI_COMMAND_LINUX:
|
||||
if (desc->size != 2)
|
||||
return -EINVAL;
|
||||
break;
|
||||
case SST1_PCI_SPECIAL1_LINUX:
|
||||
if (desc->size != 4)
|
||||
return -EINVAL;
|
||||
break;
|
||||
case SST1_PCI_SPECIAL2_LINUX:
|
||||
if (desc->size != 4)
|
||||
return -EINVAL;
|
||||
break;
|
||||
case SST1_PCI_SPECIAL3_LINUX:
|
||||
if (desc->size != 4)
|
||||
return -EINVAL;
|
||||
break;
|
||||
case SST1_PCI_SPECIAL4_LINUX:
|
||||
if (desc->size != 4)
|
||||
return -EINVAL;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
pcibios_read_config_dword(cards[desc->device].bus, cards[desc->device].dev, desc->port & ~0x3, &retval);
|
||||
switch (desc->size) {
|
||||
case 1:
|
||||
copy_from_user(&retchar, desc->value, 1);
|
||||
preval = retchar << (8 * (desc->port & 0x3));
|
||||
mask = 0xFF << (8 * (desc->port & 0x3));
|
||||
break;
|
||||
case 2:
|
||||
copy_from_user(&retword, desc->value, 2);
|
||||
preval = retword << (8 * (desc->port & 0x3));
|
||||
mask = 0xFFFF << (8 * (desc->port & 0x3));
|
||||
break;
|
||||
case 4:
|
||||
copy_from_user(&retlong, desc->value, 4);
|
||||
preval = retlong;
|
||||
mask = ~0;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
retval = (retval & ~mask) | preval;
|
||||
pcibios_write_config_dword(cards[desc->device].bus, cards[desc->device].dev, desc->port, retval);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int doQuery(unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
pioData desc;
|
||||
int retval;
|
||||
|
||||
if (_IOC_NR(cmd) == 2)
|
||||
return doQueryBoards();
|
||||
if ((retval = verify_area(VERIFY_READ, (void *)arg, sizeof(pioData))))
|
||||
return retval;
|
||||
copy_from_user(&desc, (void *)arg, sizeof(pioData));
|
||||
if (_IOC_NR(cmd) == 3)
|
||||
return doQueryFetch(&desc);
|
||||
if (_IOC_NR(cmd) == 4)
|
||||
return doQueryUpdate(&desc);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int doPIORead(pioData *desc)
|
||||
{
|
||||
int retval;
|
||||
char retchar;
|
||||
|
||||
if ((retval = verify_area(VERIFY_WRITE, desc->value, desc->size)))
|
||||
return retval;
|
||||
switch (desc->port) {
|
||||
case VGA_INPUT_STATUS_1C:
|
||||
break;
|
||||
case SC_INDEX:
|
||||
break;
|
||||
case SC_DATA:
|
||||
break;
|
||||
case VGA_MISC_OUTPUT_READ:
|
||||
break;
|
||||
default:
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
if (desc->size != 1)
|
||||
return -EINVAL;
|
||||
|
||||
retchar = inb(desc->port);
|
||||
copy_to_user(desc->value, &retchar, sizeof(char));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int doPIOWrite(pioData *desc)
|
||||
{
|
||||
int retval;
|
||||
char retchar;
|
||||
|
||||
if ((retval = verify_area(VERIFY_READ, desc->value, desc->size)))
|
||||
return retval;
|
||||
|
||||
switch (desc->port) {
|
||||
case SC_INDEX:
|
||||
break;
|
||||
case SC_DATA:
|
||||
break;
|
||||
case VGA_MISC_OUTPUT_WRITE:
|
||||
break;
|
||||
default:
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
if (desc->size != 1)
|
||||
return -EINVAL;
|
||||
|
||||
copy_from_user(&retchar, desc->value, sizeof(char));
|
||||
outb(retchar, desc->port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int doPIO(unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
pioData desc;
|
||||
int retval;
|
||||
|
||||
if ((retval = verify_area(VERIFY_READ, (void *)arg, sizeof(pioData))))
|
||||
return retval;
|
||||
copy_from_user(&desc, (void *)arg, sizeof(pioData));
|
||||
if (_IOC_DIR(cmd) == _IOC_READ)
|
||||
return doPIORead(&desc);
|
||||
if (_IOC_DIR(cmd) == _IOC_WRITE)
|
||||
return doPIOWrite(&desc);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ioctl_3dfx(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
DEBUGMSG(("3dfx: Entering ioctl_3dfx, inode %p file %p cmd %x arg %lx\n", inode, file, cmd, arg));
|
||||
switch (_IOC_TYPE(cmd)) {
|
||||
case '3':
|
||||
return doQuery(cmd, arg);
|
||||
case 0:
|
||||
return doPIO(cmd, arg);
|
||||
default:
|
||||
DEBUGMSG(("3dfx: Unknown 3dfx request made\n"));
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_MTRR
|
||||
int setmtrr_3dfx(void)
|
||||
{
|
||||
int i = 0, retval = -2;
|
||||
unsigned char dlc;
|
||||
|
||||
/* First do a bios fixup if this system has a 82441FX chipset */
|
||||
struct pci_dev *dev = NULL;
|
||||
if ((dev = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_0, dev))) {
|
||||
pci_read_config_byte(dev, 0x82, &dlc);
|
||||
if (!(dlc & 1 << 1)) {
|
||||
dlc |= 1 << 1;
|
||||
pci_write_config_byte(dev, 0x82, dlc);
|
||||
printk("3dfx: PIIX3: Enabling Passive Release\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* Set up the mtrr's */
|
||||
if (numCards == 0)
|
||||
return -EIO;
|
||||
for (i = 0; i < numCards; i++) {
|
||||
if ((cards[i].vendor != PCI_VENDOR_ID_3DFX) ||
|
||||
(cards[i].type>PCI_DEVICE_ID_3DFX_VOODOO3))
|
||||
{
|
||||
cards[i].mtrr_buf = -1; /* Used as flag in resetmtrr_3dfx() */
|
||||
continue;
|
||||
}
|
||||
switch (cards[i].type) {
|
||||
case PCI_DEVICE_ID_3DFX_VOODOO:
|
||||
case PCI_DEVICE_ID_3DFX_VOODOO2:
|
||||
/* Frame buffer to write combining */
|
||||
retval = cards[i].mtrr_buf = mtrr_add(cards[i].addr0, 0x400000, MTRR_TYPE_WRCOMB, 1);
|
||||
if (retval>=0) {
|
||||
retval = cards[i].mtrr_ctl = mtrr_add(cards[i].addr0, 0x1000, MTRR_TYPE_UNCACHABLE, 1);
|
||||
if (retval<0) {
|
||||
mtrr_del(cards[i].mtrr_buf, 0, 0);
|
||||
cards[i].mtrr_buf=-1;
|
||||
}
|
||||
}
|
||||
if (retval < 0) {
|
||||
printk("3dfx: Could not set MTRR for Voodoo card\n");
|
||||
/* Can still run */
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case PCI_DEVICE_ID_3DFX_BANSHEE:
|
||||
case PCI_DEVICE_ID_3DFX_VOODOO3:
|
||||
retval = cards[i].mtrr_buf = mtrr_add(cards[i].addr1, 0x1000000, MTRR_TYPE_WRCOMB, 1);
|
||||
if (retval < 0) {
|
||||
printk("3dfx: Could not set MTRR for Voodoo card\n");
|
||||
/* Can still run */
|
||||
return 0;
|
||||
}
|
||||
cards[i].mtrr_ctl=-1;
|
||||
break;
|
||||
default:
|
||||
/* We should never hit this */
|
||||
}
|
||||
}
|
||||
if (retval == -2)
|
||||
{
|
||||
DEBUGMSG(("3dfx: Could not set MTRR for this graphics card\n"));
|
||||
retval = 0; /* Can still run */
|
||||
}
|
||||
#ifdef DEBUG
|
||||
else if (retval >= 0)
|
||||
DEBUGMSG(("3dfx: Successfully set MTRR, mtrr register: %d\n", retval));
|
||||
#endif
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
int resetmtrr_3dfx(void)
|
||||
{
|
||||
int i, ret, retval = 0;
|
||||
for (i = 0; i < numCards; i++) {
|
||||
if (cards[i].mtrr_buf >= 0) {
|
||||
ret = mtrr_del(cards[i].mtrr_buf, 0, 0);
|
||||
if (ret < 0)
|
||||
retval = ret;
|
||||
cards[i].mtrr_buf = -1;
|
||||
if (cards[i].mtrr_ctl >= 0) {
|
||||
ret = mtrr_del(cards[i].mtrr_ctl, 0, 0);
|
||||
if (ret < 0)
|
||||
retval = ret;
|
||||
cards[i].mtrr_ctl = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
#endif /* HAVE_MTRR */
|
||||
|
||||
static struct file_operations fops_3dfx = {
|
||||
#if KERNEL_MIN_VER(2, 4, 0)
|
||||
owner: THIS_MODULE,
|
||||
#endif
|
||||
ioctl: ioctl_3dfx, /* ioctl */
|
||||
mmap: mmap_3dfx, /* mmap */
|
||||
open: open_3dfx, /* open */
|
||||
release: release_3dfx, /* release */
|
||||
};
|
||||
|
||||
#ifdef MODULE
|
||||
int init_module(void)
|
||||
{
|
||||
int ret;
|
||||
DEBUGMSG(("3dfx: Entering init_module()\n"));
|
||||
|
||||
if ((ret = register_chrdev(MAJOR_3DFX, "3dfx", &fops_3dfx)) < 0) {
|
||||
printk("3dfx: Unable to register character device with major %d\n", MAJOR_3DFX);
|
||||
return ret;
|
||||
}
|
||||
DEBUGMSG(("3dfx: Successfully registered device 3dfx\n"));
|
||||
findCards();
|
||||
|
||||
#ifdef HAVE_MTRR
|
||||
ret = setmtrr_3dfx();
|
||||
if (ret < 0)
|
||||
{
|
||||
DEBUGMSG(("setmtrr_3dfx() failed, returned %d\n", ret));
|
||||
/*
|
||||
unregister_chrdev(MAJOR_3DFX, "3dfx");
|
||||
return ret;
|
||||
*/
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cleanup_module(void)
|
||||
{
|
||||
DEBUGMSG(("3dfx: Entering cleanup_module\n"));
|
||||
|
||||
#ifdef HAVE_MTRR
|
||||
resetmtrr_3dfx();
|
||||
#endif
|
||||
if (unregister_chrdev(MAJOR_3DFX, "3dfx"))
|
||||
{
|
||||
DEBUGMSG(("3dfx: unregister_chrdev failed\n"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
#else /* !MODULE */
|
||||
|
||||
long init_3dfx(long mem_start, long mem_end)
|
||||
{
|
||||
if (register_chrdev(MAJOR_3DFX, "3dfx", &fops_3dfx))
|
||||
{
|
||||
DEBUGMSG(("3dfx: Unable to register_chrdev with major %d\n", MAJOR_3DFX));
|
||||
return 0;
|
||||
}
|
||||
findCards();
|
||||
|
||||
return mem_start;
|
||||
}
|
||||
#endif /* !MODULE */
|
||||
|
||||
|
||||
#if defined(DEBUG)
|
||||
/*
|
||||
* Kludge to get rid of:
|
||||
* ./3dfx.o: unresolved symbol inb
|
||||
* ./3dfx.o: unresolved symbol inl
|
||||
* ./3dfx.o: unresolved symbol inw
|
||||
* ./3dfx.o: unresolved symbol outb
|
||||
* ./3dfx.o: unresolved symbol outl
|
||||
* ./3dfx.o: unresolved symbol outw
|
||||
* when compiling this module with -g during debugging.
|
||||
*/
|
||||
#undef __KERNEL__
|
||||
#define extern
|
||||
#undef _ASM_IO_H
|
||||
#include <asm/io.h>
|
||||
/*
|
||||
* And this is copied from asm/uaccess.h
|
||||
* in order to get rid of
|
||||
* ./3dfx.o: unresolved symbol verify_area
|
||||
* when compiling this module with -g during debugging.
|
||||
*/
|
||||
inline int verify_area(int type, const void * addr, unsigned long size)
|
||||
{
|
||||
return access_ok(type,addr,size) ? 0 : -EFAULT;
|
||||
}
|
||||
|
||||
int a_last_dummy_function(int a)
|
||||
{
|
||||
return a;
|
||||
}
|
||||
#endif
|
160
device/Device3Dfx.spec
Normal file
160
device/Device3Dfx.spec
Normal file
@ -0,0 +1,160 @@
|
||||
Summary: Device driver for 3Dfx boards for 2.x kernels
|
||||
Name: Device3Dfx
|
||||
Version: 2.3
|
||||
Release: 5
|
||||
Icon: 3dfx.gif
|
||||
Source: Device3Dfx.tar.gz
|
||||
License: GPL
|
||||
Group: Drivers
|
||||
|
||||
%changelog
|
||||
|
||||
* Sat Apr 08 2000 Joseph Kain <joseph@3dfx.com>
|
||||
Release 2.3-5
|
||||
- Check both the kernel and the processor for MTRR support before enabling.
|
||||
|
||||
* Wed Jan 04 2000 Joseph Kain <joseph@3dfx.com>
|
||||
Release 2.3-4
|
||||
|
||||
- Forgot to include kinfo.h in 3dfx_driver.c in the last release. This
|
||||
fixes some build problems for SMP kernels.
|
||||
|
||||
* Wed Dec 22 1999 Joseph Kain <joseph@3dfx.com>
|
||||
Release 2.3-3
|
||||
|
||||
- Fixes to the Makefile to stop grepping the header files. Changes to
|
||||
mtrr.c and 3dfx_driver.c to support these Makefile changes.
|
||||
- 3dfx_driver.c now supports 2.3.14+ kernels.
|
||||
|
||||
* Mon Dec 13 1999 Joseph Kain <joseph@3dfx.com>
|
||||
Release 2.3-2
|
||||
|
||||
- Removed depmod -a from the Makefile. This elliminates errors in the
|
||||
build on systems with modules with broken depedancies (Happens on Madrake)
|
||||
- Changed depmod -a to depmod -a > /dev/null in the post and postun to
|
||||
elliminate any warnings/errors with modules that don't have anything
|
||||
to do with Device3Dfx. Warnings scare people.
|
||||
|
||||
* Tue Nov 09 1999 Joseph Kain <joseph@3dfx.com>
|
||||
Release 2.3:
|
||||
|
||||
- Made cards static to remove conflict with hisax ISDN driver.
|
||||
- Removed 3dfx.o from the source distribution
|
||||
|
||||
* Fri Jun 25 1999 Daryll Strauss <daryll@harlot.rb.ca.us>
|
||||
|
||||
Release 2.2:
|
||||
|
||||
- Set MTRR for VB/V3 boards
|
||||
- Allow build without kernel sources installed
|
||||
- Code cleanup
|
||||
- Improvements in /etc/conf.modules setup
|
||||
|
||||
* Sun May 16 1999 Daryll Strauss <daryll@harlot.rb.ca.us>
|
||||
|
||||
Release 2.1:
|
||||
|
||||
- Added support for multiple board mappings
|
||||
- Enlarged size of mappings
|
||||
- Changes to support VB/V3 boards
|
||||
|
||||
* Sun Apr 4 1999 Daryll Strauss <daryll@harlot.rb.ca.us>
|
||||
|
||||
Release 2.0-1:
|
||||
|
||||
- This is just a rename of Michael's version to return it to my original
|
||||
naming scheme.
|
||||
- Added the License field to the spec file.
|
||||
|
||||
* Thu Mar 11 1999 Michael Vance <mkv102@psu.edu>
|
||||
|
||||
Release 2.5-2:
|
||||
|
||||
- Fixed for 2.2.3 kernels because of an mmap() update
|
||||
|
||||
* Fri Oct 30 1998 Carlo Wood <carlo@runaway.xs4all.nl>
|
||||
|
||||
Release 2.5-1:
|
||||
|
||||
- Upped source to Dev3Dfx-2.5.tar.gz
|
||||
- Boosted version number of rpm to the version of the tar.gz.
|
||||
Moved everything to the Makefile, so Dev3Dfx-2.5.tar.gz is
|
||||
self containing and will work without rpm too.
|
||||
|
||||
* Wed Oct 21 1998 Carlo Wood <carlo@runaway.xs4all.nl>
|
||||
|
||||
Release 1.2-5:
|
||||
|
||||
- Upped source to Dev3Dfx-2.4.tar.gz
|
||||
- Removed dangerous and confusing use of /lib/modules/preferred
|
||||
- Removed the use of `uname': It now will compile for the kernel
|
||||
version in /usr/src/linux and not be bothered by the running
|
||||
kernel version.
|
||||
|
||||
* Wed Oct 21 1998 Carlo Wood <carlo@runaway.xs4all.nl>
|
||||
|
||||
Release 1.2-4:
|
||||
|
||||
- Upped source to Dev3Dfx-2.3.tar.gz
|
||||
|
||||
* Mon Oct 19 1998 Carlo Wood <carlo@runaway.xs4all.nl>
|
||||
|
||||
Release 1.2-3:
|
||||
|
||||
- Corrected Summary and Description to refer to 2.x rather
|
||||
then 2.1 kernels.
|
||||
- Removed script lines that write an `option' line to /etc/conf.modules.
|
||||
- Upped source to Dev3Dfx-2.2.tar.gz
|
||||
|
||||
* Sun Oct 18 1998 Carlo Wood <carlo@runaway.xs4all.nl>
|
||||
|
||||
Release 1.2-2:
|
||||
|
||||
- Added OPT_CFLAGS stuff.
|
||||
|
||||
* Sun Oct 18 1998 Carlo Wood <carlo@runaway.xs4all.nl>
|
||||
|
||||
Release 1.2-1:
|
||||
|
||||
- Packaged version 1.2 with support for 2.1 kernels by John Taylor and
|
||||
MTRR settings added by Jens Axboe.
|
||||
|
||||
%description
|
||||
This package installs the 3Dfx device driver to allow access to 3Dfx
|
||||
boards without the user having root privledges. It should work on both
|
||||
2.0 and 2.1/2.2 kernels and set the MTRR settings correctly. It should
|
||||
also work with SMP kernels (2.1/2.2).
|
||||
|
||||
%prep
|
||||
%setup -c
|
||||
|
||||
%build
|
||||
make OPT_CFLAGS="$RPM_OPT_FLAGS"
|
||||
|
||||
%install
|
||||
make RPM_INSTALL="1" install | grep '^/lib/modules/' > modules-file-list
|
||||
|
||||
%post
|
||||
if [ "$1" = 1 ]; then
|
||||
grep -v 3dfx /etc/conf.modules > /etc/conf.modules.tmp
|
||||
echo alias char-major-107 3dfx >> /etc/conf.modules.tmp
|
||||
mv /etc/conf.modules.tmp /etc/conf.modules
|
||||
fi
|
||||
/sbin/depmod -a > /dev/null
|
||||
|
||||
%postun
|
||||
if [ "$1" = 0 ]; then
|
||||
grep -v 3dfx /etc/conf.modules > /etc/conf.modules.tmp
|
||||
mv /etc/conf.modules.tmp /etc/conf.modules
|
||||
fi
|
||||
/sbin/depmod -a > /dev/null
|
||||
|
||||
%verifyscript
|
||||
inconf=`grep 'alias char-major-107 3dfx' /etc/conf.modules`
|
||||
if [ "x$inconf" = "x" ]; then
|
||||
echo "3dfx entry not included in /etc/conf.modules"
|
||||
fi
|
||||
|
||||
%files -f modules-file-list
|
||||
%verify(not mode user group) /dev/3dfx
|
||||
|
110
device/Makefile
Normal file
110
device/Makefile
Normal file
@ -0,0 +1,110 @@
|
||||
###############################################################################
|
||||
# Makefile by Carlo Wood (and others)
|
||||
|
||||
ifeq ($(OPT_CFLAGS),)
|
||||
|
||||
# Determine the machine type
|
||||
ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
|
||||
|
||||
# Setup machine dependant compiler flags
|
||||
ifeq ($(ARCH), i386)
|
||||
OPT_CFLAGS = -O2 -m486 -fomit-frame-pointer \
|
||||
-fno-strength-reduce \
|
||||
-malign-loops=2 -malign-jumps=2 -malign-functions=2
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH), alpha)
|
||||
OPT_CFLAGS = -O2 -mno-fp-regs -mcpu=ev4 \
|
||||
-ffixed-8 \
|
||||
-Wa,-mev6 \
|
||||
-fomit-frame-pointer -fno-strict-aliasing
|
||||
endif
|
||||
|
||||
endif # ifeq ($OPT_CFLAGS),)
|
||||
|
||||
CFLAGS := -DMODULE -D__KERNEL__ -I/usr/src/linux/include $(OPT_CFLAGS)
|
||||
|
||||
###############################################################################
|
||||
# You should never need to change anything below.
|
||||
|
||||
all: sanity 3dfx.o
|
||||
|
||||
# Sanity checks
|
||||
sanity:
|
||||
@( \
|
||||
if [ ! -e /usr/src/linux ]; then \
|
||||
echo "Expect kernel source at location /usr/src/linux"; \
|
||||
echo "Sym.link /usr/src/linux -> where you have your sources"; \
|
||||
exit -1; \
|
||||
fi; \
|
||||
if [ ! -r /usr/src/linux/include ]; then \
|
||||
echo "Expect readable headers in /usr/src/linux/include"; \
|
||||
exit -1; \
|
||||
fi; \
|
||||
if [ ! -r /usr/src/linux/include/linux/version.h ]; then \
|
||||
echo "Missing /usr/src/linux/include/linux/version.h"; \
|
||||
echo "Configure and install the kernel first"; \
|
||||
exit -1; \
|
||||
fi; \
|
||||
if [ ! -e /proc/cpuinfo ]; then \
|
||||
echo "You need the /proc file system"; \
|
||||
echo "Reconfigure kernel and say Yes to CONFIG_PROC_FS"; \
|
||||
exit -1; \
|
||||
fi; \
|
||||
)
|
||||
|
||||
kinfo: kinfo.c
|
||||
$(CC) -o kinfo kinfo.c
|
||||
|
||||
kinfo.h: kinfo
|
||||
./kinfo
|
||||
|
||||
###############################################################################
|
||||
# kernel 2.1+
|
||||
|
||||
3dfx.o: kinfo.h 3dfx_driver.c Makefile
|
||||
$(CC) $(CFLAGS) -c -o $@ 3dfx_driver.c
|
||||
|
||||
###############################################################################
|
||||
|
||||
install:
|
||||
mkdir -p /lib/modules/$(shell ./kinfo --UTS)/misc
|
||||
cp 3dfx.o /lib/modules/$(shell ./kinfo --UTS)/misc/3dfx.o
|
||||
@( \
|
||||
if [ -e /lib/modules/$(shell ./kinfo --UTS)/modules.dep ]; then \
|
||||
indep=`grep 'misc/3dfx.o:' /lib/modules/$(shell ./kinfo --UTS)/modules.dep`; \
|
||||
if [ -z "$$indep" ]; then \
|
||||
echo "/lib/modules/$(shell ./kinfo --UTS)/misc/3dfx.o:" >> /lib/modules/$(shell ./kinfo --UTS)/modules.dep; \
|
||||
echo "" >> /lib/modules/$(shell ./kinfo --UTS)/modules.dep; \
|
||||
fi; \
|
||||
fi; \
|
||||
if [ ! -c /dev/3dfx ]; then \
|
||||
mknod /dev/3dfx c 107 0; \
|
||||
chmod go+w /dev/3dfx; \
|
||||
fi; \
|
||||
if [ "$(RPM_INSTALL)" = "1" ]; then \
|
||||
echo "/lib/modules/$(shell ./kinfo --UTS)/misc/3dfx.o"; \
|
||||
else \
|
||||
inconf=`grep 'alias char-major-107 3dfx' /etc/conf.modules`; \
|
||||
if [ -z "$$inconf" ]; then \
|
||||
echo "alias char-major-107 3dfx" >> /etc/conf.modules; \
|
||||
fi; \
|
||||
fi; \
|
||||
)
|
||||
|
||||
###############################################################################
|
||||
# This is for debugging purposes by the developers:
|
||||
|
||||
clean:
|
||||
rm -f *.o *.s kinfo kinfo.h
|
||||
|
||||
3dfx.s: 3dfx_driver.c Makefile
|
||||
$(CC) $(CFLAGS) -S -c 3dfx_driver.c
|
||||
|
||||
tar:
|
||||
tar czf ../../SOURCES/Dev3Dfx-2.5.tar.gz 3dfx_driver.c Makefile
|
||||
|
||||
|
||||
debug:
|
||||
make OPT_CFLAGS="-g -Wall -Wstrict-prototypes -DDEBUG"
|
||||
|
67
device/kinfo.c
Normal file
67
device/kinfo.c
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
kinfo.c: A small program that includes several kernel heades
|
||||
and builds a header defining options for 3dfx_driver.c.
|
||||
|
||||
1999/12/22 Joseph Kain <joseph@3dfx.com>
|
||||
|
||||
* Initial version
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
/* These include files will bring in the information we need */
|
||||
#include <linux/autoconf.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
/* Here is the strategy. The old Makefile would have grepped the kernel
|
||||
* headers and put these defines on the command line. Now we include
|
||||
* the kernel headers and extract the information. Then we create a
|
||||
* new header with all of the options we need. */
|
||||
|
||||
if (argc == 1)
|
||||
{
|
||||
FILE *f = fopen ("kinfo.h", "w");
|
||||
FILE *p = NULL;
|
||||
int result;
|
||||
char temp[1000];
|
||||
|
||||
#ifdef CONFIG_MTRR
|
||||
/* It is not enough to just check if the kernel supports MTRRs, if the
|
||||
* processor doesn't have MTRRs then its possible (and likely) that
|
||||
* the kernel was still compiled with MTRR support. So we also have to
|
||||
* check the processor has MTRRs. */
|
||||
|
||||
result = system ("grep mtrr /proc/cpuinfo > /dev/null");
|
||||
|
||||
/* See if grep found anything */
|
||||
if (result == 0) /* Grep reported a match */
|
||||
{
|
||||
fprintf (f, "#define HAVE_MTRR\n");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
fprintf (f, "#define __SMP__\n");
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MODVERSIONS
|
||||
fprintf (f, "#define MODVERSIONS\n");
|
||||
#endif
|
||||
|
||||
fclose (f);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (strcmp (argv[1], "--UTS") == 0)
|
||||
{
|
||||
printf ("%s", UTS_RELEASE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
935
device/mtrrs.c
Normal file
935
device/mtrrs.c
Normal file
@ -0,0 +1,935 @@
|
||||
/* Generic MTRR (Memory Type Range Register) driver.
|
||||
|
||||
Copyright (C) 1997-1998 Richard Gooch
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the Free
|
||||
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
Richard Gooch may be reached by email at rgooch@atnf.csiro.au
|
||||
The postal address is:
|
||||
Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia.
|
||||
|
||||
Source: "Pentium Pro Family Developer's Manual, Volume 3:
|
||||
Operating System Writer's Guide" (Intel document number 242692),
|
||||
section 11.11.7
|
||||
|
||||
ChangeLog
|
||||
|
||||
Prehistory Martin Tischhäuser <martin@ikcbarka.fzk.de>
|
||||
Initial register-setting code (from proform-1.0).
|
||||
19971216 Richard Gooch <rgooch@atnf.csiro.au>
|
||||
Original version for /proc/mtrr interface, SMP-safe.
|
||||
v1.0
|
||||
19971217 Richard Gooch <rgooch@atnf.csiro.au>
|
||||
Bug fix for ioctls()'s.
|
||||
Added sample code in Documentation/mtrr.txt
|
||||
v1.1
|
||||
19971218 Richard Gooch <rgooch@atnf.csiro.au>
|
||||
Disallow overlapping regions.
|
||||
19971219 Jens Maurer <jmaurer@menuett.rhein-main.de>
|
||||
Register-setting fixups.
|
||||
v1.2
|
||||
19971222 Richard Gooch <rgooch@atnf.csiro.au>
|
||||
Fixups for kernel 2.1.75.
|
||||
v1.3
|
||||
19971229 David Wragg <dpw@doc.ic.ac.uk>
|
||||
Register-setting fixups and conformity with Intel conventions.
|
||||
19971229 Richard Gooch <rgooch@atnf.csiro.au>
|
||||
Cosmetic changes and wrote this ChangeLog ;-)
|
||||
19980106 Richard Gooch <rgooch@atnf.csiro.au>
|
||||
Fixups for kernel 2.1.78.
|
||||
v1.4
|
||||
19980119 David Wragg <dpw@doc.ic.ac.uk>
|
||||
Included passive-release enable code (elsewhere in PCI setup).
|
||||
v1.5
|
||||
19980131 Richard Gooch <rgooch@atnf.csiro.au>
|
||||
Replaced global kernel lock with private spinlock.
|
||||
v1.6
|
||||
19980201 Richard Gooch <rgooch@atnf.csiro.au>
|
||||
Added wait for other CPUs to complete changes.
|
||||
v1.7
|
||||
19980202 Richard Gooch <rgooch@atnf.csiro.au>
|
||||
Bug fix in definition of <set_mtrr> for UP.
|
||||
v1.8
|
||||
19980319 Richard Gooch <rgooch@atnf.csiro.au>
|
||||
Fixups for kernel 2.1.90.
|
||||
19980323 Richard Gooch <rgooch@atnf.csiro.au>
|
||||
Move SMP BIOS fixup before secondary CPUs call <calibrate_delay>
|
||||
v1.9
|
||||
19980325 Richard Gooch <rgooch@atnf.csiro.au>
|
||||
Fixed test for overlapping regions: confused by adjacent regions
|
||||
19980326 Richard Gooch <rgooch@atnf.csiro.au>
|
||||
Added wbinvd in <set_mtrr_prepare>.
|
||||
19980401 Richard Gooch <rgooch@atnf.csiro.au>
|
||||
Bug fix for non-SMP compilation.
|
||||
19980418 David Wragg <dpw@doc.ic.ac.uk>
|
||||
Fixed-MTRR synchronisation for SMP and use atomic operations
|
||||
instead of spinlocks.
|
||||
19980418 Richard Gooch <rgooch@atnf.csiro.au>
|
||||
Differentiate different MTRR register classes for BIOS fixup.
|
||||
v1.10
|
||||
19980419 David Wragg <dpw@doc.ic.ac.uk>
|
||||
Bug fix in variable MTRR synchronisation.
|
||||
v1.11
|
||||
19980419 Richard Gooch <rgooch@atnf.csiro.au>
|
||||
Fixups for kernel 2.1.97.
|
||||
v1.12
|
||||
19980421 Richard Gooch <rgooch@atnf.csiro.au>
|
||||
Safer synchronisation across CPUs when changing MTRRs.
|
||||
v1.13
|
||||
19980423 Richard Gooch <rgooch@atnf.csiro.au>
|
||||
Bugfix for SMP systems without MTRR support.
|
||||
v1.14
|
||||
19980427 Richard Gooch <rgooch@atnf.csiro.au>
|
||||
Trap calls to <mtrr_add> and <mtrr_del> on non-MTRR machines.
|
||||
v1.15
|
||||
19980427 Richard Gooch <rgooch@atnf.csiro.au>
|
||||
Use atomic bitops for setting SMP change mask.
|
||||
v1.16
|
||||
19980428 Richard Gooch <rgooch@atnf.csiro.au>
|
||||
Removed spurious diagnostic message.
|
||||
v1.17
|
||||
19980429 Richard Gooch <rgooch@atnf.csiro.au>
|
||||
Moved register-setting macros into this file.
|
||||
Moved setup code from init/main.c to i386-specific areas.
|
||||
v1.18
|
||||
19980502 Richard Gooch <rgooch@atnf.csiro.au>
|
||||
Moved MTRR detection outside conditionals in <mtrr_init>.
|
||||
v1.19
|
||||
19980502 Richard Gooch <rgooch@atnf.csiro.au>
|
||||
Documentation improvement: mention Pentium II and AGP.
|
||||
v1.20
|
||||
19980521 Richard Gooch <rgooch@atnf.csiro.au>
|
||||
Only manipulate interrupt enable flag on local CPU.
|
||||
Allow enclosed uncachable regions.
|
||||
v1.21
|
||||
|
||||
v1.21-- Emil Briggs <briggs@tick.physics.ncsu.edu>
|
||||
Backported to 2.0.x kernels, /proc and ioctl
|
||||
interfaces removed and the result incorporated
|
||||
into the 3dfx device driver. Not SMP safe. If
|
||||
you have an SMP use the 2.2 series kernels.
|
||||
|
||||
v1.22
|
||||
19991222 Joseph Kain <joseph@3dfx.com>
|
||||
Do kernel version checks here instead of in
|
||||
Makefile. The Makefile now only needs the
|
||||
kernel version for install.
|
||||
*/
|
||||
|
||||
|
||||
/* Include this first as it defines things that affect the kernel headers */
|
||||
#include "kinfo.h"
|
||||
|
||||
/* MTRR support is only available in kerenls versioned 2.1.0 or higher.
|
||||
* For earlier kernels this code compiles to nothing. */
|
||||
#include <linux/version.h>
|
||||
|
||||
#ifndef KERNEL_VERSION
|
||||
#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 1, 0)
|
||||
|
||||
|
||||
#ifndef _LINUX_MTRR_H
|
||||
#define _LINUX_MTRR_H
|
||||
|
||||
struct mtrr_sentry
|
||||
{
|
||||
unsigned long base; /* Base address */
|
||||
unsigned long size; /* Size of region */
|
||||
unsigned int type; /* Type of region */
|
||||
};
|
||||
|
||||
struct mtrr_gentry
|
||||
{
|
||||
unsigned int regnum; /* Register number */
|
||||
unsigned long base; /* Base address */
|
||||
unsigned long size; /* Size of region */
|
||||
unsigned int type; /* Type of region */
|
||||
};
|
||||
|
||||
|
||||
/* These are the region types */
|
||||
#define MTRR_TYPE_UNCACHABLE 0
|
||||
#define MTRR_TYPE_WRCOMB 1
|
||||
/*#define MTRR_TYPE_ 2*/
|
||||
/*#define MTRR_TYPE_ 3*/
|
||||
#define MTRR_TYPE_WRTHROUGH 4
|
||||
#define MTRR_TYPE_WRPROT 5
|
||||
#define MTRR_TYPE_WRBACK 6
|
||||
#define MTRR_NUM_TYPES 7
|
||||
|
||||
static char *mtrr_strings[MTRR_NUM_TYPES] =
|
||||
{
|
||||
"uncachable", /* 0 */
|
||||
"write-combining", /* 1 */
|
||||
"?", /* 2 */
|
||||
"?", /* 3 */
|
||||
"write-through", /* 4 */
|
||||
"write-protect", /* 5 */
|
||||
"write-back", /* 6 */
|
||||
};
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
/* The following functions are for use by other drivers */
|
||||
extern int mtrr_add (unsigned long base, unsigned long size,
|
||||
unsigned int type, char increment);
|
||||
extern int mtrr_del (int reg, unsigned long base, unsigned long size);
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* _LINUX_MTRR_H */
|
||||
|
||||
#ifdef MODULE
|
||||
#include <linux/autoconf.h>
|
||||
#if defined(CONFIG_MODVERSIONS) && !defined(MODVERSIONS)
|
||||
#define MODVERSIONS
|
||||
#endif
|
||||
#ifdef MODVERSIONS
|
||||
#include <linux/modversions.h>
|
||||
#endif
|
||||
#endif
|
||||
#include <linux/sched.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/config.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/malloc.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#define MTRR_NEED_STRINGS
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/segment.h>
|
||||
#include <asm/bitops.h>
|
||||
#include <asm/smp_lock.h>
|
||||
#include <asm/atomic.h>
|
||||
#include <linux/smp.h>
|
||||
|
||||
#undef __SMP__
|
||||
|
||||
#define MTRR_VERSION "1.21 (19980521)"
|
||||
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
|
||||
#define X86_FEATURE_MTRR 0x1000 /* memory type registers */
|
||||
|
||||
#define MTRRcap_MSR 0x0fe
|
||||
#define MTRRdefType_MSR 0x2ff
|
||||
|
||||
#define MTRRphysBase_MSR(reg) (0x200 + 2 * (reg))
|
||||
#define MTRRphysMask_MSR(reg) (0x200 + 2 * (reg) + 1)
|
||||
|
||||
#define NUM_FIXED_RANGES 88
|
||||
#define MTRRfix64K_00000_MSR 0x250
|
||||
#define MTRRfix16K_80000_MSR 0x258
|
||||
#define MTRRfix16K_A0000_MSR 0x259
|
||||
#define MTRRfix4K_C0000_MSR 0x268
|
||||
#define MTRRfix4K_C8000_MSR 0x269
|
||||
#define MTRRfix4K_D0000_MSR 0x26a
|
||||
#define MTRRfix4K_D8000_MSR 0x26b
|
||||
#define MTRRfix4K_E0000_MSR 0x26c
|
||||
#define MTRRfix4K_E8000_MSR 0x26d
|
||||
#define MTRRfix4K_F0000_MSR 0x26e
|
||||
#define MTRRfix4K_F8000_MSR 0x26f
|
||||
|
||||
|
||||
#ifdef __SMP__
|
||||
# define MTRR_CHANGE_MASK_FIXED 0x01
|
||||
# define MTRR_CHANGE_MASK_VARIABLE 0x02
|
||||
# define MTRR_CHANGE_MASK_DEFTYPE 0x04
|
||||
#endif
|
||||
|
||||
/* In the processor's MTRR interface, the MTRR type is always held in
|
||||
an 8 bit field: */
|
||||
typedef u8 mtrr_type;
|
||||
|
||||
#define LINE_SIZE 80
|
||||
#define JIFFIE_TIMEOUT 100
|
||||
|
||||
#define set_mtrr(reg,base,size,type) set_mtrr_up (reg, base, size, type,TRUE)
|
||||
|
||||
|
||||
static unsigned int *usage_table = NULL;
|
||||
|
||||
|
||||
struct set_mtrr_context
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned long deftype_lo;
|
||||
unsigned long deftype_hi;
|
||||
unsigned long cr4val;
|
||||
};
|
||||
|
||||
/*
|
||||
* Access to machine-specific registers (available on 586 and better only)
|
||||
* Note: the rd* operations modify the parameters directly (without using
|
||||
* pointer indirection), this allows gcc to optimize better
|
||||
*/
|
||||
#define rdmsr(msr,val1,val2) \
|
||||
__asm__ __volatile__("rdmsr" \
|
||||
: "=a" (val1), "=d" (val2) \
|
||||
: "c" (msr))
|
||||
|
||||
#define wrmsr(msr,val1,val2) \
|
||||
__asm__ __volatile__("wrmsr" \
|
||||
: /* no outputs */ \
|
||||
: "c" (msr), "a" (val1), "d" (val2))
|
||||
|
||||
#define rdtsc(low,high) \
|
||||
__asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high))
|
||||
|
||||
#define rdpmc(counter,low,high) \
|
||||
__asm__ __volatile__("rdpmc" \
|
||||
: "=a" (low), "=d" (high) \
|
||||
: "c" (counter))
|
||||
|
||||
|
||||
/* Put the processor into a state where MTRRs can be safely set. */
|
||||
static void set_mtrr_prepare(struct set_mtrr_context *ctxt)
|
||||
{
|
||||
unsigned long tmp;
|
||||
|
||||
/* disable interrupts locally */
|
||||
save_flags (ctxt->flags); cli ();
|
||||
|
||||
/* save value of CR4 and clear Page Global Enable (bit 7) */
|
||||
asm volatile ("movl %%cr4, %0\n\t"
|
||||
"movl %0, %1\n\t"
|
||||
"andb $0x7f, %b1\n\t"
|
||||
"movl %1, %%cr4\n\t"
|
||||
: "=r" (ctxt->cr4val), "=q" (tmp) : : "memory");
|
||||
|
||||
/* disable and flush caches. Note that wbinvd flushes the TLBs as
|
||||
a side-effect. */
|
||||
asm volatile ("movl %%cr0, %0\n\t"
|
||||
"orl $0x40000000, %0\n\t"
|
||||
"wbinvd\n\t"
|
||||
"movl %0, %%cr0\n\t"
|
||||
"wbinvd\n\t"
|
||||
: "=r" (tmp) : : "memory");
|
||||
|
||||
/* disable MTRRs, and set the default type to uncached. */
|
||||
rdmsr(MTRRdefType_MSR, ctxt->deftype_lo, ctxt->deftype_hi);
|
||||
wrmsr(MTRRdefType_MSR, ctxt->deftype_lo & 0xf300UL, ctxt->deftype_hi);
|
||||
} /* End Function set_mtrr_prepare */
|
||||
|
||||
|
||||
/* Restore the processor after a set_mtrr_prepare */
|
||||
static void set_mtrr_done(struct set_mtrr_context *ctxt)
|
||||
{
|
||||
unsigned long tmp;
|
||||
|
||||
/* flush caches and TLBs */
|
||||
asm volatile ("wbinvd" : : : "memory" );
|
||||
|
||||
/* restore MTRRdefType */
|
||||
wrmsr(MTRRdefType_MSR, ctxt->deftype_lo, ctxt->deftype_hi);
|
||||
|
||||
/* enable caches */
|
||||
asm volatile ("movl %%cr0, %0\n\t"
|
||||
"andl $0xbfffffff, %0\n\t"
|
||||
"movl %0, %%cr0\n\t"
|
||||
: "=r" (tmp) : : "memory");
|
||||
|
||||
/* restore value of CR4 */
|
||||
asm volatile ("movl %0, %%cr4"
|
||||
: : "r" (ctxt->cr4val) : "memory");
|
||||
|
||||
/* re-enable interrupts locally (if enabled previously) */
|
||||
restore_flags (ctxt->flags);
|
||||
} /* End Function set_mtrr_done */
|
||||
|
||||
|
||||
/* this function returns the number of variable MTRRs */
|
||||
static unsigned int get_num_var_ranges (void)
|
||||
{
|
||||
unsigned long config, dummy;
|
||||
|
||||
rdmsr(MTRRcap_MSR, config, dummy);
|
||||
return (config & 0xff);
|
||||
} /* End Function get_num_var_ranges */
|
||||
|
||||
|
||||
/* non-zero if we have the write-combining memory type. */
|
||||
static int have_wrcomb (void)
|
||||
{
|
||||
unsigned long config, dummy;
|
||||
|
||||
rdmsr(MTRRcap_MSR, config, dummy);
|
||||
return (config & (1<<10));
|
||||
}
|
||||
|
||||
|
||||
static void get_mtrr (unsigned int reg, unsigned long *base,
|
||||
unsigned long *size, mtrr_type *type)
|
||||
{
|
||||
unsigned long dummy, mask_lo, base_lo;
|
||||
|
||||
rdmsr(MTRRphysMask_MSR(reg), mask_lo, dummy);
|
||||
if ((mask_lo & 0x800) == 0) {
|
||||
/* Invalid (i.e. free) range. */
|
||||
*base = 0;
|
||||
*size = 0;
|
||||
*type = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
rdmsr(MTRRphysBase_MSR(reg), base_lo, dummy);
|
||||
|
||||
/* We ignore the extra address bits (32-35). If someone wants to
|
||||
run x86 Linux on a machine with >4GB memory, this will be the
|
||||
least of their problems. */
|
||||
|
||||
/* Clean up mask_lo so it gives the real address mask. */
|
||||
mask_lo = (mask_lo & 0xfffff000UL);
|
||||
|
||||
/* This works correctly if size is a power of two, i.e. a
|
||||
contiguous range. */
|
||||
*size = ~(mask_lo - 1);
|
||||
|
||||
*base = (base_lo & 0xfffff000UL);
|
||||
*type = (base_lo & 0xff);
|
||||
} /* End Function get_mtrr */
|
||||
|
||||
|
||||
static void set_mtrr_up (unsigned int reg, unsigned long base,
|
||||
unsigned long size, mtrr_type type, int do_safe)
|
||||
/* [SUMMARY] Set variable MTRR register on the local CPU.
|
||||
<reg> The register to set.
|
||||
<base> The base address of the region.
|
||||
<size> The size of the region. If this is 0 the region is disabled.
|
||||
<type> The type of the region.
|
||||
<do_safe> If TRUE, do the change safely. If FALSE, safety measures should
|
||||
be done externally.
|
||||
*/
|
||||
{
|
||||
struct set_mtrr_context ctxt;
|
||||
|
||||
if (do_safe) set_mtrr_prepare (&ctxt);
|
||||
if (size == 0)
|
||||
{
|
||||
/* The invalid bit is kept in the mask, so we simply clear the
|
||||
relevant mask register to disable a range. */
|
||||
wrmsr (MTRRphysMask_MSR (reg), 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
wrmsr (MTRRphysBase_MSR (reg), base | type, 0);
|
||||
wrmsr (MTRRphysMask_MSR (reg), ~(size - 1) | 0x800, 0);
|
||||
}
|
||||
if (do_safe) set_mtrr_done (&ctxt);
|
||||
} /* End Function set_mtrr_up */
|
||||
|
||||
|
||||
#ifdef __SMP__
|
||||
|
||||
struct mtrr_var_range
|
||||
{
|
||||
unsigned long base_lo;
|
||||
unsigned long base_hi;
|
||||
unsigned long mask_lo;
|
||||
unsigned long mask_hi;
|
||||
};
|
||||
|
||||
|
||||
/* Get the MSR pair relating to a var range. */
|
||||
static void get_mtrr_var_range (unsigned int index,
|
||||
struct mtrr_var_range *vr)
|
||||
{
|
||||
rdmsr (MTRRphysBase_MSR (index), vr->base_lo, vr->base_hi);
|
||||
rdmsr (MTRRphysMask_MSR (index), vr->mask_lo, vr->mask_hi);
|
||||
} /* End Function get_mtrr_var_range */
|
||||
|
||||
|
||||
/* Set the MSR pair relating to a var range. Returns TRUE if
|
||||
changes are made. */
|
||||
static int set_mtrr_var_range_testing (unsigned int index,
|
||||
struct mtrr_var_range *vr)
|
||||
{
|
||||
unsigned int lo, hi;
|
||||
int changed = FALSE;
|
||||
|
||||
rdmsr(MTRRphysBase_MSR(index), lo, hi);
|
||||
|
||||
if ((vr->base_lo & 0xfffff0ffUL) != (lo & 0xfffff0ffUL)
|
||||
|| (vr->base_hi & 0xfUL) != (hi & 0xfUL)) {
|
||||
wrmsr(MTRRphysBase_MSR(index), vr->base_lo, vr->base_hi);
|
||||
changed = TRUE;
|
||||
}
|
||||
|
||||
rdmsr(MTRRphysMask_MSR(index), lo, hi);
|
||||
|
||||
if ((vr->mask_lo & 0xfffff800UL) != (lo & 0xfffff800UL)
|
||||
|| (vr->mask_hi & 0xfUL) != (hi & 0xfUL)) {
|
||||
wrmsr(MTRRphysMask_MSR(index), vr->mask_lo, vr->mask_hi);
|
||||
changed = TRUE;
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
|
||||
static void get_fixed_ranges(mtrr_type *frs)
|
||||
{
|
||||
unsigned long *p = (unsigned long *)frs;
|
||||
int i;
|
||||
|
||||
rdmsr(MTRRfix64K_00000_MSR, p[0], p[1]);
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
rdmsr(MTRRfix16K_80000_MSR + i, p[2 + i*2], p[3 + i*2]);
|
||||
|
||||
for (i = 0; i < 8; i++)
|
||||
rdmsr(MTRRfix4K_C0000_MSR + i, p[6 + i*2], p[7 + i*2]);
|
||||
}
|
||||
|
||||
|
||||
static int set_fixed_ranges_testing(mtrr_type *frs)
|
||||
{
|
||||
unsigned long *p = (unsigned long *)frs;
|
||||
int changed = FALSE;
|
||||
int i;
|
||||
unsigned long lo, hi;
|
||||
|
||||
rdmsr(MTRRfix64K_00000_MSR, lo, hi);
|
||||
if (p[0] != lo || p[1] != hi) {
|
||||
wrmsr(MTRRfix64K_00000_MSR, p[0], p[1]);
|
||||
changed = TRUE;
|
||||
}
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
rdmsr(MTRRfix16K_80000_MSR + i, lo, hi);
|
||||
if (p[2 + i*2] != lo || p[3 + i*2] != hi) {
|
||||
wrmsr(MTRRfix16K_80000_MSR + i, p[2 + i*2], p[3 + i*2]);
|
||||
changed = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
rdmsr(MTRRfix4K_C0000_MSR + i, lo, hi);
|
||||
if (p[6 + i*2] != lo || p[7 + i*2] != hi) {
|
||||
wrmsr(MTRRfix4K_C0000_MSR + i, p[6 + i*2], p[7 + i*2]);
|
||||
changed = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
|
||||
struct mtrr_state
|
||||
{
|
||||
unsigned int num_var_ranges;
|
||||
struct mtrr_var_range *var_ranges;
|
||||
mtrr_type fixed_ranges[NUM_FIXED_RANGES];
|
||||
unsigned char enabled;
|
||||
mtrr_type def_type;
|
||||
};
|
||||
|
||||
|
||||
/* Grab all of the MTRR state for this CPU into *state. */
|
||||
static void get_mtrr_state(struct mtrr_state *state)
|
||||
{
|
||||
unsigned int nvrs, i;
|
||||
struct mtrr_var_range *vrs;
|
||||
unsigned long lo, dummy;
|
||||
|
||||
nvrs = state->num_var_ranges = get_num_var_ranges();
|
||||
vrs = state->var_ranges
|
||||
= kmalloc(nvrs * sizeof(struct mtrr_var_range), GFP_KERNEL);
|
||||
if (vrs == NULL)
|
||||
nvrs = state->num_var_ranges = 0;
|
||||
|
||||
for (i = 0; i < nvrs; i++)
|
||||
get_mtrr_var_range(i, &vrs[i]);
|
||||
|
||||
get_fixed_ranges(state->fixed_ranges);
|
||||
|
||||
rdmsr(MTRRdefType_MSR, lo, dummy);
|
||||
state->def_type = (lo & 0xff);
|
||||
state->enabled = (lo & 0xc00) >> 10;
|
||||
} /* End Function get_mtrr_state */
|
||||
|
||||
|
||||
/* Free resources associated with a struct mtrr_state */
|
||||
static void finalize_mtrr_state(struct mtrr_state *state)
|
||||
{
|
||||
if (state->var_ranges) kfree (state->var_ranges);
|
||||
} /* End Function finalize_mtrr_state */
|
||||
|
||||
|
||||
static unsigned long set_mtrr_state (struct mtrr_state *state,
|
||||
struct set_mtrr_context *ctxt)
|
||||
/* [SUMMARY] Set the MTRR state for this CPU.
|
||||
<state> The MTRR state information to read.
|
||||
<ctxt> Some relevant CPU context.
|
||||
[NOTE] The CPU must already be in a safe state for MTRR changes.
|
||||
[RETURNS] 0 if no changes made, else a mask indication what was changed.
|
||||
*/
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned long change_mask = 0;
|
||||
|
||||
for (i = 0; i < state->num_var_ranges; i++)
|
||||
if (set_mtrr_var_range_testing(i, &state->var_ranges[i]))
|
||||
change_mask |= MTRR_CHANGE_MASK_VARIABLE;
|
||||
|
||||
if (set_fixed_ranges_testing(state->fixed_ranges))
|
||||
change_mask |= MTRR_CHANGE_MASK_FIXED;
|
||||
|
||||
/* set_mtrr_restore restores the old value of MTRRdefType,
|
||||
so to set it we fiddle with the saved value. */
|
||||
if ((ctxt->deftype_lo & 0xff) != state->def_type
|
||||
|| ((ctxt->deftype_lo & 0xc00) >> 10) != state->enabled)
|
||||
{
|
||||
ctxt->deftype_lo |= (state->def_type | state->enabled << 10);
|
||||
change_mask |= MTRR_CHANGE_MASK_DEFTYPE;
|
||||
}
|
||||
|
||||
return change_mask;
|
||||
} /* End Function set_mtrr_state */
|
||||
|
||||
|
||||
static atomic_t undone_count;
|
||||
static void (*handler_func) (struct set_mtrr_context *ctxt, void *info);
|
||||
static void *handler_info;
|
||||
static volatile int wait_barrier_execute = FALSE;
|
||||
static volatile int wait_barrier_cache_enable = FALSE;
|
||||
|
||||
static void sync_handler (void)
|
||||
/* [SUMMARY] Synchronisation handler. Executed by "other" CPUs.
|
||||
[RETURNS] Nothing.
|
||||
*/
|
||||
{
|
||||
struct set_mtrr_context ctxt;
|
||||
|
||||
set_mtrr_prepare (&ctxt);
|
||||
/* Notify master CPU that I'm at the barrier and then wait */
|
||||
atomic_dec (&undone_count);
|
||||
while (wait_barrier_execute) barrier ();
|
||||
/* The master has cleared me to execute */
|
||||
(*handler_func) (&ctxt, handler_info);
|
||||
/* Notify master CPU that I've executed the function */
|
||||
atomic_dec (&undone_count);
|
||||
/* Wait for master to clear me to enable cache and return */
|
||||
while (wait_barrier_cache_enable) barrier ();
|
||||
set_mtrr_done (&ctxt);
|
||||
} /* End Function sync_handler */
|
||||
|
||||
static void do_all_cpus (void (*handler) (struct set_mtrr_context *ctxt,
|
||||
void *info),
|
||||
void *info, int local)
|
||||
/* [SUMMARY] Execute a function on all CPUs, with caches flushed and disabled.
|
||||
[PURPOSE] This function will synchronise all CPUs, flush and disable caches
|
||||
on all CPUs, then call a specified function. When the specified function
|
||||
finishes on all CPUs, caches are enabled on all CPUs.
|
||||
<handler> The function to execute.
|
||||
<info> An arbitrary information pointer which is passed to <<handler>>.
|
||||
<local> If TRUE <<handler>> is executed locally.
|
||||
[RETURNS] Nothing.
|
||||
*/
|
||||
{
|
||||
unsigned long timeout;
|
||||
struct set_mtrr_context ctxt;
|
||||
|
||||
mtrr_hook = sync_handler;
|
||||
handler_func = handler;
|
||||
handler_info = info;
|
||||
wait_barrier_execute = TRUE;
|
||||
wait_barrier_cache_enable = TRUE;
|
||||
/* Send a message to all other CPUs and wait for them to enter the
|
||||
barrier */
|
||||
atomic_set (&undone_count, smp_num_cpus - 1);
|
||||
smp_message_pass (MSG_ALL_BUT_SELF, MSG_MTRR_CHANGE, 0, 0);
|
||||
/* Wait for it to be done */
|
||||
timeout = jiffies + JIFFIE_TIMEOUT;
|
||||
while ( (atomic_read (&undone_count) > 0) && (jiffies < timeout) )
|
||||
barrier ();
|
||||
if (atomic_read (&undone_count) > 0)
|
||||
{
|
||||
panic ("mtrr: timed out waiting for other CPUs\n");
|
||||
}
|
||||
mtrr_hook = NULL;
|
||||
/* All other CPUs should be waiting for the barrier, with their caches
|
||||
already flushed and disabled. Prepare for function completion
|
||||
notification */
|
||||
atomic_set (&undone_count, smp_num_cpus - 1);
|
||||
/* Flush and disable the local CPU's cache and release the barier, which
|
||||
should cause the other CPUs to execute the function. Also execute it
|
||||
locally if required */
|
||||
set_mtrr_prepare (&ctxt);
|
||||
wait_barrier_execute = FALSE;
|
||||
if (local) (*handler) (&ctxt, info);
|
||||
/* Now wait for other CPUs to complete the function */
|
||||
while (atomic_read (&undone_count) > 0) barrier ();
|
||||
/* Now all CPUs should have finished the function. Release the barrier to
|
||||
allow them to re-enable their caches and return from their interrupt,
|
||||
then enable the local cache and return */
|
||||
wait_barrier_cache_enable = FALSE;
|
||||
set_mtrr_done (&ctxt);
|
||||
handler_func = NULL;
|
||||
handler_info = NULL;
|
||||
} /* End Function do_all_cpus */
|
||||
|
||||
|
||||
struct set_mtrr_data
|
||||
{
|
||||
unsigned long smp_base;
|
||||
unsigned long smp_size;
|
||||
unsigned int smp_reg;
|
||||
mtrr_type smp_type;
|
||||
};
|
||||
|
||||
static void set_mtrr_handler (struct set_mtrr_context *ctxt, void *info)
|
||||
{
|
||||
struct set_mtrr_data *data = info;
|
||||
|
||||
set_mtrr_up (data->smp_reg, data->smp_base, data->smp_size, data->smp_type,
|
||||
FALSE);
|
||||
} /* End Function set_mtrr_handler */
|
||||
|
||||
|
||||
|
||||
/* A warning that is common to the module and non-module cases. */
|
||||
/* Some BIOS's are fucked and don't set all MTRRs the same! */
|
||||
#ifdef MODULE
|
||||
static void mtrr_state_warn (unsigned long mask)
|
||||
#else
|
||||
static void mtrr_state_warn (unsigned long mask)
|
||||
#endif
|
||||
{
|
||||
if (!mask) return;
|
||||
if (mask & MTRR_CHANGE_MASK_FIXED)
|
||||
printk ("mtrr: your CPUs had inconsistent fixed MTRR settings\n");
|
||||
if (mask & MTRR_CHANGE_MASK_VARIABLE)
|
||||
printk ("mtrr: your CPUs had inconsistent variable MTRR settings\n");
|
||||
if (mask & MTRR_CHANGE_MASK_DEFTYPE)
|
||||
printk ("mtrr: your CPUs had inconsistent MTRRdefType settings\n");
|
||||
printk ("mtrr: probably your BIOS does not setup all CPUs\n");
|
||||
} /* End Function mtrr_state_warn */
|
||||
|
||||
#ifdef MODULE
|
||||
/* As a module, copy the MTRR state using an IPI handler. */
|
||||
|
||||
static volatile unsigned long smp_changes_mask = 0;
|
||||
|
||||
static void copy_mtrr_state_handler (struct set_mtrr_context *ctxt, void *info)
|
||||
{
|
||||
unsigned long mask, count;
|
||||
struct mtrr_state *smp_mtrr_state = info;
|
||||
|
||||
mask = set_mtrr_state (smp_mtrr_state, ctxt);
|
||||
/* Use the atomic bitops to update the global mask */
|
||||
for (count = 0; count < sizeof mask * 8; ++count)
|
||||
{
|
||||
if (mask & 0x01) set_bit (count, &smp_changes_mask);
|
||||
mask >>= 1;
|
||||
}
|
||||
} /* End Function copy_mtrr_state_handler */
|
||||
|
||||
/* Copies the entire MTRR state of this CPU to all the others. */
|
||||
static void copy_mtrr_state (void)
|
||||
{
|
||||
struct mtrr_state ms;
|
||||
|
||||
get_mtrr_state (&ms);
|
||||
do_all_cpus (copy_mtrr_state_handler, &ms, FALSE);
|
||||
finalize_mtrr_state (&ms);
|
||||
mtrr_state_warn (smp_changes_mask);
|
||||
} /* End Function copy_mtrr_state */
|
||||
|
||||
#endif /* MODULE */
|
||||
#endif /* __SMP__ */
|
||||
|
||||
static char *attrib_to_str (int x)
|
||||
{
|
||||
return (x <= 6) ? mtrr_strings[x] : "?";
|
||||
} /* End Function attrib_to_str */
|
||||
|
||||
static void init_table (void)
|
||||
{
|
||||
int i, max;
|
||||
|
||||
max = get_num_var_ranges ();
|
||||
if ( ( usage_table = kmalloc (max * sizeof *usage_table, GFP_KERNEL) )
|
||||
== NULL )
|
||||
{
|
||||
printk ("mtrr: could not allocate\n");
|
||||
return;
|
||||
}
|
||||
for (i = 0; i < max; i++) usage_table[i] = 1;
|
||||
|
||||
} /* End Function init_table */
|
||||
|
||||
int mtrr_add (unsigned long base, unsigned long size, unsigned int type,
|
||||
char increment)
|
||||
/* [SUMMARY] Add an MTRR entry.
|
||||
<base> The starting (base) address of the region.
|
||||
<size> The size (in bytes) of the region.
|
||||
<type> The type of the new region.
|
||||
<increment> If true and the region already exists, the usage count will be
|
||||
incremented.
|
||||
[RETURNS] The MTRR register on success, else a negative number indicating
|
||||
the error code.
|
||||
[NOTE] This routine uses a spinlock.
|
||||
*/
|
||||
{
|
||||
int i, max;
|
||||
mtrr_type ltype;
|
||||
unsigned long lbase, lsize, last;
|
||||
|
||||
if (!usage_table) init_table();
|
||||
if ( (base & 0xfff) || (size & 0xfff) )
|
||||
{
|
||||
printk ("mtrr: size and base must be multiples of 4kB\n");
|
||||
printk ("mtrr: size: %lx base: %lx\n", size, base);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (base + size < 0x100000)
|
||||
{
|
||||
printk ("mtrr: cannot set region below 1 MByte (0x%lx,0x%lx)\n",
|
||||
base, size);
|
||||
return -EINVAL;
|
||||
}
|
||||
/* Check upper bits of base and last are equal and lower bits are 0 for
|
||||
base and 1 for last */
|
||||
last = base + size - 1;
|
||||
for (lbase = base; !(lbase & 1) && (last & 1);
|
||||
lbase = lbase >> 1, last = last >> 1);
|
||||
if (lbase != last)
|
||||
{
|
||||
printk ("mtrr: base(0x%lx) is not aligned on a size(0x%lx) boundary\n",
|
||||
base, size);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (type >= MTRR_NUM_TYPES)
|
||||
{
|
||||
printk ("mtrr: type: %u illegal\n", type);
|
||||
return -EINVAL;
|
||||
}
|
||||
/* If the type is WC, check that this processor supports it */
|
||||
if ( (type == MTRR_TYPE_WRCOMB) && !have_wrcomb () )
|
||||
{
|
||||
printk ("mtrr: your processor doesn't support write-combining\n");
|
||||
return -ENOSYS;
|
||||
}
|
||||
increment = increment ? 1 : 0;
|
||||
max = get_num_var_ranges ();
|
||||
/* Search for existing MTRR */
|
||||
|
||||
for (i = 0; i < max; ++i)
|
||||
{
|
||||
get_mtrr (i, &lbase, &lsize, <ype);
|
||||
if (base >= lbase + lsize) continue;
|
||||
if ( (base < lbase) && (base + size <= lbase) ) continue;
|
||||
/* At this point we know there is some kind of overlap/enclosure */
|
||||
if ( (base < lbase) || (base + size > lbase + lsize) )
|
||||
{
|
||||
printk ("mtrr: 0x%lx,0x%lx overlaps existing 0x%lx,0x%lx\n",
|
||||
base, size, lbase, lsize);
|
||||
return -EINVAL;
|
||||
}
|
||||
/* New region is enclosed by an existing region */
|
||||
if (ltype != type)
|
||||
{
|
||||
if (type == MTRR_TYPE_UNCACHABLE) continue;
|
||||
printk ( "mtrr: type mismatch for %lx,%lx old: %s new: %s\n",
|
||||
base, size, attrib_to_str (ltype), attrib_to_str (type) );
|
||||
return -EINVAL;
|
||||
}
|
||||
if (increment) ++usage_table[i];
|
||||
return i;
|
||||
}
|
||||
/* Search for an empty MTRR */
|
||||
for (i = 0; i < max; ++i)
|
||||
{
|
||||
get_mtrr (i, &lbase, &lsize, <ype);
|
||||
if (lsize > 0) continue;
|
||||
set_mtrr (i, base, size, type);
|
||||
usage_table[i] = 1;
|
||||
return i;
|
||||
}
|
||||
printk ("mtrr: no more MTRRs available\n");
|
||||
return -ENOSPC;
|
||||
} /* End Function mtrr_add */
|
||||
|
||||
int mtrr_del (int reg, unsigned long base, unsigned long size)
|
||||
/* [SUMMARY] Delete MTRR/decrement usage count.
|
||||
<reg> The register. If this is less than 0 then <<base>> and <<size>> must
|
||||
be supplied.
|
||||
<base> The base address of the region. This is ignored if <<reg>> is >= 0.
|
||||
<size> The size of the region. This is ignored if <<reg>> is >= 0.
|
||||
[RETURNS] The register on success, else a negative number indicating
|
||||
the error code.
|
||||
[NOTE] This routine uses a spinlock.
|
||||
*/
|
||||
{
|
||||
int i, max;
|
||||
mtrr_type ltype;
|
||||
unsigned long lbase, lsize;
|
||||
|
||||
if (!usage_table) init_table();
|
||||
max = get_num_var_ranges ();
|
||||
if (reg < 0)
|
||||
{
|
||||
/* Search for existing MTRR */
|
||||
for (i = 0; i < max; ++i)
|
||||
{
|
||||
get_mtrr (i, &lbase, &lsize, <ype);
|
||||
if ( (lbase == base) && (lsize == size) )
|
||||
{
|
||||
reg = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (reg < 0)
|
||||
{
|
||||
printk ("mtrr: no MTRR for %lx,%lx found\n", base, size);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
if (reg >= max)
|
||||
{
|
||||
printk ("mtrr: register: %d too big\n", reg);
|
||||
return -EINVAL;
|
||||
}
|
||||
get_mtrr (reg, &lbase, &lsize, <ype);
|
||||
if (lsize < 1)
|
||||
{
|
||||
printk ("mtrr: MTRR %d not used\n", reg);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (usage_table[reg] < 1)
|
||||
{
|
||||
printk ("mtrr: reg: %d has count=0\n", reg);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (--usage_table[reg] < 1) set_mtrr (reg, 0, 0, 0);
|
||||
return reg;
|
||||
} /* End Function mtrr_del */
|
||||
|
||||
#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 1, 0) */
|
BIN
glide/Glide_SDK-2.2-16.i386.rpm.html
Normal file
BIN
glide/Glide_SDK-2.2-16.i386.rpm.html
Normal file
Binary file not shown.
BIN
glide/Glide_V3-2.60-16.i386.rpm.html
Normal file
BIN
glide/Glide_V3-2.60-16.i386.rpm.html
Normal file
Binary file not shown.
BIN
glide/XFree86_3DFX-SVGA-3.3.3-5.i386.rpm.html
Normal file
BIN
glide/XFree86_3DFX-SVGA-3.3.3-5.i386.rpm.html
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user