/* $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/lnx_pci.c,v 3.8 2002/04/09 15:59:37 tsi Exp $ */ #ifdef HAVE_XORG_CONFIG_H #include #endif #include #include #include "os.h" #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 int lnxPciInit(void); 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; int xf86OSLinuxNumPciDevs = 0; 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; xf86OSLinuxNumPciDevs = 0; 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; xf86OSLinuxNumPciDevs++; } } while (res); fclose(file); return ret; } /* 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; } Bool xf86GetPciSizeFromOS(PCITAG tag, int index, int* bits) { unsigned int dev, fn; signed PCIADDR_TYPE Size; struct pci_dev *device; if (index > 7) return FALSE; 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; } } return FALSE; } /* Query the kvirt address (64bit) of a BAR range from TAG */ Bool xf86GetPciOffsetFromOS(PCITAG tag, int index, unsigned long* bases) { unsigned int dev, fn; struct pci_dev *device; if (index > 7) return FALSE; 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)) { /* return the offset for the index requested */ *bases = device->offset[index]; return TRUE; } } 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 dev, fn; unsigned int ndx; struct pci_dev *device; 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)) { /* ok now look through all the BAR values of this device */ pciConfigPtr pDev = xf86GetPciConfigFromTag(tag); for (ndx=0; ndx<7; ndx++) { unsigned long savePtr, flagMask; if (ndx == 6) savePtr = pDev->pci_baserom; else /* this the ROM bar */ savePtr = (&pDev->pci_base0)[ndx]; /* Ignore unset base addresses. The kernel may * have reported non-zero size and address even * if they are disabled (e.g. disabled ROM BAR). */ if (savePtr == 0) continue; /* Remove memory attribute bits, different for IO * and memory ranges. */ 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); } } } }; return 0; }