2003-11-14 17:48:57 +01:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2005-07-03 09:02:09 +02:00
|
|
|
#ifdef HAVE_XORG_CONFIG_H
|
|
|
|
#include <xorg-config.h>
|
|
|
|
#endif
|
|
|
|
|
2003-11-14 17:48:57 +01:00
|
|
|
#include <stdio.h>
|
|
|
|
#include "compiler.h"
|
|
|
|
#include "xf86.h"
|
|
|
|
#include "xf86Priv.h"
|
|
|
|
#include "xf86_OSlib.h"
|
|
|
|
#include "Pci.h"
|
2006-11-02 18:50:15 +01:00
|
|
|
#include <dirent.h>
|
2003-11-14 17:48:57 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* linux platform specific PCI access functions -- using /proc/bus/pci
|
|
|
|
* needs kernel version 2.2.x
|
|
|
|
*/
|
2005-04-22 18:49:22 +02:00
|
|
|
static ADDRESS linuxTransAddrBusToHost(PCITAG tag, PciAddrType type, ADDRESS addr);
|
2005-02-09 12:12:54 +01:00
|
|
|
#if defined(__powerpc__)
|
|
|
|
static ADDRESS linuxPpcBusAddrToHostAddr(PCITAG, PciAddrType, ADDRESS);
|
|
|
|
static ADDRESS linuxPpcHostAddrToBusAddr(PCITAG, PciAddrType, ADDRESS);
|
|
|
|
#endif
|
2003-11-14 17:48:57 +01:00
|
|
|
|
|
|
|
static pciBusFuncs_t linuxFuncs0 = {
|
2005-02-09 12:12:54 +01:00
|
|
|
#if defined(__powerpc__)
|
|
|
|
/* pciAddrHostToBus */ linuxPpcHostAddrToBusAddr,
|
2005-11-08 20:04:56 +01:00
|
|
|
/* pciAddrBusToHost */ linuxPpcBusAddrToHostAddr,
|
2005-02-09 12:12:54 +01:00
|
|
|
#else
|
2003-11-14 17:48:57 +01:00
|
|
|
/* pciAddrHostToBus */ pciAddrNOOP,
|
2006-09-10 20:13:18 +02:00
|
|
|
/* linuxTransAddrBusToHost is busted on sparc64 but the PCI rework tree
|
|
|
|
* makes it all moot, so we kludge it for now */
|
2006-09-21 00:39:39 +02:00
|
|
|
#if defined(__sparc__)
|
2006-09-10 20:13:18 +02:00
|
|
|
/* pciAddrBusToHost */ pciAddrNOOP,
|
|
|
|
#else
|
2005-11-08 20:04:56 +01:00
|
|
|
/* pciAddrBusToHost */ linuxTransAddrBusToHost,
|
2006-09-10 20:13:18 +02:00
|
|
|
#endif /* __sparc64__ */
|
2005-02-09 12:12:54 +01:00
|
|
|
#endif
|
2005-11-08 20:04:56 +01:00
|
|
|
|
|
|
|
/* pciControlBridge */ NULL,
|
|
|
|
/* pciGetBridgeBuses */ NULL,
|
|
|
|
/* pciGetBridgeResources */ NULL,
|
2003-11-14 17:48:57 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
static pciBusInfo_t linuxPci0 = {
|
|
|
|
/* configMech */ PCI_CFG_MECH_OTHER,
|
|
|
|
/* numDevices */ 32,
|
|
|
|
/* secondary */ FALSE,
|
|
|
|
/* primary_bus */ 0,
|
|
|
|
/* funcs */ &linuxFuncs0,
|
|
|
|
/* pciBusPriv */ NULL,
|
|
|
|
/* bridge */ NULL
|
|
|
|
};
|
|
|
|
|
2006-07-24 21:23:23 +02:00
|
|
|
static const struct pci_id_match match_host_bridge = {
|
|
|
|
PCI_MATCH_ANY, PCI_MATCH_ANY, PCI_MATCH_ANY, PCI_MATCH_ANY,
|
|
|
|
(PCI_CLASS_BRIDGE << 16) | (PCI_SUBCLASS_BRIDGE_HOST << 8),
|
|
|
|
0x0000ffff00, 0
|
|
|
|
};
|
|
|
|
|
2006-06-05 22:22:06 +02:00
|
|
|
|
2006-11-02 18:50:15 +01:00
|
|
|
static Bool domain_support = FALSE;
|
|
|
|
|
2003-11-14 17:48:57 +01:00
|
|
|
void
|
2006-08-02 22:29:21 +02:00
|
|
|
linuxPciInit(void)
|
2003-11-14 17:48:57 +01:00
|
|
|
{
|
|
|
|
struct stat st;
|
|
|
|
if ((xf86Info.pciFlags == PCIForceNone) ||
|
|
|
|
(-1 == stat("/proc/bus/pci", &st))) {
|
|
|
|
/* when using this as default for all linux architectures,
|
|
|
|
we'll need a fallback for 2.0 kernels here */
|
|
|
|
return;
|
|
|
|
}
|
2006-11-02 18:50:15 +01:00
|
|
|
#ifndef INCLUDE_XF86_NO_DOMAIN
|
|
|
|
domain_support = linuxDomainSupport();
|
|
|
|
#endif
|
2003-11-14 17:48:57 +01:00
|
|
|
pciNumBuses = 1;
|
|
|
|
pciBusInfo[0] = &linuxPci0;
|
|
|
|
}
|
|
|
|
|
2006-08-02 18:55:32 +02:00
|
|
|
/**
|
|
|
|
* \bug
|
2006-08-02 22:29:21 +02:00
|
|
|
* The generation of the procfs file name for the domain != 0 case may not be
|
|
|
|
* correct.
|
2006-08-02 18:55:32 +02:00
|
|
|
*/
|
2003-11-14 17:48:57 +01:00
|
|
|
static int
|
2006-08-02 18:55:32 +02:00
|
|
|
linuxPciOpenFile(struct pci_device *dev, Bool write)
|
2003-11-14 17:48:57 +01:00
|
|
|
{
|
2006-08-02 18:55:32 +02:00
|
|
|
static struct pci_device *last_dev = NULL;
|
|
|
|
static int fd = -1,is_write = 0;
|
|
|
|
char file[64];
|
|
|
|
struct stat ignored;
|
|
|
|
static int is26 = -1;
|
|
|
|
|
|
|
|
if (is26 == -1) {
|
|
|
|
is26 = (stat("/sys/bus/pci", &ignored) < 0) ? 0 : 1;
|
|
|
|
}
|
2006-06-02 00:30:52 +02:00
|
|
|
|
2006-08-02 18:55:32 +02:00
|
|
|
if (fd == -1 || (write && (!is_write)) || (last_dev != dev)) {
|
2006-12-09 02:24:15 +01:00
|
|
|
if (fd != -1) {
|
2006-08-02 18:55:32 +02:00
|
|
|
close(fd);
|
2006-12-09 02:24:15 +01:00
|
|
|
fd = -1;
|
|
|
|
}
|
2006-08-02 18:55:32 +02:00
|
|
|
|
|
|
|
if (is26) {
|
|
|
|
sprintf(file,"/sys/bus/pci/devices/%04u:%02x:%02x.%01x/config",
|
|
|
|
dev->domain, dev->bus, dev->dev, dev->func);
|
|
|
|
} else {
|
|
|
|
if (dev->domain == 0) {
|
|
|
|
sprintf(file,"/proc/bus/pci/%02x", dev->bus);
|
|
|
|
if (stat(file, &ignored) < 0) {
|
|
|
|
sprintf(file, "/proc/bus/pci/0000:%02x/%02x.%1x",
|
|
|
|
dev->bus, dev->dev, dev->func);
|
|
|
|
} else {
|
|
|
|
sprintf(file, "/proc/bus/pci/%02x/%02x.%1x",
|
|
|
|
dev->bus, dev->dev, dev->func);
|
2004-07-06 16:37:48 +02:00
|
|
|
}
|
2006-08-02 18:55:32 +02:00
|
|
|
} else {
|
|
|
|
sprintf(file,"/proc/bus/pci/%02x%02x", dev->domain, dev->bus);
|
|
|
|
if (stat(file, &ignored) < 0) {
|
|
|
|
sprintf(file, "/proc/bus/pci/%04x:%04x/%02x.%1x",
|
|
|
|
dev->domain, dev->bus, dev->dev, dev->func);
|
|
|
|
} else {
|
|
|
|
sprintf(file, "/proc/bus/pci/%02x%02x/%02x.%1x",
|
|
|
|
dev->domain, dev->bus, dev->dev, dev->func);
|
2005-01-28 17:13:00 +01:00
|
|
|
}
|
2006-08-02 18:55:32 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (write) {
|
|
|
|
fd = open(file,O_RDWR);
|
|
|
|
if (fd != -1) is_write = TRUE;
|
|
|
|
} else {
|
|
|
|
switch (is_write) {
|
|
|
|
case TRUE:
|
|
|
|
fd = open(file,O_RDWR);
|
|
|
|
if (fd > -1)
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fd = open(file,O_RDONLY);
|
|
|
|
is_write = FALSE;
|
|
|
|
}
|
2003-11-14 17:48:57 +01:00
|
|
|
}
|
2006-08-02 18:55:32 +02:00
|
|
|
|
|
|
|
last_dev = dev;
|
|
|
|
}
|
|
|
|
|
|
|
|
return fd;
|
2003-11-14 17:48:57 +01:00
|
|
|
}
|
|
|
|
|
2005-04-22 18:49:22 +02:00
|
|
|
/*
|
|
|
|
* This function will convert a BAR address into a host address
|
|
|
|
* suitable for passing into the mmap function of a /proc/bus
|
|
|
|
* device.
|
|
|
|
*/
|
|
|
|
ADDRESS linuxTransAddrBusToHost(PCITAG tag, PciAddrType type, ADDRESS addr)
|
|
|
|
{
|
|
|
|
ADDRESS ret = xf86GetOSOffsetFromPCI(tag, PCI_MEM|PCI_IO, addr);
|
|
|
|
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* if it is not a BAR address, it must be legacy, (or wrong)
|
|
|
|
* return it as is..
|
|
|
|
*/
|
|
|
|
return addr;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-02-09 12:12:54 +01:00
|
|
|
#if defined(__powerpc__)
|
|
|
|
|
|
|
|
#ifndef __NR_pciconfig_iobase
|
|
|
|
#define __NR_pciconfig_iobase 200
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static ADDRESS
|
|
|
|
linuxPpcBusAddrToHostAddr(PCITAG tag, PciAddrType type, ADDRESS addr)
|
|
|
|
{
|
|
|
|
if (type == PCI_MEM)
|
|
|
|
{
|
|
|
|
ADDRESS membase = syscall(__NR_pciconfig_iobase, 1,
|
|
|
|
PCI_BUS_FROM_TAG(tag), PCI_DFN_FROM_TAG(tag));
|
|
|
|
return (addr + membase);
|
|
|
|
}
|
|
|
|
else if (type == PCI_IO)
|
|
|
|
{
|
|
|
|
ADDRESS iobase = syscall(__NR_pciconfig_iobase, 2,
|
|
|
|
PCI_BUS_FROM_TAG(tag), PCI_DFN_FROM_TAG(tag));
|
|
|
|
return (addr + iobase);
|
|
|
|
}
|
|
|
|
else return addr;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ADDRESS
|
|
|
|
linuxPpcHostAddrToBusAddr(PCITAG tag, PciAddrType type, ADDRESS addr)
|
|
|
|
{
|
|
|
|
if (type == PCI_MEM)
|
|
|
|
{
|
|
|
|
ADDRESS membase = syscall(__NR_pciconfig_iobase, 1,
|
|
|
|
PCI_BUS_FROM_TAG(tag), PCI_DFN_FROM_TAG(tag));
|
|
|
|
return (addr - membase);
|
|
|
|
}
|
|
|
|
else if (type == PCI_IO)
|
|
|
|
{
|
|
|
|
ADDRESS iobase = syscall(__NR_pciconfig_iobase, 2,
|
|
|
|
PCI_BUS_FROM_TAG(tag), PCI_DFN_FROM_TAG(tag));
|
|
|
|
return (addr - iobase);
|
|
|
|
}
|
|
|
|
else return addr;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* __powerpc__ */
|
|
|
|
|
2003-11-14 17:48:57 +01:00
|
|
|
#ifndef INCLUDE_XF86_NO_DOMAIN
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Compiling the following simply requires the presence of <linux/pci.c>.
|
|
|
|
* Actually running this is another matter altogether...
|
|
|
|
*
|
|
|
|
* This scheme requires that the kernel allow mmap()'ing of a host bridge's I/O
|
|
|
|
* and memory spaces through its /proc/bus/pci/BUS/DFN entry. Which one is
|
|
|
|
* determined by a prior ioctl().
|
|
|
|
*
|
|
|
|
* For the sparc64 port, this means 2.4.12 or later. For ppc, this
|
|
|
|
* functionality is almost, but not quite there yet. Alpha and other kernel
|
|
|
|
* ports to multi-domain architectures still need to implement this.
|
|
|
|
*
|
|
|
|
* This scheme is also predicated on the use of an IOADDRESS compatible type to
|
|
|
|
* designate I/O addresses. Although IOADDRESS is defined as an unsigned
|
|
|
|
* integral type, it is actually the virtual address of, i.e. a pointer to, the
|
|
|
|
* I/O port to access. And so, the inX/outX macros in "compiler.h" need to be
|
|
|
|
* #define'd appropriately (as is done on SPARC's).
|
|
|
|
*
|
|
|
|
* Another requirement to port this scheme to another multi-domain architecture
|
|
|
|
* is to add the appropriate entries in the pciControllerSizes array below.
|
|
|
|
*
|
|
|
|
* TO DO: Address the deleterious reaction some host bridges have to master
|
|
|
|
* aborts. This is already done for secondary PCI buses, but not yet
|
|
|
|
* for accesses to primary buses (except for the SPARC port, where
|
|
|
|
* master aborts are avoided during PCI scans).
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/pci.h>
|
|
|
|
|
|
|
|
#ifndef PCIIOC_BASE /* Ioctls for /proc/bus/pci/X/Y nodes. */
|
|
|
|
#define PCIIOC_BASE ('P' << 24 | 'C' << 16 | 'I' << 8)
|
|
|
|
|
|
|
|
/* Get controller for PCI device. */
|
|
|
|
#define PCIIOC_CONTROLLER (PCIIOC_BASE | 0x00)
|
|
|
|
/* Set mmap state to I/O space. */
|
|
|
|
#define PCIIOC_MMAP_IS_IO (PCIIOC_BASE | 0x01)
|
|
|
|
/* Set mmap state to MEM space. */
|
|
|
|
#define PCIIOC_MMAP_IS_MEM (PCIIOC_BASE | 0x02)
|
|
|
|
/* Enable/disable write-combining. */
|
|
|
|
#define PCIIOC_WRITE_COMBINE (PCIIOC_BASE | 0x03)
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* This probably shouldn't be Linux-specific */
|
2006-08-02 18:55:32 +02:00
|
|
|
static struct pci_device *
|
2003-11-14 17:48:57 +01:00
|
|
|
xf86GetPciHostConfigFromTag(PCITAG Tag)
|
|
|
|
{
|
|
|
|
int bus = PCI_BUS_FROM_TAG(Tag);
|
|
|
|
pciBusInfo_t *pBusInfo;
|
|
|
|
|
|
|
|
while ((bus < pciNumBuses) && (pBusInfo = pciBusInfo[bus])) {
|
|
|
|
if (bus == pBusInfo->primary_bus)
|
|
|
|
return pBusInfo->bridge;
|
|
|
|
bus = pBusInfo->primary_bus;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL; /* Bad data */
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is ugly, but until I can extract this information from the kernel,
|
|
|
|
* it'll have to do. The default I/O space size is 64K, and 4G for memory.
|
|
|
|
* Anything else needs to go in this table. (PowerPC folk take note.)
|
|
|
|
*
|
|
|
|
* Note that Linux/SPARC userland is 32-bit, so 4G overflows to zero here.
|
|
|
|
*
|
|
|
|
* Please keep this table in ascending vendor/device order.
|
|
|
|
*/
|
2006-07-06 21:48:51 +02:00
|
|
|
static const struct pciSizes {
|
2003-11-14 17:48:57 +01:00
|
|
|
unsigned short vendor, device;
|
|
|
|
unsigned long io_size, mem_size;
|
|
|
|
} pciControllerSizes[] = {
|
|
|
|
{
|
|
|
|
PCI_VENDOR_SUN, PCI_CHIP_PSYCHO,
|
|
|
|
1U << 16, 1U << 31
|
|
|
|
},
|
|
|
|
{
|
|
|
|
PCI_VENDOR_SUN, PCI_CHIP_SCHIZO,
|
|
|
|
1U << 24, 1U << 31 /* ??? */
|
|
|
|
},
|
|
|
|
{
|
|
|
|
PCI_VENDOR_SUN, PCI_CHIP_SABRE,
|
|
|
|
1U << 24, (unsigned long)(1ULL << 32)
|
|
|
|
},
|
|
|
|
{
|
|
|
|
PCI_VENDOR_SUN, PCI_CHIP_HUMMINGBIRD,
|
|
|
|
1U << 24, (unsigned long)(1ULL << 32)
|
|
|
|
}
|
|
|
|
};
|
|
|
|
#define NUM_SIZES (sizeof(pciControllerSizes) / sizeof(pciControllerSizes[0]))
|
|
|
|
|
2006-07-06 21:48:51 +02:00
|
|
|
static const struct pciSizes *
|
2006-07-24 22:13:05 +02:00
|
|
|
linuxGetSizesStruct(const struct pci_device *dev)
|
2003-11-14 17:48:57 +01:00
|
|
|
{
|
2006-07-06 21:48:51 +02:00
|
|
|
static const struct pciSizes default_size = {
|
|
|
|
0, 0, 1U << 16, (unsigned long)(1ULL << 32)
|
|
|
|
};
|
2003-11-14 17:48:57 +01:00
|
|
|
int i;
|
|
|
|
|
2006-07-24 22:13:05 +02:00
|
|
|
/* Look up vendor/device */
|
|
|
|
for (i = 0; i < NUM_SIZES; i++) {
|
|
|
|
if ((dev->vendor_id == pciControllerSizes[i].vendor)
|
|
|
|
&& (dev->device_id == pciControllerSizes[i].device)) {
|
|
|
|
return & pciControllerSizes[i];
|
2003-11-14 17:48:57 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-07-06 21:48:51 +02:00
|
|
|
/* Default to 64KB I/O and 4GB memory. */
|
|
|
|
return & default_size;
|
2003-11-14 17:48:57 +01:00
|
|
|
}
|
|
|
|
|
2006-07-06 21:48:51 +02:00
|
|
|
static __inline__ unsigned long
|
2006-08-09 01:42:23 +02:00
|
|
|
linuxGetIOSize(const struct pci_device *dev)
|
2003-11-14 17:48:57 +01:00
|
|
|
{
|
2006-08-09 01:42:23 +02:00
|
|
|
const struct pciSizes * const sizes = linuxGetSizesStruct(dev);
|
|
|
|
return sizes->io_size;
|
2003-11-14 17:48:57 +01:00
|
|
|
}
|
|
|
|
|
2006-03-25 20:52:05 +01:00
|
|
|
_X_EXPORT int
|
2003-11-14 17:48:57 +01:00
|
|
|
xf86GetPciDomain(PCITAG Tag)
|
|
|
|
{
|
2006-08-02 18:55:32 +02:00
|
|
|
const struct pci_device *dev;
|
2003-11-14 17:48:57 +01:00
|
|
|
int fd, result;
|
|
|
|
|
2006-08-02 18:55:32 +02:00
|
|
|
dev = xf86GetPciHostConfigFromTag(Tag);
|
2003-11-14 17:48:57 +01:00
|
|
|
|
2006-08-02 18:55:32 +02:00
|
|
|
if (!dev)
|
2005-04-22 18:49:22 +02:00
|
|
|
return 1; /* Domain 0 is reserved */
|
|
|
|
|
2006-08-02 18:55:32 +02:00
|
|
|
if ((result = PCI_DOM_FROM_TAG(Tag)) != 0)
|
2006-12-09 02:24:15 +01:00
|
|
|
return result;
|
2006-08-02 18:55:32 +02:00
|
|
|
|
2006-08-09 01:42:23 +02:00
|
|
|
if ((fd = linuxPciOpenFile(dev, FALSE)) < 0)
|
2003-11-14 17:48:57 +01:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
if ((result = ioctl(fd, PCIIOC_CONTROLLER, 0)) < 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return result + 1; /* Domain 0 is reserved */
|
|
|
|
}
|
|
|
|
|
|
|
|
static pointer
|
2006-08-09 01:42:23 +02:00
|
|
|
linuxMapPci(int ScreenNum, int Flags, struct pci_device *dev,
|
2003-11-14 17:48:57 +01:00
|
|
|
ADDRESS Base, unsigned long Size, int mmap_ioctl)
|
|
|
|
{
|
2006-08-09 01:42:23 +02:00
|
|
|
/* Align to page boundary */
|
|
|
|
const ADDRESS realBase = Base & ~(getpagesize() - 1);
|
|
|
|
const ADDRESS Offset = Base - realBase;
|
|
|
|
|
2003-11-14 17:48:57 +01:00
|
|
|
do {
|
|
|
|
unsigned char *result;
|
|
|
|
int fd, mmapflags, prot;
|
|
|
|
|
|
|
|
xf86InitVidMem();
|
|
|
|
|
2006-08-02 18:55:32 +02:00
|
|
|
/* FIXME: What if dev == NULL? */
|
|
|
|
|
|
|
|
if (((fd = linuxPciOpenFile(dev, FALSE)) < 0) ||
|
2003-11-14 17:48:57 +01:00
|
|
|
(ioctl(fd, mmap_ioctl, 0) < 0))
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Note: IA-64 doesn't compile this and doesn't need to */
|
|
|
|
#ifdef __ia64__
|
|
|
|
|
|
|
|
# ifndef MAP_WRITECOMBINED
|
|
|
|
# define MAP_WRITECOMBINED 0x00010000
|
|
|
|
# endif
|
|
|
|
# ifndef MAP_NONCACHED
|
|
|
|
# define MAP_NONCACHED 0x00020000
|
|
|
|
# endif
|
|
|
|
|
|
|
|
if (Flags & VIDMEM_FRAMEBUFFER)
|
|
|
|
mmapflags = MAP_SHARED | MAP_WRITECOMBINED;
|
|
|
|
else
|
2005-04-22 18:49:22 +02:00
|
|
|
mmapflags = MAP_SHARED | MAP_NONCACHED;
|
2003-11-14 17:48:57 +01:00
|
|
|
|
|
|
|
#else /* !__ia64__ */
|
|
|
|
|
|
|
|
mmapflags = (Flags & VIDMEM_FRAMEBUFFER) / VIDMEM_FRAMEBUFFER;
|
|
|
|
|
|
|
|
if (ioctl(fd, PCIIOC_WRITE_COMBINE, mmapflags) < 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
mmapflags = MAP_SHARED;
|
|
|
|
|
|
|
|
#endif /* ?__ia64__ */
|
|
|
|
|
|
|
|
|
|
|
|
if (Flags & VIDMEM_READONLY)
|
|
|
|
prot = PROT_READ;
|
|
|
|
else
|
|
|
|
prot = PROT_READ | PROT_WRITE;
|
|
|
|
|
|
|
|
result = mmap(NULL, Size + Offset, prot, mmapflags, fd, realBase);
|
|
|
|
|
|
|
|
if (!result || ((pointer)result == MAP_FAILED))
|
2005-04-22 18:49:22 +02:00
|
|
|
return NULL;
|
2003-11-14 17:48:57 +01:00
|
|
|
|
|
|
|
xf86MakeNewMapping(ScreenNum, Flags, realBase, Size + Offset, result);
|
|
|
|
|
|
|
|
return result + Offset;
|
|
|
|
} while (0);
|
|
|
|
|
|
|
|
if (mmap_ioctl == PCIIOC_MMAP_IS_MEM)
|
|
|
|
return xf86MapVidMem(ScreenNum, Flags, Base, Size);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2005-04-22 18:49:22 +02:00
|
|
|
#define MAX_DOMAINS 257
|
|
|
|
static pointer DomainMmappedIO[MAX_DOMAINS];
|
|
|
|
|
|
|
|
static int
|
2006-08-09 01:42:23 +02:00
|
|
|
linuxOpenLegacy(struct pci_device *dev, char *name)
|
2005-04-22 18:49:22 +02:00
|
|
|
{
|
|
|
|
#define PREFIX "/sys/class/pci_bus/%04x:%02x/%s"
|
|
|
|
char *path;
|
|
|
|
int domain, bus;
|
|
|
|
pciBusInfo_t *pBusInfo;
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
path = xalloc(strlen(PREFIX) + strlen(name));
|
|
|
|
if (!path)
|
|
|
|
return -1;
|
|
|
|
|
2006-08-02 18:55:32 +02:00
|
|
|
while (dev != NULL) {
|
|
|
|
sprintf(path, PREFIX, dev->domain, dev->bus, name);
|
2005-04-22 18:49:22 +02:00
|
|
|
fd = open(path, O_RDWR);
|
|
|
|
if (fd >= 0) {
|
|
|
|
xfree(path);
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
2006-08-02 18:55:32 +02:00
|
|
|
pBusInfo = pciBusInfo[PCI_MAKE_BUS(dev->domain, dev->bus)];
|
2005-04-22 18:49:22 +02:00
|
|
|
if (!pBusInfo || (bridge == pBusInfo->bridge) ||
|
2006-08-02 18:55:32 +02:00
|
|
|
!pBusInfo->bridge) {
|
2005-04-22 18:49:22 +02:00
|
|
|
xfree(path);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2006-08-02 18:55:32 +02:00
|
|
|
dev = pBusInfo->bridge;
|
2005-04-22 18:49:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
xfree(path);
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* xf86MapDomainMemory - memory map PCI domain memory
|
|
|
|
*
|
|
|
|
* This routine maps the memory region in the domain specified by Tag and
|
|
|
|
* returns a pointer to it. The pointer is saved for future use if it's in
|
|
|
|
* the legacy ISA memory space (memory in a domain between 0 and 1MB).
|
|
|
|
*/
|
2006-03-25 20:52:05 +01:00
|
|
|
_X_EXPORT pointer
|
2003-11-14 17:48:57 +01:00
|
|
|
xf86MapDomainMemory(int ScreenNum, int Flags, PCITAG Tag,
|
|
|
|
ADDRESS Base, unsigned long Size)
|
|
|
|
{
|
2005-04-22 18:49:22 +02:00
|
|
|
int domain = xf86GetPciDomain(Tag);
|
2006-08-09 01:42:23 +02:00
|
|
|
const struct pci_device *dev = xf86GetPciHostConfigFromTag(Tag);
|
2006-11-16 17:29:06 +01:00
|
|
|
int fd = -1;
|
2006-11-03 18:54:06 +01:00
|
|
|
pointer addr;
|
2005-04-22 18:49:22 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We use /proc/bus/pci on non-legacy addresses or if the Linux sysfs
|
|
|
|
* legacy_mem interface is unavailable.
|
|
|
|
*/
|
2006-08-09 01:42:23 +02:00
|
|
|
if ((Base > 1024*1024) || ((fd = linuxOpenLegacy(dev, "legacy_mem")) < 0))
|
|
|
|
return linuxMapPci(ScreenNum, Flags, dev, Base, Size,
|
2005-04-22 18:49:22 +02:00
|
|
|
PCIIOC_MMAP_IS_MEM);
|
2006-11-16 17:29:06 +01:00
|
|
|
else
|
|
|
|
addr = mmap(NULL, Size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, Base);
|
2005-04-22 18:49:22 +02:00
|
|
|
|
2006-11-16 17:29:06 +01:00
|
|
|
if (fd >= 0)
|
|
|
|
close(fd);
|
|
|
|
if (addr == NULL || addr == MAP_FAILED) {
|
2006-11-03 18:54:06 +01:00
|
|
|
perror("mmap failure");
|
|
|
|
FatalError("xf86MapDomainMem(): mmap() failure\n");
|
2005-04-22 18:49:22 +02:00
|
|
|
}
|
2006-11-03 18:54:06 +01:00
|
|
|
return addr;
|
2005-04-22 18:49:22 +02:00
|
|
|
}
|
2003-11-14 17:48:57 +01:00
|
|
|
|
2006-07-14 06:10:48 +02:00
|
|
|
/**
|
|
|
|
* Map I/O space in this domain
|
2005-04-22 18:49:22 +02:00
|
|
|
*
|
|
|
|
* Each domain has a legacy ISA I/O space. This routine will try to
|
|
|
|
* map it using the Linux sysfs legacy_io interface. If that fails,
|
|
|
|
* it'll fall back to using /proc/bus/pci.
|
|
|
|
*
|
2006-07-14 06:10:48 +02:00
|
|
|
* If the legacy_io interface \b does exist, the file descriptor (\c fd below)
|
|
|
|
* will be saved in the \c DomainMmappedIO array in the upper bits of the
|
2005-04-22 18:49:22 +02:00
|
|
|
* pointer. Callers will do I/O with small port numbers (<64k values), so
|
2006-07-14 06:10:48 +02:00
|
|
|
* the platform I/O code can extract the port number and the \c fd, \c lseek
|
|
|
|
* to the port number in the legacy_io file, and issue the read or write.
|
2005-04-22 18:49:22 +02:00
|
|
|
*
|
|
|
|
* This has no means of returning failure, so all errors are fatal
|
|
|
|
*/
|
2006-07-24 22:33:34 +02:00
|
|
|
IOADDRESS
|
|
|
|
xf86MapLegacyIO(struct pci_device *dev)
|
2003-11-14 17:48:57 +01:00
|
|
|
{
|
2006-07-24 22:33:34 +02:00
|
|
|
const PCITAG tag = PCI_MAKE_TAG(PCI_MAKE_BUS(dev->domain, dev->bus),
|
|
|
|
dev->dev, dev->func);
|
|
|
|
const int domain = xf86GetPciDomain(tag);
|
2006-08-09 01:42:23 +02:00
|
|
|
const struct pci_device *bridge = xf86GetPciHostConfigFromTag(Tag);
|
2005-04-22 18:49:22 +02:00
|
|
|
int fd;
|
2003-11-14 17:48:57 +01:00
|
|
|
|
|
|
|
if ((domain <= 0) || (domain >= MAX_DOMAINS))
|
2006-07-24 22:33:34 +02:00
|
|
|
FatalError("xf86MapLegacyIO(): domain out of range\n");
|
2003-11-14 17:48:57 +01:00
|
|
|
|
2005-04-22 18:49:22 +02:00
|
|
|
if (DomainMmappedIO[domain])
|
2006-07-24 22:33:34 +02:00
|
|
|
return (IOADDRESS)DomainMmappedIO[domain];
|
2005-04-22 18:49:22 +02:00
|
|
|
|
2003-11-14 17:48:57 +01:00
|
|
|
/* Permanently map all of I/O space */
|
2006-08-09 01:42:23 +02:00
|
|
|
if ((fd = linuxOpenLegacy(bridge, "legacy_io")) < 0) {
|
|
|
|
DomainMmappedIO[domain] = linuxMapPci(-1, VIDMEM_MMIO, bridge,
|
2006-07-24 22:33:34 +02:00
|
|
|
0, linuxGetIOSize(tag),
|
2005-04-22 18:49:22 +02:00
|
|
|
PCIIOC_MMAP_IS_IO);
|
|
|
|
/* ia64 can't mmap legacy IO port space */
|
|
|
|
if (!DomainMmappedIO[domain])
|
2006-07-24 22:33:34 +02:00
|
|
|
return 0;
|
2005-04-22 18:49:22 +02:00
|
|
|
}
|
|
|
|
else { /* legacy_io file exists, encode fd */
|
|
|
|
DomainMmappedIO[domain] = (pointer)(fd << 24);
|
2003-11-14 17:48:57 +01:00
|
|
|
}
|
|
|
|
|
2006-07-24 22:33:34 +02:00
|
|
|
return (IOADDRESS)DomainMmappedIO[domain];
|
2003-11-14 17:48:57 +01:00
|
|
|
}
|
|
|
|
|
2006-08-09 01:42:23 +02:00
|
|
|
/**
|
|
|
|
* Read legacy VGA video BIOS associated with specified domain.
|
|
|
|
*
|
|
|
|
* Attempts to read up to 128KiB of legacy VGA video BIOS.
|
|
|
|
*
|
|
|
|
* \return
|
|
|
|
* The number of bytes read on success or -1 on failure.
|
2005-04-22 18:49:22 +02:00
|
|
|
*/
|
2006-03-25 20:52:05 +01:00
|
|
|
_X_EXPORT int
|
2006-08-09 01:42:23 +02:00
|
|
|
xf86ReadLegacyVideoBIOS(PCITAG Tag, unsigned char *Buf)
|
2003-11-14 17:48:57 +01:00
|
|
|
{
|
2006-08-09 01:42:23 +02:00
|
|
|
const ADDRESS Base = V_BIOS;
|
|
|
|
const int Len = V_BIOS_SIZE * 2;
|
|
|
|
const int pagemask = getpagesize() - 1;
|
|
|
|
const ADDRESS offset = Base & ~pagemask;
|
|
|
|
const unsigned long size = ((Base + Len + pagemask) & ~pagemask) - offset;
|
|
|
|
const struct pci_device * const dev =
|
|
|
|
pci_device_find_by_slot(PCI_DOM_FROM_TAG(Tag),
|
|
|
|
PCI_BUS_NO_DOM(PCI_BUS_FROM_TAG(Tag)),
|
|
|
|
PCI_DEV_FROM_TAG(Tag),
|
|
|
|
PCI_FUNC_FROM_TAG(Tag));
|
2003-11-14 17:48:57 +01:00
|
|
|
unsigned char *ptr, *src;
|
2006-08-09 01:42:23 +02:00
|
|
|
int len;
|
2005-04-22 18:49:22 +02:00
|
|
|
|
|
|
|
|
2006-08-09 01:42:23 +02:00
|
|
|
/* Try to use the civilized PCI interface first.
|
|
|
|
*/
|
|
|
|
if (pci_device_read_rom(dev, Buf) == 0) {
|
|
|
|
return dev->rom_size;
|
2005-04-22 18:49:22 +02:00
|
|
|
}
|
|
|
|
|
2003-11-14 17:48:57 +01:00
|
|
|
ptr = xf86MapDomainMemory(-1, VIDMEM_READONLY, Tag, offset, size);
|
|
|
|
|
|
|
|
if (!ptr)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* Using memcpy() here can hang the system */
|
|
|
|
src = ptr + (Base - offset);
|
2006-08-09 01:42:23 +02:00
|
|
|
for (len = 0; len < V_BIOS_SIZE; len++) {
|
|
|
|
Buf[len] = src[len];
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((buf[0] == 0x55) && (buf[1] == 0xAA) && (buf[2] > 0x80)) {
|
|
|
|
for ( /* empty */ ; len < (2 * V_BIOS_SIZE); len++) {
|
|
|
|
Buf[len] = src[len];
|
|
|
|
}
|
|
|
|
}
|
2003-11-14 17:48:57 +01:00
|
|
|
|
|
|
|
xf86UnMapVidMem(-1, ptr, size);
|
|
|
|
|
|
|
|
return Len;
|
|
|
|
}
|
|
|
|
|
|
|
|
resPtr
|
|
|
|
xf86BusAccWindowsFromOS(void)
|
|
|
|
{
|
2006-07-24 21:23:23 +02:00
|
|
|
struct pci_device *dev;
|
|
|
|
sturct pci_device_iterator *iter;
|
2003-11-14 17:48:57 +01:00
|
|
|
resPtr pRes = NULL;
|
|
|
|
resRange range;
|
|
|
|
|
2006-07-24 21:23:23 +02:00
|
|
|
iter = pci_id_match_iterator_create(& match_host_bridge);
|
|
|
|
while ((dev = pci_device_next(iter)) != NULL) {
|
|
|
|
const PCITAG tag = PCI_MAKE_TAG(PCI_MAKE_BUS(dev->domain, dev->bus),
|
|
|
|
dev->dev, dev->func);
|
|
|
|
const int domain = xf86GetPciDomain(tag);
|
2006-07-24 22:13:05 +02:00
|
|
|
const struct pciSizes * const sizes = linuxGetSizesStruct(dev);
|
2003-11-14 17:48:57 +01:00
|
|
|
|
2006-07-24 22:13:05 +02:00
|
|
|
RANGE(range, 0, (ADDRESS)(sizes->mem_size - 1),
|
2006-07-24 21:23:23 +02:00
|
|
|
RANGE_TYPE(ResExcMemBlock, domain));
|
|
|
|
pRes = xf86AddResToList(pRes, &range, -1);
|
2003-11-14 17:48:57 +01:00
|
|
|
|
2006-07-24 22:13:05 +02:00
|
|
|
RANGE(range, 0, (IOADDRESS)(sizes->io_size - 1),
|
2006-07-24 21:23:23 +02:00
|
|
|
RANGE_TYPE(ResExcIoBlock, domain));
|
|
|
|
pRes = xf86AddResToList(pRes, &range, -1);
|
|
|
|
|
|
|
|
if (domain <= 0)
|
|
|
|
break;
|
2003-11-14 17:48:57 +01:00
|
|
|
}
|
|
|
|
|
2006-07-24 21:23:23 +02:00
|
|
|
pci_iterator_destroy(iter);
|
|
|
|
|
2003-11-14 17:48:57 +01:00
|
|
|
return pRes;
|
|
|
|
}
|
|
|
|
|
|
|
|
resPtr
|
|
|
|
xf86PciBusAccWindowsFromOS(void)
|
|
|
|
{
|
2006-07-24 21:17:27 +02:00
|
|
|
return xf86BusAccWindowsFromOS();
|
2003-11-14 17:48:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
resPtr
|
|
|
|
xf86AccResFromOS(resPtr pRes)
|
|
|
|
{
|
2006-07-24 21:23:23 +02:00
|
|
|
struct pci_device *dev;
|
|
|
|
sturct pci_device_iterator *iter;
|
2003-11-14 17:48:57 +01:00
|
|
|
resRange range;
|
2006-07-24 21:23:23 +02:00
|
|
|
|
|
|
|
iter = pci_id_match_iterator_create(& match_host_bridge);
|
|
|
|
while ((dev = pci_device_next(iter)) != NULL) {
|
|
|
|
const PCITAG tag = PCI_MAKE_TAG(PCI_MAKE_BUS(dev->domain, dev->bus),
|
|
|
|
dev->dev, dev->func);
|
|
|
|
const int domain = xf86GetPciDomain(tag);
|
2006-07-24 22:13:05 +02:00
|
|
|
const struct pciSizes * const sizes = linuxGetSizesStruct(dev);
|
2006-07-24 21:23:23 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* At minimum, the top and bottom resources must be claimed, so
|
|
|
|
* that resources that are (or appear to be) unallocated can be
|
|
|
|
* relocated.
|
|
|
|
*/
|
|
|
|
RANGE(range, 0x00000000u, 0x0009ffffu,
|
|
|
|
RANGE_TYPE(ResExcMemBlock, domain));
|
|
|
|
pRes = xf86AddResToList(pRes, &range, -1);
|
|
|
|
RANGE(range, 0x000c0000u, 0x000effffu,
|
|
|
|
RANGE_TYPE(ResExcMemBlock, domain));
|
|
|
|
pRes = xf86AddResToList(pRes, &range, -1);
|
|
|
|
RANGE(range, 0x000f0000u, 0x000fffffu,
|
|
|
|
RANGE_TYPE(ResExcMemBlock, domain));
|
|
|
|
pRes = xf86AddResToList(pRes, &range, -1);
|
|
|
|
|
2006-07-24 22:13:05 +02:00
|
|
|
RANGE(range, (ADDRESS)(sizes->mem_size - 1),
|
|
|
|
(ADDRESS)(sizes->mem_size - 1),
|
2006-07-24 21:23:23 +02:00
|
|
|
RANGE_TYPE(ResExcMemBlock, domain));
|
|
|
|
pRes = xf86AddResToList(pRes, &range, -1);
|
|
|
|
|
|
|
|
RANGE(range, 0x00000000u, 0x00000000u,
|
|
|
|
RANGE_TYPE(ResExcIoBlock, domain));
|
|
|
|
pRes = xf86AddResToList(pRes, &range, -1);
|
2006-07-24 22:13:05 +02:00
|
|
|
RANGE(range, (IOADDRESS)(sizes->io_size - 1),
|
|
|
|
(IOADDRESS)(sizes->io_size - 1),
|
2006-07-24 21:23:23 +02:00
|
|
|
RANGE_TYPE(ResExcIoBlock, domain));
|
|
|
|
pRes = xf86AddResToList(pRes, &range, -1);
|
|
|
|
|
|
|
|
if (domain <= 0)
|
|
|
|
break;
|
2003-11-14 17:48:57 +01:00
|
|
|
}
|
|
|
|
|
2006-07-24 21:23:23 +02:00
|
|
|
pci_iterator_destroy(iter);
|
|
|
|
|
2003-11-14 17:48:57 +01:00
|
|
|
return pRes;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* !INCLUDE_XF86_NO_DOMAIN */
|