10fefb5e1b
xf86scanpci() exit early if pciInit() had been called already. Fix tested by krw@.
1418 lines
41 KiB
C
1418 lines
41 KiB
C
/*
|
|
* Pci.c - New server PCI access functions
|
|
*
|
|
* The XFree86 server PCI access functions have been reimplemented as a
|
|
* framework that allows each supported platform/OS to have their own
|
|
* platform/OS specific pci driver.
|
|
*
|
|
* All of the public PCI access functions exported to the other parts of
|
|
* the server are declared in Pci.h and defined herein. These include:
|
|
* pciInit() - Initialize PCI access functions
|
|
* pciFindFirst() - Find a PCI device by dev/vend id
|
|
* pciFindNext() - Find another PCI device by dev/vend id
|
|
* pciReadLong() - Read a 32 bit value from a device's cfg space
|
|
* pciReadWord() - Read a 16 bit value from a device's cfg space
|
|
* pciReadByte() - Read an 8 bit value from a device's cfg space
|
|
* pciWriteLong() - Write a 32 bit value to a device's cfg space
|
|
* pciWriteWord() - Write a 16 bit value to a device's cfg space
|
|
* pciWriteByte() - Write an 8 bit value to a device's cfg space
|
|
* pciSetBitsLong() - Write a 32 bit value against a mask
|
|
* pciSetBitsByte() - Write an 8 bit value against a mask
|
|
* pciTag() - Return tag for a given PCI bus, device, &
|
|
* function
|
|
* pciDomTag() - Return tag for a given PCI domain, bus,
|
|
* device & function
|
|
* pciBusAddrToHostAddr() - Convert a PCI address to a host address
|
|
* pciHostAddrToBusAddr() - Convert a host address to a PCI address
|
|
* pciGetBaseSize() - Returns the number of bits in a PCI base
|
|
* addr mapping
|
|
* xf86MapPciMem() - Like xf86MapVidMem() except function expects
|
|
* a PCI address and a PCITAG that identifies
|
|
* a PCI device
|
|
* xf86ReadPciBIOS() - Like xf86ReadBIOS() but can handle PCI/host
|
|
* address translation and BIOS decode enabling
|
|
* xf86scanpci() - Return info about all PCI devices
|
|
* xf86GetPciDomain() - Return domain number from a PCITAG
|
|
* xf86MapDomainMemory() - Like xf86MapPciMem() but can handle
|
|
* domain/host address translation
|
|
* xf86MapDomainIO() - Maps PCI I/O spaces
|
|
* xf86ReadDomainMemory() - Like xf86ReadPciBIOS() but can handle
|
|
* domain/host address translation
|
|
*
|
|
* The actual PCI backend driver is selected by the pciInit() function
|
|
* (see below) using either compile time definitions, run-time checks,
|
|
* or both.
|
|
*
|
|
* Certain generic functions are provided that make the implementation
|
|
* of certain well behaved platforms (e.g. those supporting PCI config
|
|
* mechanism 1 or some thing close to it) very easy.
|
|
*
|
|
* Less well behaved platforms/OS's can roll their own functions.
|
|
*
|
|
* To add support for another platform/OS, add a call to fooPciInit() within
|
|
* pciInit() below under the correct compile time definition or run-time
|
|
* conditional.
|
|
*
|
|
* The fooPciInit() procedure must do three things:
|
|
* 1) Initialize the pciBusTable[] for all primary PCI buses including
|
|
* the per domain PCI access functions (readLong, writeLong,
|
|
* addrBusToHost, and addrHostToBus).
|
|
*
|
|
* 2) Add entries to pciBusTable[] for configured secondary buses. This
|
|
* step may be skipped if a platform is using the generic findFirst/
|
|
* findNext functions because these procedures will automatically
|
|
* discover and add secondary buses dynamically.
|
|
*
|
|
* 3) Overide default settings for global PCI access functions if
|
|
* required. These include pciFindFirstFP, pciFindNextFP,
|
|
* Of course, if you choose not to use one of the generic
|
|
* functions, you will need to provide a platform specifc replacement.
|
|
*
|
|
* Gary Barton
|
|
* Concurrent Computer Corporation
|
|
* garyb@gate.net
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* Copyright 1998 by Concurrent Computer Corporation
|
|
*
|
|
* Permission to use, copy, modify, distribute, and sell this software
|
|
* and its documentation for any purpose is hereby granted without fee,
|
|
* provided that the above copyright notice appear in all copies and that
|
|
* both that copyright notice and this permission notice appear in
|
|
* supporting documentation, and that the name of Concurrent Computer
|
|
* Corporation not be used in advertising or publicity pertaining to
|
|
* distribution of the software without specific, written prior
|
|
* permission. Concurrent Computer Corporation makes no representations
|
|
* about the suitability of this software for any purpose. It is
|
|
* provided "as is" without express or implied warranty.
|
|
*
|
|
* CONCURRENT COMPUTER CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD
|
|
* TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
* AND FITNESS, IN NO EVENT SHALL CONCURRENT COMPUTER CORPORATION BE
|
|
* LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
|
|
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
|
|
* WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
|
* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
|
|
* SOFTWARE.
|
|
*
|
|
* Copyright 1998 by Metro Link Incorporated
|
|
*
|
|
* Permission to use, copy, modify, distribute, and sell this software
|
|
* and its documentation for any purpose is hereby granted without fee,
|
|
* provided that the above copyright notice appear in all copies and that
|
|
* both that copyright notice and this permission notice appear in
|
|
* supporting documentation, and that the name of Metro Link
|
|
* Incorporated not be used in advertising or publicity pertaining to
|
|
* distribution of the software without specific, written prior
|
|
* permission. Metro Link Incorporated makes no representations
|
|
* about the suitability of this software for any purpose. It is
|
|
* provided "as is" without express or implied warranty.
|
|
*
|
|
* METRO LINK INCORPORATED DISCLAIMS ALL WARRANTIES WITH REGARD
|
|
* TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
* AND FITNESS, IN NO EVENT SHALL METRO LINK INCORPORATED BE
|
|
* LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
|
|
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
|
|
* WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
|
* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
|
|
* SOFTWARE.
|
|
*
|
|
* This software is derived from the original XFree86 PCI code
|
|
* which includes the following copyright notices as well:
|
|
*
|
|
* Copyright 1995 by Robin Cutshaw <robin@XFree86.Org>
|
|
*
|
|
* Permission to use, copy, modify, distribute, and sell this software and its
|
|
* documentation for any purpose is hereby granted without fee, provided that
|
|
* the above copyright notice appear in all copies and that both that
|
|
* copyright notice and this permission notice appear in supporting
|
|
* documentation, and that the names of the above listed copyright holder(s)
|
|
* not be used in advertising or publicity pertaining to distribution of
|
|
* the software without specific, written prior permission. The above listed
|
|
* copyright holder(s) make(s) no representations about the suitability of this
|
|
* software for any purpose. It is provided "as is" without express or
|
|
* implied warranty.
|
|
*
|
|
* THE ABOVE LISTED COPYRIGHT HOLDER(S) DISCLAIM(S) ALL WARRANTIES WITH REGARD
|
|
* TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
* AND FITNESS, IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE
|
|
* LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
|
|
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
|
|
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
|
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*
|
|
* This code is also based heavily on the code in FreeBSD-current, which was
|
|
* written by Wolfgang Stanglmeier, and contains the following copyright:
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
*/
|
|
/*
|
|
* Copyright (c) 1999-2003 by The XFree86 Project, Inc.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
* OTHER DEALINGS IN THE SOFTWARE.
|
|
*
|
|
* Except as contained in this notice, the name of the copyright holder(s)
|
|
* and author(s) shall not be used in advertising or otherwise to promote
|
|
* the sale, use or other dealings in this Software without prior written
|
|
* authorization from the copyright holder(s) and author(s).
|
|
*/
|
|
|
|
#ifdef HAVE_XORG_CONFIG_H
|
|
#include <xorg-config.h>
|
|
#endif
|
|
|
|
#include <errno.h>
|
|
#include <signal.h>
|
|
#include <X11/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 /* Include PCI multifunction device support */
|
|
#define PCI_BRIDGE_SUPPORT 1 /* Include support for PCI-to-PCI bridges */
|
|
|
|
/*
|
|
* Global data
|
|
*/
|
|
static int pciInitialized = 0;
|
|
|
|
CARD32 pciDevid; /* Requested device/vendor ID (after mask) */
|
|
CARD32 pciDevidMask; /* Bit mask applied (AND) before comparison */
|
|
/* of real devid's with requested */
|
|
|
|
int pciBusNum; /* Bus Number of current device */
|
|
int pciDevNum; /* Device number of current device */
|
|
int pciFuncNum; /* Function number of current device */
|
|
PCITAG pciDeviceTag; /* Tag for current device */
|
|
|
|
pciBusInfo_t *pciBusInfo[MAX_PCI_BUSES] = { NULL, };
|
|
_X_EXPORT int pciNumBuses = 0; /* Actual number of PCI buses */
|
|
int pciMaxBusNum = MAX_PCI_BUSES;
|
|
static Bool inProbe = FALSE;
|
|
|
|
static pciConfigPtr *pci_devp = NULL;
|
|
|
|
static int readPciBios( PCITAG Tag, CARD8* tmp, ADDRESS hostbase,
|
|
unsigned char * buf, int len, PciBiosType BiosType );
|
|
|
|
static int (*pciOSHandleBIOS)(PCITAG Tag, int basereg, unsigned char *buf, int len);
|
|
|
|
int xf86MaxPciDevs = 0;
|
|
|
|
/*
|
|
* Platform specific PCI function pointers.
|
|
*
|
|
* NOTE: A platform/OS specific pci init procedure can override these defaults
|
|
* by setting them to the appropriate platform dependent functions.
|
|
*/
|
|
PCITAG (*pciFindFirstFP)(void) = pciGenFindFirst;
|
|
PCITAG (*pciFindNextFP)(void) = pciGenFindNext;
|
|
|
|
/*
|
|
* pciInit - choose correct platform/OS specific PCI init routine
|
|
*/
|
|
void
|
|
pciInit()
|
|
{
|
|
if (pciInitialized)
|
|
return;
|
|
|
|
pciInitialized = 1;
|
|
|
|
/* XXX */
|
|
#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
|
|
if (xf86MaxPciDevs == 0) {
|
|
xf86Msg(X_WARNING,
|
|
"OS did not count PCI devices, guessing wildly\n");
|
|
xf86MaxPciDevs = MAX_PCI_DEVICES;
|
|
}
|
|
if (pci_devp)
|
|
xfree(pci_devp);
|
|
pci_devp = xnfcalloc(xf86MaxPciDevs + 1, sizeof(pciConfigPtr));
|
|
}
|
|
|
|
void pciSetOSBIOSPtr(int (*bios_fn)(PCITAG Tag, int basereg, unsigned char * buf, int len))
|
|
{
|
|
pciOSHandleBIOS = bios_fn;
|
|
}
|
|
|
|
_X_EXPORT 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)());
|
|
}
|
|
|
|
_X_EXPORT PCITAG
|
|
pciFindNext(void)
|
|
{
|
|
#ifdef DEBUGPCI
|
|
ErrorF("pciFindNext(), pciInit = %d\n", pciInitialized);
|
|
#endif
|
|
pciInit();
|
|
|
|
return((*pciFindNextFP)());
|
|
}
|
|
|
|
_X_EXPORT 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);
|
|
}
|
|
|
|
_X_EXPORT CARD16
|
|
pciReadWord(PCITAG tag, int offset)
|
|
{
|
|
CARD32 tmp;
|
|
int shift = (offset & 3) * 8;
|
|
int aligned_offset = offset & ~3;
|
|
int bus = PCI_BUS_FROM_TAG(tag);
|
|
|
|
if (shift != 0 && shift != 16)
|
|
FatalError("pciReadWord: Alignment error: Cannot read 16 bits "
|
|
"at offset %d\n", offset);
|
|
|
|
pciInit();
|
|
|
|
if ((bus >= 0) && ((bus < pciNumBuses) || inProbe) && pciBusInfo[bus] &&
|
|
pciBusInfo[bus]->funcs->pciReadWord) {
|
|
CARD32 rv = (*pciBusInfo[bus]->funcs->pciReadWord)(tag, offset);
|
|
|
|
return(rv);
|
|
} else {
|
|
tmp = pciReadLong(tag, aligned_offset);
|
|
|
|
return((CARD16)((tmp >> shift) & 0xffff));
|
|
}
|
|
}
|
|
|
|
_X_EXPORT CARD8
|
|
pciReadByte(PCITAG tag, int offset)
|
|
{
|
|
CARD32 tmp;
|
|
int shift = (offset & 3) * 8;
|
|
int aligned_offset = offset & ~3;
|
|
int bus = PCI_BUS_FROM_TAG(tag);
|
|
|
|
pciInit();
|
|
|
|
if ((bus >= 0) && ((bus < pciNumBuses) || inProbe) && pciBusInfo[bus] &&
|
|
pciBusInfo[bus]->funcs->pciReadByte) {
|
|
CARD8 rv = (*pciBusInfo[bus]->funcs->pciReadByte)(tag, offset);
|
|
|
|
return(rv);
|
|
} else {
|
|
tmp = pciReadLong(tag, aligned_offset);
|
|
|
|
return((CARD8)((tmp >> shift) & 0xff));
|
|
}
|
|
}
|
|
|
|
_X_EXPORT 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);
|
|
}
|
|
|
|
_X_EXPORT void
|
|
pciWriteWord(PCITAG tag, int offset, CARD16 val)
|
|
{
|
|
CARD32 tmp;
|
|
int aligned_offset = offset & ~3;
|
|
int shift = (offset & 3) * 8;
|
|
int bus = PCI_BUS_FROM_TAG(tag);
|
|
|
|
if (shift != 0 && shift != 16)
|
|
FatalError("pciWriteWord: Alignment Error: Cannot read 16 bits "
|
|
"from offset %d\n", offset);
|
|
|
|
pciInit();
|
|
|
|
if ((bus >= 0) && (bus < pciNumBuses) && pciBusInfo[bus] &&
|
|
pciBusInfo[bus]->funcs->pciWriteWord) {
|
|
(*pciBusInfo[bus]->funcs->pciWriteWord)(tag, offset, val);
|
|
} else {
|
|
tmp = pciReadLong(tag, aligned_offset);
|
|
|
|
tmp &= ~(0xffffL << shift);
|
|
tmp |= (((CARD32)val) << shift);
|
|
|
|
pciWriteLong(tag, aligned_offset, tmp);
|
|
}
|
|
}
|
|
|
|
_X_EXPORT void
|
|
pciWriteByte(PCITAG tag, int offset, CARD8 val)
|
|
{
|
|
CARD32 tmp;
|
|
int aligned_offset = offset & ~3;
|
|
int shift = (offset & 3) *8 ;
|
|
int bus = PCI_BUS_FROM_TAG(tag);
|
|
|
|
pciInit();
|
|
|
|
if ((bus >= 0) && (bus < pciNumBuses) && pciBusInfo[bus] &&
|
|
pciBusInfo[bus]->funcs->pciWriteByte) {
|
|
(*pciBusInfo[bus]->funcs->pciWriteByte)(tag, offset, val);
|
|
} else {
|
|
|
|
tmp = pciReadLong(tag, aligned_offset);
|
|
|
|
tmp &= ~(0xffL << shift);
|
|
tmp |= (((CARD32)val) << shift);
|
|
|
|
pciWriteLong(tag, aligned_offset, tmp);
|
|
}
|
|
}
|
|
|
|
_X_EXPORT 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);
|
|
}
|
|
|
|
_X_EXPORT 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);
|
|
}
|
|
|
|
_X_EXPORT 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);
|
|
}
|
|
|
|
/*
|
|
* pciGetBaseSize() returns the size of a PCI base address mapping in bits.
|
|
* The index identifies the base register: 0-5 are the six standard registers,
|
|
* and 6 is the ROM base register. If destructive is TRUE, it will write
|
|
* to the base address register to get an accurate result. Otherwise it
|
|
* makes a conservative guess based on the alignment of the already allocated
|
|
* address. If the result is accurate (ie, not an over-estimate), this is
|
|
* indicated by setting *min to TRUE (when min is non-NULL). This happens
|
|
* when either the destructive flag is set, the information is supplied by
|
|
* the OS if the OS supports this.
|
|
*/
|
|
|
|
int
|
|
pciGetBaseSize(PCITAG tag, int index, Bool destructive, Bool *min)
|
|
{
|
|
int offset;
|
|
CARD32 addr1;
|
|
CARD32 addr2;
|
|
CARD32 mask1;
|
|
CARD32 mask2;
|
|
int bits = 0;
|
|
|
|
/*
|
|
* Eventually a function for this should be added to pciBusFuncs_t, but for
|
|
* now we'll just use a simple method based on the alignment of the already
|
|
* allocated address.
|
|
*/
|
|
|
|
/*
|
|
* silently ignore bogus index values. Valid values are 0-6. 0-5 are
|
|
* the 6 base address registers, and 6 is the ROM base address register.
|
|
*/
|
|
if (index < 0 || index > 6)
|
|
return 0;
|
|
|
|
pciInit();
|
|
|
|
if (xf86GetPciSizeFromOS(tag, index, &bits)) {
|
|
if (min)
|
|
*min = TRUE;
|
|
return bits;
|
|
}
|
|
|
|
if (min)
|
|
*min = destructive;
|
|
|
|
/* Get the PCI offset */
|
|
if (index == 6)
|
|
offset = PCI_MAP_ROM_REG;
|
|
else
|
|
offset = PCI_MAP_REG_START + (index << 2);
|
|
|
|
addr1 = pciReadLong(tag, offset);
|
|
/*
|
|
* Check if this is the second part of a 64 bit address.
|
|
* XXX need to check how endianness affects 64 bit addresses.
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/* Check if this is the first part of a 64 bit address. */
|
|
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;
|
|
}
|
|
/* I/O maps can be no larger than 8 bits */
|
|
|
|
if ((index < 6) && PCI_MAP_IS_IO(addr1) && bits > 8)
|
|
bits = 8;
|
|
/* ROM maps can be no larger than 24 bits */
|
|
if (index == 6 && bits > 24)
|
|
bits = 24;
|
|
return bits;
|
|
}
|
|
|
|
_X_EXPORT PCITAG
|
|
pciTag(int busnum, int devnum, int funcnum)
|
|
{
|
|
return(PCI_MAKE_TAG(busnum,devnum,funcnum));
|
|
}
|
|
|
|
_X_EXPORT PCITAG
|
|
pciDomTag(int domnum, int busnum, int devnum, int funcnum)
|
|
{
|
|
return(PCI_MAKE_TAG(PCI_MAKE_BUS(domnum,busnum),devnum,funcnum));
|
|
}
|
|
|
|
#if defined(PCI_MFDEV_SUPPORT)
|
|
|
|
Bool
|
|
pciMfDev(int busnum, int devnum)
|
|
{
|
|
PCITAG tag0, tag1;
|
|
CARD32 id0, id1, val;
|
|
|
|
/* Detect a multi-function device that complies to the PCI 2.0 spec */
|
|
|
|
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;
|
|
|
|
/*
|
|
* Now, to find non-compliant devices...
|
|
* If there is a valid ID for function 1 and the ID for func 0 and 1
|
|
* are different, or the base0 values of func 0 and 1 are differend,
|
|
* then assume there is a multi-function device.
|
|
*/
|
|
tag1 = PCI_MAKE_TAG(busnum, devnum, 1);
|
|
id1 = pciReadLong(tag1, PCI_ID_REG);
|
|
if ((CARD16)(id1 + 1) <= (CARD16)1UL)
|
|
return FALSE;
|
|
|
|
/* Vendor IDs should match */
|
|
if ((id0 ^ id1) & 0x0000ffff)
|
|
return FALSE;
|
|
|
|
if ((id0 != id1) ||
|
|
/* Note the following test is valid for header types 0, 1 and 2 */
|
|
(pciReadLong(tag0, PCI_MAP_REG_START) !=
|
|
pciReadLong(tag1, PCI_MAP_REG_START)))
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
#endif
|
|
|
|
/*
|
|
* Generic find/read/write functions
|
|
*/
|
|
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) {
|
|
/*
|
|
* Start at top of the order
|
|
*/
|
|
if (pciNumBuses <= 0)
|
|
return(PCI_NOT_FOUND);
|
|
|
|
/* Skip ahead to the first bus defined by pciInit() */
|
|
for (pciBusNum = 0; !pciBusInfo[pciBusNum]; ++pciBusNum);
|
|
pciFuncNum = 0;
|
|
pciDevNum = 0;
|
|
previousBus = pciBusNum; /* make sure previousBus exists */
|
|
} else {
|
|
#ifdef PCI_MFDEV_SUPPORT
|
|
#ifdef DEBUGPCI
|
|
ErrorF("pciGenFindNext: pciFuncNum %d\n", pciFuncNum);
|
|
#endif
|
|
/*
|
|
* Somewhere in middle of order. Determine who's
|
|
* next up
|
|
*/
|
|
if (pciFuncNum == 0) {
|
|
/*
|
|
* Is current dev a multifunction device?
|
|
*/
|
|
if (!speculativeProbe && pciMfDev(pciBusNum, pciDevNum))
|
|
/* Probe for other functions */
|
|
pciFuncNum = 1;
|
|
else
|
|
/*
|
|
* No more functions this device. Next
|
|
* device please
|
|
*/
|
|
pciDevNum ++;
|
|
} else if (++pciFuncNum >= 8) {
|
|
/* No more functions for this device. Next device please */
|
|
pciFuncNum = 0;
|
|
pciDevNum ++;
|
|
}
|
|
#else
|
|
pciDevNum ++;
|
|
#endif
|
|
if (pciDevNum >= 32 ||
|
|
!pciBusInfo[pciBusNum] ||
|
|
pciDevNum >= pciBusInfo[pciBusNum]->numDevices) {
|
|
#ifdef DEBUGPCI
|
|
ErrorF("pciGenFindNext: next bus\n");
|
|
#endif
|
|
/*
|
|
* No more devices for this bus. Next bus please
|
|
*/
|
|
if (speculativeProbe) {
|
|
NextSpeculativeBus:
|
|
xfree(pciBusInfo[pciBusNum]);
|
|
pciBusInfo[pciBusNum] = NULL;
|
|
speculativeProbe = FALSE;
|
|
}
|
|
|
|
if (++pciBusNum >= pciMaxBusNum) {
|
|
#ifdef DEBUGPCI
|
|
ErrorF("pciGenFindNext: out of buses\n");
|
|
#endif
|
|
/* No more buses. All done for now */
|
|
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;
|
|
}
|
|
|
|
/*
|
|
* At this point, pciBusNum, pciDevNum, and pciFuncNum have been
|
|
* advanced to the next device. Compute the tag, and read the
|
|
* device/vendor ID field.
|
|
*/
|
|
#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; /* Nobody home. Next device please */
|
|
|
|
/*
|
|
* Some devices mis-decode configuration cycles in such a way as to
|
|
* create phantom buses.
|
|
*/
|
|
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
|
|
/*
|
|
* Before checking for a specific devid, look for enabled
|
|
* PCI to PCI bridge devices. If one is found, create and
|
|
* initialize a bus info record (if one does not already exist).
|
|
*/
|
|
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) {
|
|
/* Some bridges do not implement the primary bus register */
|
|
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]) {
|
|
/*
|
|
* Found a secondary PCI bus
|
|
*/
|
|
if (!pciBusInfo[sec_bus]) {
|
|
pciBusInfo[sec_bus] = xnfalloc(sizeof(pciBusInfo_t));
|
|
|
|
/* Copy parents settings... */
|
|
*pciBusInfo[sec_bus] = *pciBusInfo[pri_bus];
|
|
}
|
|
|
|
/* ...but not everything same as parent */
|
|
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
|
|
|
|
/*
|
|
* Does this device match the requested devid after
|
|
* applying mask?
|
|
*/
|
|
#ifdef DEBUGPCI
|
|
ErrorF("pciGenFindNext: pciDevidMask = 0x%lx, pciDevid = 0x%lx\n", pciDevidMask, pciDevid);
|
|
#endif
|
|
if ((devid & pciDevidMask) == pciDevid)
|
|
/* Yes - Return it. Otherwise, next device */
|
|
return(pciDeviceTag); /* got a match */
|
|
|
|
} /* for */
|
|
/*NOTREACHED*/
|
|
}
|
|
|
|
PCITAG
|
|
pciGenFindFirst(void)
|
|
{
|
|
/* Reset PCI bus number to start from top */
|
|
pciBusNum = -1;
|
|
|
|
return pciGenFindNext();
|
|
}
|
|
|
|
CARD32
|
|
pciByteSwap(CARD32 u)
|
|
{
|
|
#if X_BYTE_ORDER == X_BIG_ENDIAN
|
|
|
|
return lswapl(u);
|
|
|
|
#else /* !BIG_ENDIAN */
|
|
|
|
return(u);
|
|
|
|
#endif
|
|
}
|
|
|
|
ADDRESS
|
|
pciAddrNOOP(PCITAG tag, PciAddrType type, ADDRESS addr)
|
|
{
|
|
return(addr);
|
|
}
|
|
|
|
_X_EXPORT pciConfigPtr *
|
|
xf86scanpci(int flags)
|
|
{
|
|
pciConfigPtr devp;
|
|
pciBusInfo_t *busp;
|
|
int idx = 0, i;
|
|
PCITAG tag;
|
|
static Bool done = FALSE;
|
|
|
|
/*
|
|
* if we haven't found PCI devices checking for pci_devp may
|
|
* result in an endless recursion if platform/OS specific PCI
|
|
* bus probing code calls this function from with in it.
|
|
*/
|
|
if (done || (pci_devp && pci_devp[0]))
|
|
return pci_devp;
|
|
|
|
done = TRUE;
|
|
|
|
pciInit();
|
|
|
|
#ifdef XF86SCANPCI_WRAPPER
|
|
XF86SCANPCI_WRAPPER(SCANPCI_INIT);
|
|
#endif
|
|
|
|
tag = pciFindFirst(0,0); /* 0 mask means match any valid device */
|
|
/* Check if no devices, return now */
|
|
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 < xf86MaxPciDevs && 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;
|
|
}
|
|
|
|
/* Identify pci device by bus, dev, func, and tag */
|
|
devp->tag = tag;
|
|
devp->busnum = PCI_BUS_FROM_TAG(tag);
|
|
devp->devnum = PCI_DEV_FROM_TAG(tag);
|
|
devp->funcnum = PCI_FUNC_FROM_TAG(tag);
|
|
|
|
/* Read config space for this device */
|
|
for (i = 0; i < 17; i++) /* PCI hdr plus 1st dev spec dword */
|
|
devp->cfgspc.dwords[i] = pciReadLong(tag, i * sizeof(CARD32));
|
|
|
|
/* Some broken devices don't implement this field... */
|
|
if (devp->pci_header_type == 0xff)
|
|
devp->pci_header_type = 0;
|
|
|
|
switch (devp->pci_header_type & 0x7f) {
|
|
case 0:
|
|
/* Get base address sizes for type 0 headers */
|
|
for (i = 0; i < 7; i++)
|
|
devp->basesize[i] =
|
|
pciGetBaseSize(tag, i, FALSE, &devp->minBasesize);
|
|
break;
|
|
|
|
case 1:
|
|
case 2:
|
|
/* Allow master aborts to complete normally on secondary buses */
|
|
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;
|
|
if (idx == xf86MaxPciDevs)
|
|
break;
|
|
tag = pciFindNext();
|
|
|
|
#ifdef DEBUGPCI
|
|
ErrorF("xf86scanpci: tag = pciFindNext = 0x%lx\n", tag);
|
|
#endif
|
|
}
|
|
|
|
/* Restore modified data (in reverse order), and link buses */
|
|
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;
|
|
/*
|
|
* The back link needs to be set here, and is unlikely to
|
|
* change.
|
|
*/
|
|
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
|
|
|
|
/*
|
|
* Lastly, link bridges to their secondary bus, after the architecture has
|
|
* had a chance to modify these assignments.
|
|
*/
|
|
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;
|
|
}
|
|
|
|
pciConfigPtr
|
|
xf86GetPciConfigFromTag(PCITAG Tag)
|
|
{
|
|
pciConfigPtr pDev;
|
|
int i = 0;
|
|
|
|
for (i = 0 ; (pDev = pci_devp[i]) && i <= xf86MaxPciDevs; i++) {
|
|
if (Tag == pDev->tag)
|
|
return pDev;
|
|
}
|
|
|
|
return NULL; /* Bad data */
|
|
}
|
|
|
|
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)
|
|
|
|
_X_EXPORT 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 there are possible read side-effects, disable memory while
|
|
* doing the mapping.
|
|
*/
|
|
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 read side-effects, do whatever might be needed to prevent
|
|
* unintended reads, then restore PCI_CMD_STAT_REG.
|
|
*/
|
|
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, unsigned char * buf, int len )
|
|
{
|
|
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;
|
|
|
|
switch (i) {
|
|
case ROM_BASE_PRESET:
|
|
/* Does the driver have a preference? */
|
|
if (basereg > ROM_BASE_PRESET && basereg <= ROM_BASE_FIND)
|
|
b_reg = basereg;
|
|
else
|
|
b_reg = ++i;
|
|
break;
|
|
case ROM_BASE_FIND:
|
|
/*
|
|
* If we have something that looks like a valid address
|
|
* in romsave, it's probably not going to help to try
|
|
* to guess a new address and reprogram it.
|
|
*/
|
|
if (PCIGETROM(romsave)) {
|
|
pciWriteLong(Tag, PCI_MAP_ROM_REG, PCI_MAP_ROM_ADDRESS_MASK);
|
|
if (romsave != pciReadLong(Tag, PCI_MAP_ROM_REG)) {
|
|
pciWriteLong(Tag, PCI_MAP_ROM_REG, romsave);
|
|
continue;
|
|
}
|
|
}
|
|
default:
|
|
b_reg = i;
|
|
}
|
|
|
|
if (!(newbase = getValidBIOSBase(Tag, b_reg)))
|
|
continue; /* no valid address found */
|
|
|
|
romaddr = PCIGETROM(newbase);
|
|
|
|
/* if we use a mem base save it and move it out of the way */
|
|
if (b_reg >= 0 && b_reg <= 5) {
|
|
memType emptybase;
|
|
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));
|
|
if (!(emptybase = getEmptyPciRange(Tag,b_reg))) {
|
|
xf86Msg(X_ERROR,"Cannot find empty range to map base to\n");
|
|
return 0;
|
|
}
|
|
pciWriteLong(Tag, PCI_MAP_REG_START + (b_reg << 2),
|
|
emptybase);
|
|
}
|
|
/* Set ROM base address and enable ROM address decoding */
|
|
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] ) {
|
|
/* Restore the base registers if they were changed. */
|
|
pciWriteLong(Tag, PCI_MAP_ROM_REG, romsave);
|
|
if (savebase) pciWriteLong(Tag, PCI_MAP_REG_START + (b_reg << 2),
|
|
(CARD32) savebase);
|
|
|
|
/* No BIOS found: try another address */
|
|
continue;
|
|
}
|
|
|
|
ret = readPciBios( Tag, tmp, hostbase, buf, len, PCI_BIOS_PC );
|
|
|
|
/* Restore the base register if it was changed. */
|
|
if (savebase) pciWriteLong(Tag, PCI_MAP_REG_START + (b_reg << 2),
|
|
(CARD32) savebase);
|
|
/* Restore ROM address decoding */
|
|
pciWriteLong(Tag, PCI_MAP_ROM_REG, romsave);
|
|
|
|
return ret;
|
|
}
|
|
/* Restore ROM address decoding */
|
|
pciWriteLong(Tag, PCI_MAP_ROM_REG, romsave);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
readPciBios(PCITAG Tag, CARD8* tmp, ADDRESS hostbase, unsigned char * buf,
|
|
int len, PciBiosType bios_type )
|
|
{
|
|
unsigned int image_length = 0;
|
|
int ret;
|
|
|
|
/* We found a PCI BIOS Image. Now we look for the correct type */
|
|
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 != bios_type) { /* not correct image: find next one */
|
|
const unsigned char indicator = data[0x15];
|
|
unsigned int i_length;
|
|
|
|
if (indicator & 0x80) /* last image */
|
|
break;
|
|
|
|
i_length = (data[0x10] | (data[0x11] << 8)) << 9;
|
|
|
|
#ifdef PRINT_PCI
|
|
ErrorF( "data image length: 0x%x, ind: 0x%x\n", i_length, indicator );
|
|
#endif
|
|
hostbase += i_length;
|
|
if (xf86ReadDomainMemory(Tag, hostbase, sizeof(tmp), tmp)
|
|
!= sizeof(tmp))
|
|
break;
|
|
continue;
|
|
}
|
|
/* OK, we have a PCI BIOS Image of the correct type */
|
|
|
|
if ( bios_type == 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 no length is given return the full length. Beware: Area pointed to
|
|
* by Buf must be large enough!
|
|
*/
|
|
if (len == 0) {
|
|
len = image_length;
|
|
}
|
|
else if ( len > image_length ) {
|
|
len = image_length;
|
|
xf86MsgVerb( X_INFO, 3, "Truncating PCI BIOS Length to %i\n",
|
|
len );
|
|
}
|
|
|
|
/* Read BIOS */
|
|
ret = xf86ReadDomainMemory( Tag, hostbase, len, buf );
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
HandlePciBios(PCITAG Tag, int basereg, unsigned char * buf, int len)
|
|
{
|
|
int n, num;
|
|
CARD32 Acc1, Acc2;
|
|
PCITAG *pTag;
|
|
int i;
|
|
|
|
/* fall back to the old code if the OS code fails */
|
|
if (pciOSHandleBIOS) {
|
|
n = pciOSHandleBIOS(Tag, basereg, buf, len);
|
|
if (n)
|
|
return n;
|
|
}
|
|
|
|
n = handlePciBIOS( Tag, basereg, buf, len );
|
|
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, buf, len );
|
|
|
|
pciWriteLong(pTag[i], PCI_CMD_STAT_REG, Acc2);
|
|
if (n)
|
|
break;
|
|
}
|
|
pciWriteLong(Tag, PCI_CMD_STAT_REG, Acc1);
|
|
return n;
|
|
}
|
|
|
|
_X_EXPORT int
|
|
xf86ReadPciBIOS(unsigned long Offset, PCITAG Tag, int basereg,
|
|
unsigned char *Buf, int Len)
|
|
{
|
|
return HandlePciBios(Tag, basereg, Buf, Len);
|
|
}
|
|
|
|
#endif /* INCLUDE_XF86_MAP_PCI_MEM */
|
|
|
|
#ifdef INCLUDE_XF86_NO_DOMAIN
|
|
|
|
_X_EXPORT int
|
|
xf86GetPciDomain(PCITAG Tag)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
_X_EXPORT pointer
|
|
xf86MapDomainMemory(int ScreenNum, int Flags, PCITAG Tag,
|
|
ADDRESS Base, unsigned long Size)
|
|
{
|
|
return xf86MapVidMem(ScreenNum, Flags, Base, Size);
|
|
}
|
|
|
|
_X_EXPORT IOADDRESS
|
|
xf86MapDomainIO(int ScreenNum, int Flags, PCITAG Tag,
|
|
IOADDRESS Base, unsigned long Size)
|
|
{
|
|
return Base;
|
|
}
|
|
|
|
_X_EXPORT int
|
|
xf86ReadDomainMemory(PCITAG Tag, ADDRESS Base, int Len, unsigned char *Buf)
|
|
{
|
|
int ret, length, rlength;
|
|
|
|
/* Read in 64kB chunks */
|
|
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 /* INCLUDE_XF86_NO_DOMAIN */
|