2003-11-14 17:48:57 +01:00
|
|
|
|
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>
|
2005-04-20 14:25:48 +02:00
|
|
|
#include <X11/X.h>
|
2005-07-03 10:53:54 +02:00
|
|
|
#include "os.h"
|
2003-11-14 17:48:57 +01:00
|
|
|
#include "xf86.h"
|
|
|
|
#include "xf86Priv.h"
|
|
|
|
#define XF86_OS_PRIVS
|
|
|
|
#include "xf86_OSproc.h"
|
|
|
|
#include "xf86Pci.h"
|
|
|
|
|
|
|
|
#ifdef __sparc__
|
|
|
|
#define PCIADDR_TYPE long long
|
|
|
|
#define PCIADDR_IGNORE_FMT "%*x"
|
|
|
|
#define PCIADDR_FMT "%llx"
|
|
|
|
#else
|
|
|
|
#define PCIADDR_TYPE long
|
|
|
|
#define PCIADDR_IGNORE_FMT "%*x"
|
|
|
|
#define PCIADDR_FMT "%lx"
|
|
|
|
#endif
|
|
|
|
|
2006-06-05 22:22:06 +02:00
|
|
|
int lnxPciInit(void);
|
|
|
|
|
2006-06-02 00:30:52 +02:00
|
|
|
struct pci_dev {
|
|
|
|
unsigned int bus;
|
|
|
|
unsigned int devfn;
|
|
|
|
PCIADDR_TYPE offset[7];
|
|
|
|
PCIADDR_TYPE size[7];
|
|
|
|
struct pci_dev *next;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct pci_dev *xf86OSLinuxPCIDevs = NULL;
|
2006-06-05 22:22:06 +02:00
|
|
|
int xf86OSLinuxNumPciDevs = 0;
|
2006-06-02 00:30:52 +02:00
|
|
|
|
|
|
|
static struct pci_dev *xf86OSLinuxGetPciDevs(void) {
|
|
|
|
char c[0x200];
|
|
|
|
FILE *file = NULL;
|
|
|
|
struct pci_dev *tmp, *ret = NULL;
|
|
|
|
unsigned int num;
|
|
|
|
char *res;
|
|
|
|
|
|
|
|
file = fopen("/proc/bus/pci/devices", "r");
|
|
|
|
if (!file) return NULL;
|
2006-06-05 22:22:06 +02:00
|
|
|
|
|
|
|
xf86OSLinuxNumPciDevs = 0;
|
2006-06-02 00:30:52 +02:00
|
|
|
|
|
|
|
do {
|
|
|
|
res = fgets(c, 0x1ff, file);
|
|
|
|
if (res) {
|
|
|
|
tmp = xcalloc(sizeof(struct pci_dev),1);
|
|
|
|
num = sscanf(res,
|
|
|
|
/*bus+dev vendorid deviceid irq */
|
|
|
|
"%02x%02x\t%*04x%*04x\t%*x"
|
|
|
|
/* 7 PCI resource base addresses */
|
|
|
|
"\t" PCIADDR_FMT
|
|
|
|
"\t" PCIADDR_FMT
|
|
|
|
"\t" PCIADDR_FMT
|
|
|
|
"\t" PCIADDR_FMT
|
|
|
|
"\t" PCIADDR_FMT
|
|
|
|
"\t" PCIADDR_FMT
|
|
|
|
"\t" PCIADDR_FMT
|
|
|
|
/* 7 PCI resource sizes, and then optionally a driver name */
|
|
|
|
"\t" PCIADDR_FMT
|
|
|
|
"\t" PCIADDR_FMT
|
|
|
|
"\t" PCIADDR_FMT
|
|
|
|
"\t" PCIADDR_FMT
|
|
|
|
"\t" PCIADDR_FMT
|
|
|
|
"\t" PCIADDR_FMT
|
|
|
|
"\t" PCIADDR_FMT,
|
|
|
|
&tmp->bus,&tmp->devfn,&tmp->offset[0],&tmp->offset[1],&tmp->offset[2],&tmp->offset[3],
|
|
|
|
&tmp->offset[4],&tmp->offset[5],&tmp->offset[6], &tmp->size[0], &tmp->size[1], &tmp->size[2],
|
|
|
|
&tmp->size[3], &tmp->size[4], &tmp->size[5], &tmp->size[6]);
|
|
|
|
if (num != 16) { /* apparantly not 2.3 style */
|
|
|
|
xfree(tmp);
|
|
|
|
fclose(file);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (ret) {
|
|
|
|
tmp->next = ret;
|
|
|
|
}
|
|
|
|
ret = tmp;
|
2006-06-05 22:22:06 +02:00
|
|
|
xf86OSLinuxNumPciDevs++;
|
2006-06-02 00:30:52 +02:00
|
|
|
}
|
|
|
|
} while (res);
|
|
|
|
fclose(file);
|
|
|
|
return ret;
|
|
|
|
}
|
2005-08-26 09:15:04 +02:00
|
|
|
|
2006-06-05 22:22:06 +02:00
|
|
|
/* not to be confused with linuxPciInit (i.e. ARCH_PCI_INIT), found in
|
|
|
|
* os-support/bus/linuxPci.c. */
|
|
|
|
int lnxPciInit(void) {
|
|
|
|
if (!xf86OSLinuxPCIDevs)
|
|
|
|
xf86OSLinuxPCIDevs = xf86OSLinuxGetPciDevs();
|
|
|
|
return xf86OSLinuxNumPciDevs;
|
|
|
|
}
|
|
|
|
|
2003-11-14 17:48:57 +01:00
|
|
|
Bool
|
|
|
|
xf86GetPciSizeFromOS(PCITAG tag, int index, int* bits)
|
|
|
|
{
|
2006-06-02 00:30:52 +02:00
|
|
|
unsigned int dev, fn;
|
2003-11-14 17:48:57 +01:00
|
|
|
signed PCIADDR_TYPE Size;
|
2006-06-02 00:30:52 +02:00
|
|
|
struct pci_dev *device;
|
2003-11-14 17:48:57 +01:00
|
|
|
|
|
|
|
if (index > 7)
|
2006-06-02 00:30:52 +02:00
|
|
|
return FALSE;
|
2003-11-14 17:48:57 +01:00
|
|
|
|
2006-06-02 00:30:52 +02:00
|
|
|
if (!xf86OSLinuxPCIDevs) {
|
|
|
|
xf86OSLinuxPCIDevs = xf86OSLinuxGetPciDevs();
|
|
|
|
}
|
|
|
|
if (!xf86OSLinuxPCIDevs)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
for (device = xf86OSLinuxPCIDevs; device; device = device->next) {
|
|
|
|
dev = device->devfn >> 3;
|
|
|
|
fn = device->devfn & 0x7;
|
|
|
|
if (tag == pciTag(device->bus,dev,fn)) {
|
|
|
|
*bits = 0;
|
|
|
|
if (device->size[index] != 0) {
|
|
|
|
Size = device->size[index] - ((PCIADDR_TYPE) 1);
|
|
|
|
while (Size & ((PCIADDR_TYPE) 0x01)) {
|
|
|
|
Size = Size >> ((PCIADDR_TYPE) 1);
|
|
|
|
(*bits)++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
}
|
2003-11-14 17:48:57 +01:00
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
2005-04-22 18:49:22 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Query the kvirt address (64bit) of a BAR range from TAG */
|
|
|
|
Bool
|
|
|
|
xf86GetPciOffsetFromOS(PCITAG tag, int index, unsigned long* bases)
|
|
|
|
{
|
2006-06-02 00:30:52 +02:00
|
|
|
unsigned int dev, fn;
|
|
|
|
struct pci_dev *device;
|
2005-04-22 18:49:22 +02:00
|
|
|
|
|
|
|
if (index > 7)
|
|
|
|
return FALSE;
|
|
|
|
|
2006-06-02 00:30:52 +02:00
|
|
|
if (!xf86OSLinuxPCIDevs) {
|
|
|
|
xf86OSLinuxPCIDevs = xf86OSLinuxGetPciDevs();
|
|
|
|
}
|
|
|
|
if (!xf86OSLinuxPCIDevs)
|
2005-04-22 18:49:22 +02:00
|
|
|
return FALSE;
|
|
|
|
|
2006-06-02 00:30:52 +02:00
|
|
|
for (device = xf86OSLinuxPCIDevs; device; device = device->next) {
|
|
|
|
dev = device->devfn >> 3;
|
|
|
|
fn = device->devfn & 0x7;
|
|
|
|
if (tag == pciTag(device->bus,dev,fn)) {
|
|
|
|
/* return the offset for the index requested */
|
|
|
|
*bases = device->offset[index];
|
|
|
|
return TRUE;
|
2005-04-22 18:49:22 +02:00
|
|
|
}
|
2006-06-02 00:30:52 +02:00
|
|
|
}
|
2005-04-22 18:49:22 +02:00
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Query the kvirt address (64bit) of a BAR range from size for a given TAG */
|
|
|
|
unsigned long
|
|
|
|
xf86GetOSOffsetFromPCI(PCITAG tag, int space, unsigned long base)
|
|
|
|
{
|
|
|
|
unsigned int ndx;
|
2006-06-02 00:30:52 +02:00
|
|
|
struct pci_dev *device;
|
2006-08-02 18:55:32 +02:00
|
|
|
struct pci_device *dev;
|
2005-04-22 18:49:22 +02:00
|
|
|
|
2006-06-02 00:30:52 +02:00
|
|
|
if (!xf86OSLinuxPCIDevs) {
|
|
|
|
xf86OSLinuxPCIDevs = xf86OSLinuxGetPciDevs();
|
|
|
|
}
|
|
|
|
if (!xf86OSLinuxPCIDevs) {
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (device = xf86OSLinuxPCIDevs; device; device = device->next) {
|
2006-08-02 18:55:32 +02:00
|
|
|
dev = pci_device_find_by_slot(0, device->bus, (device->devfn >> 3),
|
|
|
|
(device->devfn & 0x7));
|
|
|
|
if (dev != NULL) {
|
2006-06-02 00:30:52 +02:00
|
|
|
/* ok now look through all the BAR values of this device */
|
|
|
|
for (ndx=0; ndx<7; ndx++) {
|
2006-08-02 18:55:32 +02:00
|
|
|
uint32_t savePtr;
|
|
|
|
uint32_t flagMask;
|
|
|
|
|
|
|
|
/* The ROM BAR isn't with the other BARs.
|
|
|
|
*/
|
|
|
|
const pciaddr_t offset = (ndx == 6)
|
|
|
|
? (4 * 12) : (4 * ndx) + 16;
|
|
|
|
|
|
|
|
pci_device_cfg_read_u32(dev, &savePtr, offset);
|
|
|
|
|
|
|
|
/* Ignore unset base addresses. The kernel may have reported
|
|
|
|
* non-zero size and address even if they are disabled (e.g.,
|
|
|
|
* disabled ROM BAR).
|
2006-06-02 00:30:52 +02:00
|
|
|
*/
|
|
|
|
if (savePtr == 0)
|
|
|
|
continue;
|
2006-08-02 18:55:32 +02:00
|
|
|
|
2006-06-02 00:30:52 +02:00
|
|
|
/* Remove memory attribute bits, different for IO
|
2006-08-02 18:55:32 +02:00
|
|
|
* and memory ranges.
|
|
|
|
*/
|
2006-06-02 00:30:52 +02:00
|
|
|
flagMask = (savePtr & 0x1) ? ~0x3UL : ~0xFUL;
|
|
|
|
savePtr &= flagMask;
|
|
|
|
|
|
|
|
/* find the index of the incoming base */
|
|
|
|
if (base >= savePtr && base < (savePtr + device->size[ndx])) {
|
|
|
|
return (device->offset[ndx] & flagMask) + (base - savePtr);
|
2005-04-22 18:49:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2006-08-02 18:55:32 +02:00
|
|
|
}
|
2005-04-22 18:49:22 +02:00
|
|
|
|
2005-10-05 09:27:52 +02:00
|
|
|
return 0;
|
2005-04-22 18:49:22 +02:00
|
|
|
}
|