#include <errno.h>
#include <signal.h>
#include "Xarch.h"
#include "compiler.h"
#include "xf86.h"
#include "xf86Priv.h"
#define XF86_OS_PRIVS
#include "xf86_OSproc.h"
#include "Pci.h"
#define PCI_MFDEV_SUPPORT 1
#define PCI_BRIDGE_SUPPORT 1
#ifdef PC98
#define outb(port,data) _outb(port,data)
#define outl(port,data) _outl(port,data)
#define inb(port) _inb(port)
#define inl(port) _inl(port)
#endif
static int pciInitialized = 0;
CARD32 pciDevid;
CARD32 pciDevidMask;
int pciBusNum;
int pciDevNum;
int pciFuncNum;
PCITAG pciDeviceTag;
pciBusInfo_t *pciBusInfo[MAX_PCI_BUSES] = { NULL, };
int pciNumBuses = 0;
int pciMaxBusNum = MAX_PCI_BUSES;
static Bool inProbe = FALSE;
static pciConfigPtr pci_devp[MAX_PCI_DEVICES + 1] = {NULL, };
PCITAG (*pciFindFirstFP)(void) = pciGenFindFirst;
PCITAG (*pciFindNextFP)(void) = pciGenFindNext;
void
pciInit()
{
if (pciInitialized)
return;
pciInitialized = 1;
#if defined(DEBUGPCI)
if (DEBUGPCI >= xf86Verbose)
xf86Verbose = DEBUGPCI;
#endif
ARCH_PCI_INIT();
#if defined(ARCH_PCI_OS_INIT)
if (pciNumBuses <= 0)
ARCH_PCI_OS_INIT();
#endif
}
PCITAG
pciFindFirst(CARD32 id, CARD32 mask)
{
#ifdef DEBUGPCI
ErrorF("pciFindFirst(0x%lx, 0x%lx), pciInit = %d\n", id, mask, pciInitialized);
#endif
pciInit();
pciDevid = id & mask;
pciDevidMask = mask;
return((*pciFindFirstFP)());
}
PCITAG
pciFindNext(void)
{
#ifdef DEBUGPCI
ErrorF("pciFindNext(), pciInit = %d\n", pciInitialized);
#endif
pciInit();
return((*pciFindNextFP)());
}
CARD32
pciReadLong(PCITAG tag, int offset)
{
int bus = PCI_BUS_FROM_TAG(tag);
#ifdef DEBUGPCI
ErrorF("pciReadLong(0x%lx, %d)\n", tag, offset);
#endif
pciInit();
if ((bus >= 0) && ((bus < pciNumBuses) || inProbe) && pciBusInfo[bus] &&
pciBusInfo[bus]->funcs->pciReadLong) {
CARD32 rv = (*pciBusInfo[bus]->funcs->pciReadLong)(tag, offset);
PCITRACE(1, ("pciReadLong: tag=0x%x [b=%d,d=%d,f=%d] returns 0x%08x\n",
tag, bus, PCI_DEV_FROM_TAG(tag), PCI_FUNC_FROM_TAG(tag), rv));
return(rv);
}
return(PCI_NOT_FOUND);
}
CARD16
pciReadWord(PCITAG tag, int offset)
{
CARD32 tmp;
int shift = (offset & 3) * 8;
int aligned_offset = offset & ~3;
if (shift != 0 && shift != 16)
FatalError("pciReadWord: Alignment error: Cannot read 16 bits "
"at offset %d\n", offset);
tmp = pciReadLong(tag, aligned_offset);
return((CARD16)((tmp >> shift) & 0xffff));
}
CARD8
pciReadByte(PCITAG tag, int offset)
{
CARD32 tmp;
int shift = (offset & 3) * 8;
int aligned_offset = offset & ~3;
tmp = pciReadLong(tag, aligned_offset);
return((CARD8)((tmp >> shift) & 0xff));
}
void
pciWriteLong(PCITAG tag, int offset, CARD32 val)
{
int bus = PCI_BUS_FROM_TAG(tag);
pciInit();
if ((bus >= 0) && (bus < pciNumBuses) && pciBusInfo[bus] &&
pciBusInfo[bus]->funcs->pciWriteLong)
(*pciBusInfo[bus]->funcs->pciWriteLong)(tag, offset, val);
}
void
pciWriteWord(PCITAG tag, int offset, CARD16 val)
{
CARD32 tmp;
int aligned_offset = offset & ~3;
int shift = (offset & 3) * 8;
if (shift != 0 && shift != 16)
FatalError("pciWriteWord: Alignment Error: Cannot read 16 bits "
"from offset %d\n", offset);
tmp = pciReadLong(tag, aligned_offset);
tmp &= ~(0xffffL << shift);
tmp |= (((CARD32)val) << shift);
pciWriteLong(tag, aligned_offset, tmp);
}
void
pciWriteByte(PCITAG tag, int offset, CARD8 val)
{
CARD32 tmp;
int aligned_offset = offset & ~3;
int shift = (offset & 3) *8 ;
tmp = pciReadLong(tag, aligned_offset);
tmp &= ~(0xffL << shift);
tmp |= (((CARD32)val) << shift);
pciWriteLong(tag, aligned_offset, tmp);
}
void
pciSetBitsLong(PCITAG tag, int offset, CARD32 mask, CARD32 val)
{
int bus = PCI_BUS_FROM_TAG(tag);
#ifdef DEBUGPCI
ErrorF("pciReadLong(0x%lx, %d)\n", tag, offset);
#endif
pciInit();
if ((bus >= 0) && (bus < pciNumBuses) && pciBusInfo[bus] &&
pciBusInfo[bus]->funcs->pciSetBitsLong) {
(*pciBusInfo[bus]->funcs->pciSetBitsLong)(tag, offset, mask, val);
}
}
void
pciSetBitsByte(PCITAG tag, int offset, CARD8 mask, CARD8 val)
{
CARD32 tmp_mask, tmp_val;
int aligned_offset = offset & ~3;
int shift = (offset & 3) *8 ;
tmp_mask = mask << shift;
tmp_val = val << shift;
pciSetBitsLong(tag, aligned_offset, tmp_mask, tmp_val);
}
ADDRESS
pciBusAddrToHostAddr(PCITAG tag, PciAddrType type, ADDRESS addr)
{
int bus = PCI_BUS_FROM_TAG(tag);
pciInit();
if ((bus >= 0) && (bus < pciNumBuses) && pciBusInfo[bus] &&
pciBusInfo[bus]->funcs->pciAddrBusToHost)
return (*pciBusInfo[bus]->funcs->pciAddrBusToHost)(tag, type, addr);
else
return(addr);
}
ADDRESS
pciHostAddrToBusAddr(PCITAG tag, PciAddrType type, ADDRESS addr)
{
int bus = PCI_BUS_FROM_TAG(tag);
pciInit();
if ((bus >= 0) && (bus < pciNumBuses) && pciBusInfo[bus] &&
pciBusInfo[bus]->funcs->pciAddrHostToBus)
return (*pciBusInfo[bus]->funcs->pciAddrHostToBus)(tag, type, addr);
else
return(addr);
}
int
pciGetBaseSize(PCITAG tag, int index, Bool destructive, Bool *min)
{
int offset;
CARD32 addr1;
CARD32 addr2;
CARD32 mask1;
CARD32 mask2;
int bits = 0;
if (index < 0 || index > 6)
return 0;
pciInit();
if (xf86GetPciSizeFromOS(tag, index, &bits)) {
if (min)
*min = TRUE;
return bits;
}
if (min)
*min = destructive;
if (index == 6)
offset = PCI_MAP_ROM_REG;
else
offset = PCI_MAP_REG_START + (index << 2);
addr1 = pciReadLong(tag, offset);
if (index > 0 && index < 6) {
addr2 = pciReadLong(tag, offset - 4);
if (PCI_MAP_IS_MEM(addr2) && PCI_MAP_IS64BITMEM(addr2))
return 0;
}
if (destructive) {
pciWriteLong(tag, offset, 0xffffffff);
mask1 = pciReadLong(tag, offset);
pciWriteLong(tag, offset, addr1);
} else {
mask1 = addr1;
}
if (index < 5 && PCI_MAP_IS_MEM(mask1) && PCI_MAP_IS64BITMEM(mask1)) {
if (PCIGETMEMORY(mask1) == 0) {
addr2 = pciReadLong(tag, offset + 4);
if (destructive) {
pciWriteLong(tag, offset + 4, 0xffffffff);
mask2 = pciReadLong(tag, offset + 4);
pciWriteLong(tag, offset + 4, addr2);
} else {
mask2 = addr2;
}
if (mask2 == 0)
return 0;
bits = 32;
while ((mask2 & 1) == 0) {
bits++;
mask2 >>= 1;
}
if (bits > 32)
return bits;
}
}
if (index < 6)
if (PCI_MAP_IS_MEM(mask1))
mask1 = PCIGETMEMORY(mask1);
else
mask1 = PCIGETIO(mask1);
else
mask1 = PCIGETROM(mask1);
if (mask1 == 0)
return 0;
bits = 0;
while ((mask1 & 1) == 0) {
bits++;
mask1 >>= 1;
}
if ((index < 6) && PCI_MAP_IS_IO(addr1) && bits > 8)
bits = 8;
if (index == 6 && bits > 24)
bits = 24;
return bits;
}
PCITAG
pciTag(int busnum, int devnum, int funcnum)
{
return(PCI_MAKE_TAG(busnum,devnum,funcnum));
}
#if defined(PCI_MFDEV_SUPPORT) || defined(PowerMAX_OS)
Bool
pciMfDev(int busnum, int devnum)
{
PCITAG tag0, tag1;
unsigned long id0, id1, val;
tag0 = PCI_MAKE_TAG(busnum, devnum, 0);
id0 = pciReadLong(tag0, PCI_ID_REG);
if ((CARD16)(id0 + 1) <= (CARD16)1UL)
return FALSE;
val = pciReadLong(tag0, PCI_HEADER_MISC) & 0x00ff0000;
if ((val != 0x00ff0000) && (val & PCI_HEADER_MULTIFUNCTION))
return TRUE;
tag1 = PCI_MAKE_TAG(busnum, devnum, 1);
id1 = pciReadLong(tag1, PCI_ID_REG);
if ((CARD16)(id1 + 1) <= (CARD16)1UL)
return FALSE;
if ((id0 ^ id1) & 0x0000ffff)
return FALSE;
if ((id0 != id1) ||
(pciReadLong(tag0, PCI_MAP_REG_START) !=
pciReadLong(tag1, PCI_MAP_REG_START)))
return TRUE;
return FALSE;
}
#endif
PCITAG
pciGenFindNext(void)
{
CARD32 devid, tmp;
int sec_bus, pri_bus;
static int previousBus = 0;
Bool speculativeProbe = FALSE;
unsigned char base_class, sub_class;
#ifdef DEBUGPCI
ErrorF("pciGenFindNext\n");
#endif
for (;;) {
#ifdef DEBUGPCI
ErrorF("pciGenFindNext: pciBusNum %d\n", pciBusNum);
#endif
if (pciBusNum == -1) {
if (pciNumBuses <= 0)
return(PCI_NOT_FOUND);
for (pciBusNum = 0; !pciBusInfo[pciBusNum]; ++pciBusNum);
pciFuncNum = 0;
pciDevNum = 0;
previousBus = pciBusNum;
} else {
#ifdef PCI_MFDEV_SUPPORT
#ifdef DEBUGPCI
ErrorF("pciGenFindNext: pciFuncNum %d\n", pciFuncNum);
#endif
if (pciFuncNum == 0) {
if (!speculativeProbe && pciMfDev(pciBusNum, pciDevNum))
pciFuncNum = 1;
else
pciDevNum ++;
} else if (++pciFuncNum >= 8) {
pciFuncNum = 0;
pciDevNum ++;
}
#else
pciDevNum ++;
#endif
if (pciDevNum >= 32 ||
!pciBusInfo[pciBusNum] ||
pciDevNum >= pciBusInfo[pciBusNum]->numDevices) {
#ifdef DEBUGPCI
ErrorF("pciGenFindNext: next bus\n");
#endif
if (speculativeProbe) {
NextSpeculativeBus:
xfree(pciBusInfo[pciBusNum]);
pciBusInfo[pciBusNum] = NULL;
speculativeProbe = FALSE;
}
if (++pciBusNum >= pciMaxBusNum) {
#ifdef DEBUGPCI
ErrorF("pciGenFindNext: out of buses\n");
#endif
return(PCI_NOT_FOUND);
}
pciDevNum = 0;
}
}
#ifdef DEBUGPCI
ErrorF("pciGenFindNext: pciBusInfo[%d] = 0x%lx\n", pciBusNum, pciBusInfo[pciBusNum]);
#endif
if (!pciBusInfo[pciBusNum]) {
pciBusInfo[pciBusNum] = xnfalloc(sizeof(pciBusInfo_t));
*pciBusInfo[pciBusNum] = *pciBusInfo[previousBus];
speculativeProbe = TRUE;
}
#ifdef DEBUGPCI
ErrorF("pciGenFindNext: [%d, %d, %d]\n", pciBusNum, pciDevNum, pciFuncNum);
#endif
pciDeviceTag = PCI_MAKE_TAG(pciBusNum, pciDevNum, pciFuncNum);
inProbe = TRUE;
devid = pciReadLong(pciDeviceTag, PCI_ID_REG);
inProbe = FALSE;
#ifdef DEBUGPCI
ErrorF("pciGenFindNext: pciDeviceTag = 0x%lx, devid = 0x%lx\n", pciDeviceTag, devid);
#endif
if ((CARD16)(devid + 1U) <= (CARD16)1UL)
continue;
if (speculativeProbe && (pciDevNum == 0) && (pciFuncNum == 0) &&
(PCI_BUS_NO_DOMAIN(pciBusNum) > 0)) {
for (;;) {
if (++pciDevNum >= pciBusInfo[pciBusNum]->numDevices)
goto NextSpeculativeBus;
if (devid !=
pciReadLong(PCI_MAKE_TAG(pciBusNum, pciDevNum, 0),
PCI_ID_REG))
break;
}
pciDevNum = 0;
}
if (pciNumBuses <= pciBusNum)
pciNumBuses = pciBusNum + 1;
speculativeProbe = FALSE;
previousBus = pciBusNum;
#ifdef PCI_BRIDGE_SUPPORT
tmp = pciReadLong(pciDeviceTag, PCI_CLASS_REG);
base_class = PCI_CLASS_EXTRACT(tmp);
sub_class = PCI_SUBCLASS_EXTRACT(tmp);
if ((base_class == PCI_CLASS_BRIDGE) &&
((sub_class == PCI_SUBCLASS_BRIDGE_PCI) ||
(sub_class == PCI_SUBCLASS_BRIDGE_CARDBUS))) {
tmp = pciReadLong(pciDeviceTag, PCI_PCI_BRIDGE_BUS_REG);
sec_bus = PCI_SECONDARY_BUS_EXTRACT(tmp, pciDeviceTag);
pri_bus = PCI_PRIMARY_BUS_EXTRACT(tmp, pciDeviceTag);
#ifdef DEBUGPCI
ErrorF("pciGenFindNext: pri_bus %d sec_bus %d\n",
pri_bus, sec_bus);
#endif
if (pciBusNum != pri_bus) {
if ((PCI_BUS_NO_DOMAIN(pri_bus) != 0) ||
(sub_class != PCI_SUBCLASS_BRIDGE_CARDBUS))
xf86Msg(X_WARNING,
"pciGenFindNext: primary bus mismatch on PCI"
" bridge 0x%08lx (0x%02x, 0x%02x)\n",
pciDeviceTag, pciBusNum, pri_bus);
pri_bus = pciBusNum;
}
if ((pri_bus < sec_bus) && (sec_bus < pciMaxBusNum) &&
pciBusInfo[pri_bus]) {
if (!pciBusInfo[sec_bus]) {
pciBusInfo[sec_bus] = xnfalloc(sizeof(pciBusInfo_t));
*pciBusInfo[sec_bus] = *pciBusInfo[pri_bus];
}
pciBusInfo[sec_bus]->primary_bus = pri_bus;
pciBusInfo[sec_bus]->secondary = TRUE;
pciBusInfo[sec_bus]->numDevices = 32;
if (pciNumBuses <= sec_bus)
pciNumBuses = sec_bus + 1;
}
}
#endif
#ifdef DEBUGPCI
ErrorF("pciGenFindNext: pciDevidMask = 0x%lx, pciDevid = 0x%lx\n", pciDevidMask, pciDevid);
#endif
if ((devid & pciDevidMask) == pciDevid)
return(pciDeviceTag);
}
}
PCITAG
pciGenFindFirst(void)
{
pciBusNum = -1;
return pciGenFindNext();
}
#if defined (__powerpc__)
static int buserr_detected;
static
void buserr(int sig)
{
buserr_detected = 1;
}
#endif
CARD32
pciCfgMech1Read(PCITAG tag, int offset)
{
unsigned long rv = 0xffffffff;
#ifdef DEBUGPCI
ErrorF("pciCfgMech1Read(tag=%08x,offset=%08x)\n", tag, offset);
#endif
#if defined(__powerpc__)
signal(SIGBUS, buserr);
buserr_detected = 0;
#endif
outl(0xCF8, PCI_EN | tag | (offset & 0xfc));
rv = inl(0xCFC);
#if defined(__powerpc__)
signal(SIGBUS, SIG_DFL);
if (buserr_detected)
{
#ifdef DEBUGPCI
ErrorF("pciCfgMech1Read() BUS ERROR\n");
#endif
return(0xffffffff);
}
else
#endif
return(rv);
}
void
pciCfgMech1Write(PCITAG tag, int offset, CARD32 val)
{
#ifdef DEBUGPCI
ErrorF("pciCfgMech1Write(tag=%08x,offset=%08x,val=%08x)\n",
tag, offset,val);
#endif
#if defined(__powerpc__)
signal(SIGBUS, SIG_IGN);
#endif
outl(0xCF8, PCI_EN | tag | (offset & 0xfc));
#if defined(Lynx) && defined(__powerpc__)
outb(0x80, 0x00);
#endif
outl(0xCFC, val);
#if defined(__powerpc__)
signal(SIGBUS, SIG_DFL);
#endif
}
void
pciCfgMech1SetBits(PCITAG tag, int offset, CARD32 mask, CARD32 val)
{
unsigned long rv = 0xffffffff;
#if defined(__powerpc__)
signal(SIGBUS, buserr);
#endif
outl(0xCF8, PCI_EN | tag | (offset & 0xfc));
rv = inl(0xCFC);
rv = (rv & ~mask) | val;
outl(0xCFC, rv);
#if defined(__powerpc__)
signal(SIGBUS, SIG_DFL);
#endif
}
CARD32
pciByteSwap(CARD32 u)
{
#if X_BYTE_ORDER == X_BIG_ENDIAN
# if defined(__powerpc__) && defined(PowerMAX_OS)
CARD32 tmp;
__inst_stwbrx(u, &tmp, 0);
return(tmp);
# else
return lswapl(u);
# endif
#else
return(u);
#endif
}
ADDRESS
pciAddrNOOP(PCITAG tag, PciAddrType type, ADDRESS addr)
{
return(addr);
}
pciConfigPtr *
xf86scanpci(int flags)
{
pciConfigPtr devp;
pciBusInfo_t *busp;
int idx = 0, i;
PCITAG tag;
if (pci_devp[0])
return pci_devp;
pciInit();
#ifdef XF86SCANPCI_WRAPPER
XF86SCANPCI_WRAPPER(SCANPCI_INIT);
#endif
tag = pciFindFirst(0,0);
if (tag == PCI_NOT_FOUND) {
#ifdef XF86SCANPCI_WRAPPER
XF86SCANPCI_WRAPPER(SCANPCI_TERM);
#endif
return NULL;
}
#ifdef DEBUGPCI
ErrorF("xf86scanpci: tag = 0x%lx\n", tag);
#endif
#ifndef OLD_FORMAT
xf86MsgVerb(X_INFO, 2, "PCI: PCI scan (all values are in hex)\n");
#endif
while (idx < MAX_PCI_DEVICES && tag != PCI_NOT_FOUND) {
devp = xcalloc(1, sizeof(pciDevice));
if (!devp) {
xf86Msg(X_ERROR,
"xf86scanpci: Out of memory after %d devices!!\n", idx);
return (pciConfigPtr *)NULL;
}
devp->tag = tag;
devp->busnum = PCI_BUS_FROM_TAG(tag);
devp->devnum = PCI_DEV_FROM_TAG(tag);
devp->funcnum = PCI_FUNC_FROM_TAG(tag);
for (i = 0; i < 17; i++)
devp->cfgspc.dwords[i] = pciReadLong(tag, i * sizeof(CARD32));
if (devp->pci_header_type == 0xff)
devp->pci_header_type = 0;
switch (devp->pci_header_type & 0x7f) {
case 0:
for (i = 0; i < 7; i++)
devp->basesize[i] =
pciGetBaseSize(tag, i, FALSE, &devp->minBasesize);
break;
case 1:
case 2:
if (!(devp->pci_bridge_control & PCI_PCI_BRIDGE_MASTER_ABORT_EN))
break;
pciWriteByte(tag, PCI_PCI_BRIDGE_CONTROL_REG,
devp->pci_bridge_control &
~(PCI_PCI_BRIDGE_MASTER_ABORT_EN |
PCI_PCI_BRIDGE_SECONDARY_RESET));
break;
default:
break;
}
#ifdef OLD_FORMAT
xf86MsgVerb(X_INFO, 2, "PCI: BusID 0x%.2x,0x%02x,0x%1x "
"ID 0x%04x,0x%04x Rev 0x%02x Class 0x%02x,0x%02x\n",
devp->busnum, devp->devnum, devp->funcnum,
devp->pci_vendor, devp->pci_device, devp->pci_rev_id,
devp->pci_base_class, devp->pci_sub_class);
#else
xf86MsgVerb(X_INFO, 2, "PCI: %.2x:%02x:%1x: chip %04x,%04x"
" card %04x,%04x rev %02x class %02x,%02x,%02x hdr %02x\n",
devp->busnum, devp->devnum, devp->funcnum,
devp->pci_vendor, devp->pci_device,
devp->pci_subsys_vendor, devp->pci_subsys_card,
devp->pci_rev_id, devp->pci_base_class,
devp->pci_sub_class, devp->pci_prog_if,
devp->pci_header_type);
#endif
pci_devp[idx++] = devp;
tag = pciFindNext();
#ifdef DEBUGPCI
ErrorF("xf86scanpci: tag = pciFindNext = 0x%lx\n", tag);
#endif
}
while (--idx >= 0) {
devp = pci_devp[idx];
switch (devp->pci_header_type & 0x7f) {
case 0:
if ((devp->pci_base_class != PCI_CLASS_BRIDGE) ||
(devp->pci_sub_class != PCI_SUBCLASS_BRIDGE_HOST))
break;
pciBusInfo[devp->busnum]->bridge = devp;
pciBusInfo[devp->busnum]->primary_bus = devp->busnum;
break;
case 1:
case 2:
i = PCI_SECONDARY_BUS_EXTRACT(devp->pci_pp_bus_register, devp->tag);
if (i > devp->busnum) {
if (pciBusInfo[i]) {
pciBusInfo[i]->bridge = devp;
devp->businfo = pciBusInfo[i];
}
#ifdef ARCH_PCI_PCI_BRIDGE
ARCH_PCI_PCI_BRIDGE(devp);
#endif
}
if (!(devp->pci_bridge_control & PCI_PCI_BRIDGE_MASTER_ABORT_EN))
break;
pciWriteByte(devp->tag, PCI_PCI_BRIDGE_CONTROL_REG,
devp->pci_bridge_control & ~PCI_PCI_BRIDGE_SECONDARY_RESET);
break;
default:
break;
}
}
#ifdef XF86SCANPCI_WRAPPER
XF86SCANPCI_WRAPPER(SCANPCI_TERM);
#endif
for (idx = 0; idx < pciNumBuses; idx++) {
if (!(busp = pciBusInfo[idx]) || !(devp = busp->bridge))
continue;
devp->businfo = busp;
}
#ifndef OLD_FORMAT
xf86MsgVerb(X_INFO, 2, "PCI: End of PCI scan\n");
#endif
return pci_devp;
}
CARD32
pciCheckForBrokenBase(PCITAG Tag,int basereg)
{
pciWriteLong(Tag, PCI_MAP_REG_START + (basereg << 2), 0xffffffff);
return pciReadLong(Tag, PCI_MAP_REG_START + (basereg << 2));
}
#if defined(INCLUDE_XF86_MAP_PCI_MEM)
pointer
xf86MapPciMem(int ScreenNum, int Flags, PCITAG Tag, ADDRESS Base,
unsigned long Size)
{
ADDRESS hostbase = pciBusAddrToHostAddr(Tag, PCI_MEM,Base);
pointer base;
CARD32 save = 0;
if (Flags & VIDMEM_READSIDEEFFECT) {
save = pciReadLong(Tag, PCI_CMD_STAT_REG);
pciWriteLong(Tag, PCI_CMD_STAT_REG,
save & ~PCI_CMD_MEM_ENABLE);
}
base = xf86MapDomainMemory(ScreenNum, Flags, Tag, hostbase, Size);
if (!base) {
FatalError("xf86MapPciMem: Could not mmap PCI memory "
"[base=0x%lx,hostbase=0x%lx,size=%lx] (%s)\n",
Base, hostbase, Size, strerror(errno));
}
if (Flags & VIDMEM_READSIDEEFFECT) {
xf86MapReadSideEffects(ScreenNum, Flags, base, Size);
pciWriteLong(Tag, PCI_CMD_STAT_REG, save);
}
return((pointer)base);
}
static int
handlePciBIOS(PCITAG Tag, int basereg,
int (*func)(PCITAG, CARD8*, ADDRESS, pointer),
pointer args)
{
CARD32 romsave = 0;
int i;
romBaseSource b_reg;
ADDRESS hostbase;
CARD8 tmp[64];
int ret = 0;
romsave = pciReadLong(Tag, PCI_MAP_ROM_REG);
for (i = ROM_BASE_PRESET; i <= ROM_BASE_FIND; i++) {
memType savebase = 0, newbase, romaddr;
if (i == ROM_BASE_PRESET) {
if (basereg > ROM_BASE_PRESET && basereg <= ROM_BASE_FIND)
b_reg = basereg;
else
b_reg = ++i;
} else
b_reg = i;
if (!(newbase = getValidBIOSBase(Tag, b_reg)))
continue;
romaddr = PCIGETROM(newbase);
if (b_reg >= 0 && b_reg <= 5) {
savebase = pciReadLong(Tag, PCI_MAP_REG_START+(b_reg<<2));
xf86MsgVerb(X_INFO,5,"xf86ReadPciBios: modifying membase[%i]"
" for device %i:%i:%i\n", basereg,
(int)PCI_BUS_FROM_TAG(Tag), (int)PCI_DEV_FROM_TAG(Tag),
(int)PCI_FUNC_FROM_TAG(Tag));
pciWriteLong(Tag, PCI_MAP_REG_START + (b_reg << 2),
(CARD32)~0);
}
pciWriteLong(Tag, PCI_MAP_ROM_REG, romaddr
| PCI_MAP_ROM_DECODE_ENABLE);
hostbase = pciBusAddrToHostAddr(Tag, PCI_MEM, PCIGETROM(romaddr));
if ((xf86ReadDomainMemory(Tag, hostbase, sizeof(tmp), tmp) !=
sizeof(tmp)) ||
(tmp[0] != 0x55) || (tmp[1] != 0xaa) || !tmp[2] ) {
if (savebase) pciWriteLong(Tag, PCI_MAP_REG_START + (b_reg << 2),
(CARD32) savebase);
continue;
}
ret = (*func)(Tag, tmp, hostbase, args);
if (savebase) pciWriteLong(Tag, PCI_MAP_REG_START + (b_reg << 2),
(CARD32) savebase);
pciWriteLong(Tag, PCI_MAP_ROM_REG, romsave);
return ret;
}
pciWriteLong(Tag, PCI_MAP_ROM_REG, romsave);
return 0;
}
typedef struct {
unsigned long Offset;
int Len;
unsigned char *Buf;
PciBiosType BiosType;
} readBios, *readBiosPtr;
static int
readPciBios(PCITAG Tag, CARD8* tmp, ADDRESS hostbase, pointer args)
{
unsigned int image_length = 0;
readBiosPtr rd = args;
int ret;
while ((tmp[0] == 0x55) && (tmp[1] == 0xAA)) {
unsigned short data_off = tmp[0x18] | (tmp[0x19] << 8);
unsigned char data[0x18];
unsigned char type;
if ((xf86ReadDomainMemory(Tag, hostbase + data_off, sizeof(data), data)
!= sizeof(data)) ||
(data[0] != 'P') ||
(data[1] != 'C') ||
(data[2] != 'I') ||
(data[3] != 'R'))
break;
type = data[0x14];
#ifdef PRINT_PCI
ErrorF("data segment in BIOS: 0x%x, type: 0x%x\n", data_off, type);
#endif
if (type != rd->BiosType) {
unsigned char indicator = data[0x15];
unsigned int i_length;
if (indicator & 0x80)
break;
i_length = (data[0x10] | (data[0x11] << 8)) << 9;
#ifdef PRINT_PCI
ErrorF("data image length: 0x%x, ind: 0x%x\n",
image_length, indicator);
#endif
hostbase += i_length;
if (xf86ReadDomainMemory(Tag, hostbase, sizeof(tmp), tmp)
!= sizeof(tmp))
break;
continue;
}
if (rd->BiosType == PCI_BIOS_PC)
image_length = tmp[2] << 9;
else
image_length = (data[0x10] | (data[0x11] << 8)) << 9;
#ifdef PRINT_PCI
ErrorF("BIOS length: 0x%x\n", image_length);
#endif
break;
}
ret = 0;
if (image_length) {
if (rd->Len == 0) {
rd->Len = image_length;
rd->Offset = 0;
}
if ((rd->Offset) > (image_length)) {
xf86Msg(X_WARNING,"xf86ReadPciBios: requesting data past "
"end of BIOS %li > %i\n",(rd->Offset) , (image_length));
} else {
if ((rd->Offset + rd->Len) > (image_length)) {
rd->Len = (image_length) - rd->Offset;
xf86MsgVerb(X_INFO,3,"Truncating PCI BIOS Length to %i\n",rd->Len);
}
}
ret = xf86ReadDomainMemory(Tag, hostbase + rd->Offset, rd->Len, rd->Buf);
}
return ret;
}
static int
getPciBIOSTypes(PCITAG Tag, CARD8* tmp, ADDRESS hostbase, pointer arg)
{
int n = 0;
PciBiosType *Buf = arg;
do {
unsigned short data_off = tmp[0x18] | (tmp[0x19] << 8);
unsigned char data[0x16];
unsigned int i_length;
if ((xf86ReadDomainMemory(Tag, hostbase + data_off, sizeof(data), data)
!= sizeof(data)) ||
(data[0] != 'P') ||
(data[1] != 'C') ||
(data[2] != 'I') ||
(data[3] != 'R'))
break;
if (data[0x14] >= PCI_BIOS_OTHER)
*Buf++ = PCI_BIOS_OTHER;
else
*Buf++ = data[0x14];
n++;
if (data[0x15] & 0x80)
break;
#ifdef PRINT_PCI
ErrorF("data segment in BIOS: 0x%x, type: 0x%x\n", data_off, type);
#endif
i_length = (data[0x10] | (data[0x11] << 8)) << 9;
#ifdef PRINT_PCI
ErrorF("data image length: 0x%x, ind: 0x%x\n",
image_length, indicator);
#endif
hostbase += i_length;
if (xf86ReadDomainMemory(Tag, hostbase, sizeof(tmp), tmp)
!= sizeof(tmp))
break;
continue;
} while ((tmp[0] == 0x55) && (tmp[1] == 0xAA));
return n;
}
typedef CARD32 (*ReadProcPtr)(PCITAG, int);
typedef void (*WriteProcPtr)(PCITAG, int, CARD32);
static int
HandlePciBios(PCITAG Tag, int basereg,
int (*func)(PCITAG, CARD8*, ADDRESS, pointer),
pointer ptr)
{
int n, num;
CARD32 Acc1, Acc2;
PCITAG *pTag;
int i;
n = handlePciBIOS(Tag,basereg,func,ptr);
if (n)
return n;
num = pciTestMultiDeviceCard(PCI_BUS_FROM_TAG(Tag),
PCI_DEV_FROM_TAG(Tag),
PCI_FUNC_FROM_TAG(Tag),&pTag);
if (!num) return 0;
#define PCI_ENA (PCI_CMD_MEM_ENABLE | PCI_CMD_IO_ENABLE)
Acc1 = pciReadLong(Tag, PCI_CMD_STAT_REG);
pciWriteLong(Tag, PCI_CMD_STAT_REG, (Acc1 & ~PCI_ENA));
for (i = 0; i < num; i++) {
Acc2 = pciReadLong(pTag[i], PCI_CMD_STAT_REG);
pciWriteLong(pTag[i], PCI_CMD_STAT_REG, (Acc2 | PCI_ENA));
n = handlePciBIOS(pTag[i],0,func,ptr);
pciWriteLong(pTag[i], PCI_CMD_STAT_REG, Acc2);
if (n)
break;
}
pciWriteLong(Tag, PCI_CMD_STAT_REG, Acc1);
return n;
}
int
xf86ReadPciBIOS(unsigned long Offset, PCITAG Tag, int basereg,
unsigned char *Buf, int Len)
{
return xf86ReadPciBIOSByType(Offset, Tag, basereg, Buf, Len, PCI_BIOS_PC);
}
int
xf86ReadPciBIOSByType(unsigned long Offset, PCITAG Tag, int basereg,
unsigned char *Buf, int Len, PciBiosType Type)
{
readBios rb;
rb.Offset = Offset;
rb.Len = Len;
rb.Buf = Buf;
rb.BiosType = Type;
return HandlePciBios(Tag, basereg, readPciBios, &rb);
}
int
xf86GetAvailablePciBIOSTypes(PCITAG Tag, int basereg, PciBiosType *Buf)
{
return HandlePciBios(Tag, basereg, getPciBIOSTypes, (pointer) Buf);
}
#endif
#ifdef INCLUDE_XF86_NO_DOMAIN
int
xf86GetPciDomain(PCITAG Tag)
{
return 0;
}
pointer
xf86MapDomainMemory(int ScreenNum, int Flags, PCITAG Tag,
ADDRESS Base, unsigned long Size)
{
return xf86MapVidMem(ScreenNum, Flags, Base, Size);
}
IOADDRESS
xf86MapDomainIO(int ScreenNum, int Flags, PCITAG Tag,
IOADDRESS Base, unsigned long Size)
{
return Base;
}
int
xf86ReadDomainMemory(PCITAG Tag, ADDRESS Base, int Len, unsigned char *Buf)
{
int ret, length, rlength;
ret = 0;
while ((length = Len) > 0) {
if (length > 0x010000) length = 0x010000;
rlength = xf86ReadBIOS(Base, 0, Buf, length);
if (rlength < 0) {
ret = rlength;
break;
}
ret += rlength;
if (rlength < length) break;
Base += rlength;
Buf += rlength;
Len -= rlength;
}
return ret;
}
#endif