home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
lxapi32.zip
/
PCI
/
pci-pc.c
< prev
next >
Wrap
C/C++ Source or Header
|
2002-04-26
|
19KB
|
608 lines
/* $Id: pci-pc.c,v 1.2 2002/04/26 23:09:35 smilcke Exp $ */
/*
* pci-pc.c
* Autor: Stefan Milcke
* Erstellt am: 25.10.2001
* Letzte Aenderung am: 12.01.2002
*
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/poll.h>
#include <asm/uaccess.h>
#include <asm/hardirq.h>
#include <asm/io.h>
#include <linux/pci.h>
#include <linux/list.h>
#include "pci-i386.h"
#define LINUX
#include <ldefos2.h>
#include <stacktoflat.h>
#define PCI_CONFIG_ADDRESS 0xCF8
#define PCI_CONFIG_DATA 0xCFC
#define PCI_CONFIG_ENABLE 0x80000000
unsigned int pci_probe=PCI_PROBE_BIOS | PCI_PROBE_CONF1 | PCI_PROBE_CONF2;
int pcibios_last_bus=-1;
struct pci_bus *pci_root_bus=NULL;
struct pci_ops *pci_root_ops=NULL;
int (*pci_config_read)(int seg,int bus,int dev,int fn,int reg,int len,u32 *value)=NULL;
int (*pci_config_write)(int seg,int bus,int dev,int fn,int reg,int len,u32 value)=NULL;
#define PCI_CONF1_ADDRESS(bus, dev, fn, reg) \
(0x80000000 | (bus << 16) | (dev << 11) | (fn << 8) | (reg & ~3))
//------------------------------- pci_conf1_read -------------------------------
static int pci_conf1_read(int seg,int bus,int dev,int fn,int reg,int len,u32 *value)
{
unsigned long flags;
if (!value || (bus > 255) || (dev > 31) || (fn > 7) || (reg > 255))
return -EINVAL;
outl(PCI_CONF1_ADDRESS(bus, dev, fn, reg), 0xCF8);
switch (len)
{
case 1:
*value = inb(0xCFC + (reg & 3));
break;
case 2:
*value = inw(0xCFC + (reg & 2));
break;
case 4:
*value = inl(0xCFC);
break;
}
return 0;
}
//------------------------------ pci_conf1_write -------------------------------
static int pci_conf1_write(int seg,int bus,int dev,int fn,int reg,int len,u32 value)
{
unsigned long flags;
if ((bus > 255) || (dev > 31) || (fn > 7) || (reg > 255))
return -EINVAL;
outl(PCI_CONF1_ADDRESS(bus, dev, fn, reg), 0xCF8);
switch (len)
{
case 1:
outb((u8)value, 0xCFC + (reg & 3));
break;
case 2:
outw((u16)value, 0xCFC + (reg & 2));
break;
case 4:
outl((u32)value, 0xCFC);
break;
}
return 0;
}
#undef PCI_CONF1_ADDRESS
//------------------------- pci_conf1_read_config_byte -------------------------
static int pci_conf1_read_config_byte(struct pci_dev *dev,int where,u8 *value)
{
int result;
u32 data;
if(!value)
return -EINVAL;
result=pci_conf1_read(0,dev->bus->number,PCI_SLOT(dev->devfn),
PCI_FUNC(dev->devfn), where, 1, &data);
*value = (u8)data;
return result;
}
//------------------------- pci_conf1_read_config_word -------------------------
static int pci_conf1_read_config_word(struct pci_dev *dev,int where,u16 *value)
{
int result;
u32 data;
if(!value)
return -EINVAL;
result=pci_conf1_read(0,dev->bus->number,PCI_SLOT(dev->devfn),
PCI_FUNC(dev->devfn), where, 2, &data);
*value = (u16)data;
return result;
}
//------------------------ pci_conf1_read_config_dword -------------------------
static int pci_conf1_read_config_dword(struct pci_dev *dev,int where,u32 *value)
{
if(!value)
return -EINVAL;
return pci_conf1_read(0,dev->bus->number,PCI_SLOT(dev->devfn),
PCI_FUNC(dev->devfn),where,4,value);
}
//------------------------ pci_conf1_write_config_byte -------------------------
static int pci_conf1_write_config_byte(struct pci_dev *dev,int where,u8 value)
{
return pci_conf1_write(0,dev->bus->number,PCI_SLOT(dev->devfn),
PCI_FUNC(dev->devfn),where,1,value);
}
//------------------------ pci_conf1_write_config_word -------------------------
static int pci_conf1_write_config_word(struct pci_dev *dev,int where,u16 value)
{
return pci_conf1_write(0,dev->bus->number,PCI_SLOT(dev->devfn),
PCI_FUNC(dev->devfn),where,2,value);
}
//------------------------ pci_conf1_write_config_dword ------------------------
static int pci_conf1_write_config_dword(struct pci_dev *dev,int where,u32 value)
{
return pci_conf1_write(0,dev->bus->number,PCI_SLOT(dev->devfn),
PCI_FUNC(dev->devfn), where, 4, value);
}
//------------------------- (pci_ops) pci_direct_conf1 -------------------------
static struct pci_ops pci_direct_conf1 = {
pci_conf1_read_config_byte,
pci_conf1_read_config_word,
pci_conf1_read_config_dword,
pci_conf1_write_config_byte,
pci_conf1_write_config_word,
pci_conf1_write_config_dword
};
/*
* Functions for accessing PCI configuration space with type 2 accesses
*/
#define PCI_CONF2_ADDRESS(dev, reg) (u16)(0xC000 | (dev << 8) | reg)
//------------------------------- pci_conf2_read -------------------------------
static int pci_conf2_read(int seg,int bus,int dev,int fn,int reg,int len,u32 *value)
{
unsigned long flags;
if(!value || (bus > 255) || (dev > 31) || (fn > 7) || (reg > 255))
return -EINVAL;
if(dev & 0x10)
return PCIBIOS_DEVICE_NOT_FOUND;
outb((u8)(0xF0 | (fn << 1)), 0xCF8);
outb((u8)bus, 0xCFA);
switch (len)
{
case 1:
*value = inb(PCI_CONF2_ADDRESS(dev, reg));
break;
case 2:
*value = inw(PCI_CONF2_ADDRESS(dev, reg));
break;
case 4:
*value = inl(PCI_CONF2_ADDRESS(dev, reg));
break;
}
outb (0, 0xCF8);
return 0;
}
//------------------------------ pci_conf2_write -------------------------------
static int pci_conf2_write (int seg,int bus,int dev,int fn,int reg,int len,u32 value)
{
unsigned long flags;
if((bus > 255) || (dev > 31) || (fn > 7) || (reg > 255))
return -EINVAL;
if(dev & 0x10)
return PCIBIOS_DEVICE_NOT_FOUND;
outb((u8)(0xF0 | (fn << 1)), 0xCF8);
outb((u8)bus, 0xCFA);
switch (len)
{
case 1:
outb ((u8)value, PCI_CONF2_ADDRESS(dev, reg));
break;
case 2:
outw ((u16)value, PCI_CONF2_ADDRESS(dev, reg));
break;
case 4:
outl ((u32)value, PCI_CONF2_ADDRESS(dev, reg));
break;
}
outb (0, 0xCF8);
return 0;
}
#undef PCI_CONF2_ADDRESS
//------------------------- pci_conf2_read_config_byte -------------------------
static int pci_conf2_read_config_byte(struct pci_dev *dev,int where,u8 *value)
{
int result;
u32 data;
result=pci_conf2_read(0,dev->bus->number,PCI_SLOT(dev->devfn),
PCI_FUNC(dev->devfn),where,1,&data);
*value = (u8)data;
return result;
}
//------------------------- pci_conf2_read_config_word -------------------------
static int pci_conf2_read_config_word(struct pci_dev *dev,int where, u16 *value)
{
int result;
u32 data;
result=pci_conf2_read(0, dev->bus->number, PCI_SLOT(dev->devfn),
PCI_FUNC(dev->devfn),where,2,&data);
*value = (u16)data;
return result;
}
//------------------------ pci_conf2_read_config_dword -------------------------
static int pci_conf2_read_config_dword(struct pci_dev *dev,int where,u32 *value)
{
return pci_conf2_read(0,dev->bus->number,PCI_SLOT(dev->devfn),
PCI_FUNC(dev->devfn),where,4,value);
}
//------------------------ pci_conf2_write_config_byte -------------------------
static int pci_conf2_write_config_byte(struct pci_dev *dev,int where,u8 value)
{
return pci_conf2_write(0,dev->bus->number,PCI_SLOT(dev->devfn),
PCI_FUNC(dev->devfn),where,1,value);
}
//------------------------ pci_conf2_write_config_word -------------------------
static int pci_conf2_write_config_word(struct pci_dev *dev,int where,u16 value)
{
return pci_conf2_write(0,dev->bus->number,PCI_SLOT(dev->devfn),
PCI_FUNC(dev->devfn),where,2,value);
}
//------------------------ pci_conf2_write_config_dword ------------------------
static int pci_conf2_write_config_dword(struct pci_dev *dev,int where,u32 value)
{
return pci_conf2_write(0,dev->bus->number,PCI_SLOT(dev->devfn),
PCI_FUNC(dev->devfn),where,4,value);
}
//------------------------- (pci_ops) pci_direct_conf2 -------------------------
static struct pci_ops pci_direct_conf2 = {
pci_conf2_read_config_byte,
pci_conf2_read_config_word,
pci_conf2_read_config_dword,
pci_conf2_write_config_byte,
pci_conf2_write_config_word,
pci_conf2_write_config_dword
};
/*
* Before we decide to use direct hardware access mechanisms, we try to do some
* trivial checks to ensure it at least _seems_ to be working -- we just test
* whether bus 00 contains a host bridge (this is similar to checking
* techniques used in XFree86, but ours should be more reliable since we
* attempt to make use of direct access hints provided by the PCI BIOS).
*
* This should be close to trivial, but it isn't, because there are buggy
* chipsets (yes, you guessed it, by Intel and Compaq) that have no class ID.
*/
//------------------------------ pci_sanity_check ------------------------------
static int __init pci_sanity_check(struct pci_ops *o)
{
u16 x;
struct pci_bus bus; /* Fake bus and device */
struct pci_dev dev;
if(pci_probe & PCI_NO_CHECKS)
return 1;
bus.number = 0;
dev.bus = &bus;
for(dev.devfn=0;dev.devfn<0x100;dev.devfn++)
if((!o->read_word(&dev, PCI_CLASS_DEVICE, &x) &&
(x == PCI_CLASS_BRIDGE_HOST || x == PCI_CLASS_DISPLAY_VGA)) ||
(!o->read_word(&dev, PCI_VENDOR_ID, &x) &&
(x == PCI_VENDOR_ID_INTEL || x == PCI_VENDOR_ID_COMPAQ)))
return 1;
return 0;
}
//------------------------------ pci_check_direct ------------------------------
static struct pci_ops *pci_check_direct(void)
{
unsigned int tmp;
unsigned long flags;
if(pci_probe & PCI_PROBE_CONF1)
{
outb(0x01,0xCFB);
tmp=inl(0xCF8);
outl(0x80000000,0xCF8);
if(inl(0xCF8)==0x80000000 && pci_sanity_check(&pci_direct_conf1))
{
outl(tmp,0xCF8);
return &pci_direct_conf1;
}
outl(tmp,0xCF8);
}
if(pci_probe&PCI_PROBE_CONF2)
{
outb(0x00,0xCFB);
outb(0x00,0xCF8);
outb(0x00,0xCFA);
if(inb(0xCF8)==0x00 && inb(0xCFA)==00 && pci_sanity_check(&pci_direct_conf2))
{
return &pci_direct_conf2;
}
}
return NULL;
}
//---------------------------- pcibios_fixup_ghosts ----------------------------
static void __init pcibios_fixup_ghosts(struct pci_bus *b)
{
struct list_head *ln,*mn;
struct pci_dev *d,*e;
int mirror=PCI_DEVFN(16,0);
int seen_host_bridge=0;
int i;
// DBG("PCI: Scanning for ghost devices on bus %d\n",b->number);
for(ln=b->devices.next;ln!=&b->devices;ln=ln->next)
{
d=pci_dev_b(ln);
if((d->pciclass>>8)==PCI_CLASS_BRIDGE_HOST)
seen_host_bridge++;
for(mn=ln->next;mn!=&b->devices;mn=mn->next)
{
e=pci_dev_b(mn);
if(e->devfn!=d->devfn+mirror ||
e->vendor!=d->vendor ||
e->device!=d->device ||
e->pciclass !=d->pciclass)
continue;
for(i=0;i<PCI_NUM_RESOURCES;i++)
if(e->resource[i].start!=d->resource[i].start ||
e->resource[i].end!=d->resource[i].end ||
e->resource[i].flags!=d->resource[i].flags)
continue;
break;
}
if(mn==&b->devices)
return;
}
if(!seen_host_bridge)
return;
CPK(printk("PCI: Ignoring ghost devices on bus %02x\n",b->number));
ln=&b->devices;
while(ln->next!=&b->devices)
{
d=pci_dev_b(ln->next);
if(d->devfn>=mirror)
{
list_del(&d->global_list);
list_del(&d->bus_list);
kfree(d);
}
else
ln=ln->next;
}
}
//------------------------- pcibios_fixup_peer_bridges -------------------------
static void __init pcibios_fixup_peer_bridges(void)
{
int n;
struct pci_bus bus;
struct pci_dev dev;
u16 l;
if(pcibios_last_bus<=0 || pcibios_last_bus >= 0xff)
return;
// DBG("PCI: Peer bridge fixup\n");
for(n=0;n<=pcibios_last_bus;n++)
{
if(pci_bus_exists(&pci_root_buses,n))
continue;
bus.number=n;
bus.ops=pci_root_ops;
dev.bus=&bus;
for(dev.devfn=0;dev.devfn<256;dev.devfn+=8)
if(!pci_read_config_word(&dev,PCI_VENDOR_ID,&l) &&
l!=0x000 && l!=0xffff)
{
// DBG("Found device at %02x:%02x [%04x]\n",n,dev.devfn,l);
CPK(printk("PCI: Discovered peer bus %02x\n",n));
pci_scan_bus(n,pci_root_ops,NULL);
break;
}
}
}
//------------------------------ pci_fixup_i450nx ------------------------------
static void __init pci_fixup_i450nx(struct pci_dev *d)
{
// i450NX -- Find and scan all secondary buses on all PXB's.
int pxb, reg;
u8 busno, suba, subb;
CPK(printk("PCI: Searching for i450NX host bridges on %s\n", d->slot_name));
reg = 0xd0;
for(pxb=0; pxb<2; pxb++)
{
pci_read_config_byte(d, reg++, &busno);
pci_read_config_byte(d, reg++, &suba);
pci_read_config_byte(d, reg++, &subb);
// DBG("i450NX PXB %d: %02x/%02x/%02x\n", pxb, busno, suba, subb);
if(busno)
pci_scan_bus(busno, pci_root_ops, NULL); /* Bus A */
if (suba < subb)
pci_scan_bus(suba+1, pci_root_ops, NULL); /* Bus B */
}
pcibios_last_bus = -1;
}
//------------------------------ pci_fixup_i450gx ------------------------------
static void __init pci_fixup_i450gx(struct pci_dev *d)
{
// i450GX and i450KX -- Find and scan all secondary buses.
// (called separately for each PCI bridge found)
u8 busno;
pci_read_config_byte(d, 0x4a, &busno);
CPK(printk("PCI: i440KX/GX host bridge %s: secondary bus %02x\n",
d->slot_name, busno));
pci_scan_bus(busno, pci_root_ops, NULL);
pcibios_last_bus = -1;
}
//----------------------------- pci_fixup_umc_ide ------------------------------
static void __init pci_fixup_umc_ide(struct pci_dev *d)
{
// UM8886BF IDE controller sets region type bits incorrectly,
//therefore they look like memory despite of them being I/O.
int i;
CPK(printk("PCI: Fixing base address flags for device %s\n", d->slot_name));
for(i=0; i<4; i++)
d->resource[i].flags |= PCI_BASE_ADDRESS_SPACE_IO;
}
//---------------------------- pci_fixup_ide_bases -----------------------------
static void __init pci_fixup_ide_bases(struct pci_dev *d)
{
// PCI IDE controllers use non-standard I/O port decoding, respect it.
int i;
if ((d->pciclass >> 8) != PCI_CLASS_STORAGE_IDE)
return;
// DBG("PCI: IDE base address fixup for %s\n", d->slot_name);
for(i=0; i<4; i++)
{
struct resource *r = &d->resource[i];
if ((r->start & ~0x80) == 0x374)
{
r->start |= 2;
r->end = r->start;
}
}
}
//---------------------------- pci_fixup_ide_trash -----------------------------
static void __init pci_fixup_ide_trash(struct pci_dev *d)
{
// There exist PCI IDE controllers which have utter garbage
// in first four base registers. Ignore that.
int i;
// DBG("PCI: IDE base address trash cleared for %s\n", d->slot_name);
for(i=0; i<4; i++)
d->resource[i].start = d->resource[i].end = d->resource[i].flags = 0;
}
//----------------------------- pci_fixup_latency ------------------------------
static void __init pci_fixup_latency(struct pci_dev *d)
{
// SiS 5597 and 5598 chipsets require latency timer set to
// at most 32 to avoid lockups.
// DBG("PCI: Setting max latency to 32\n");
pcibios_max_latency = 32;
}
//---------------------------- pci_fixup_piix4_acpi ----------------------------
static void __init pci_fixup_piix4_acpi(struct pci_dev *d)
{
// PIIX4 ACPI device: hardwired IRQ9
d->irq = 9;
}
/*
* Nobody seems to know what this does. Damn.
*
* But it does seem to fix some unspecified problem
* with 'movntq' copies on Athlons.
*
* VIA 8363 chipset:
* - bit 7 at offset 0x55: Debug (RW)
*/
//-------------------------- pci_fixup_via_athlon_bug --------------------------
static void __init pci_fixup_via_athlon_bug(struct pci_dev *d)
{
u8 v;
pci_read_config_byte(d, 0x55, &v);
if (v & 0x80)
{
CPK(printk("Trying to stomp on Athlon bug...\n"));
v &= 0x7f; /* clear bit 55.7 */
pci_write_config_byte(d, 0x55, v);
}
}
struct pci_fixup pcibios_fixups[] = {
{ PCI_FIXUP_HEADER, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82451NX, pci_fixup_i450nx },
{ PCI_FIXUP_HEADER, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82454GX, pci_fixup_i450gx },
#if 0
/* Until we get proper handling pray the BIOS gets it right */
{ PCI_FIXUP_HEADER, PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_HE, pci_fixup_serverworks },
{ PCI_FIXUP_HEADER, PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_LE, pci_fixup_serverworks },
{ PCI_FIXUP_HEADER, PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CMIC_HE, pci_fixup_serverworks },
#endif
#if 0
/* Our bus code shouldnt need this fixup any more. Delete once verified */
{ PCI_FIXUP_HEADER, PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_6010, pci_fixup_compaq },
#endif
{ PCI_FIXUP_HEADER, PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8886BF, pci_fixup_umc_ide },
{ PCI_FIXUP_HEADER, PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5513, pci_fixup_ide_trash },
{ PCI_FIXUP_HEADER, PCI_ANY_ID, PCI_ANY_ID, pci_fixup_ide_bases },
{ PCI_FIXUP_HEADER, PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5597, pci_fixup_latency },
{ PCI_FIXUP_HEADER, PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5598, pci_fixup_latency },
{ PCI_FIXUP_HEADER, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, pci_fixup_piix4_acpi },
{ PCI_FIXUP_HEADER, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8363_0, pci_fixup_via_athlon_bug },
{ 0 }
};
//----------------------------- pcibios_fixup_bus ------------------------------
void __init pcibios_fixup_bus(struct pci_bus *b)
{
pcibios_fixup_ghosts(b);
pci_read_bridge_bases(b);
}
//---------------------------- pcibios_config_init -----------------------------
void pcibios_config_init(void)
{
if((pci_probe & (PCI_PROBE_CONF1 | PCI_PROBE_CONF2))
&&(pci_root_ops=pci_check_direct()))
{
if(pci_root_ops=&pci_direct_conf1)
{
pci_config_read=pci_conf1_read;
pci_config_write=pci_conf1_write;
}
else
{
pci_config_read=pci_conf1_read;
pci_config_write=pci_conf1_write;
}
}
}
//-------------------------------- pcibios_init --------------------------------
void pcibios_init(void)
{
if(!pci_root_ops)
pcibios_config_init();
if(!pci_root_ops)
return;
pci_root_bus=pci_scan_bus(0,pci_root_ops,NULL);
pcibios_irq_init();
pcibios_fixup_peer_bridges();
pcibios_resource_survey();
// Incomplete (SM)
/*
if((pci_probe&PCI_BIOS_SORT) && !(pci_probe&PCI_NO_SORT))
pcibios_sort();
*/
}
//-------------------------- pcibios_assign_all_buses --------------------------
unsigned int pcibios_assign_all_busses(void)
{
return (pci_probe & PCI_ASSIGN_ALL_BUSSES) ? 1 : 0;
}
//--------------------------- pcibios_enable_device ----------------------------
int pcibios_enable_device(struct pci_dev *dev)
{
int err;
if((err=pcibios_enable_resources(dev))<0)
return err;
pcibios_enable_irq(dev);
return 0;
}