diff options
Diffstat (limited to 'xorg-server/hw/xfree86/os-support/bus/linuxPci.c')
-rw-r--r-- | xorg-server/hw/xfree86/os-support/bus/linuxPci.c | 904 |
1 files changed, 450 insertions, 454 deletions
diff --git a/xorg-server/hw/xfree86/os-support/bus/linuxPci.c b/xorg-server/hw/xfree86/os-support/bus/linuxPci.c index 289315ebc..77f724bf1 100644 --- a/xorg-server/hw/xfree86/os-support/bus/linuxPci.c +++ b/xorg-server/hw/xfree86/os-support/bus/linuxPci.c @@ -1,454 +1,450 @@ -/* - * 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. - */ - -#ifdef HAVE_XORG_CONFIG_H -#include <xorg-config.h> -#endif - -#include <stdio.h> -#include "compiler.h" -#include "xf86.h" -#include "xf86Priv.h" -#include "xf86_OSlib.h" -#include "Pci.h" -#include <dirent.h> - -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 -}; - -#define MAX_DOMAINS 257 -static pointer DomainMmappedIO[MAX_DOMAINS]; - -void -linuxPciInit(void) -{ - memset(DomainMmappedIO, 0, sizeof(DomainMmappedIO)); -} - -/** - * \bug - * The generation of the procfs file name for the domain != 0 case may not be - * correct. - */ -static int -linuxPciOpenFile(struct pci_device *dev, Bool write) -{ - 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 (dev == NULL) { - return -1; - } - - if (is26 == -1) { - is26 = (stat("/sys/bus/pci", &ignored) < 0) ? 0 : 1; - } - - if (fd == -1 || (write && (!is_write)) || (last_dev != dev)) { - if (fd != -1) { - close(fd); - fd = -1; - } - - 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); - } - } 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); - } - } - } - - 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; - } - } - - last_dev = dev; - } - - return fd; -} - -/* - * 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 */ -static struct pci_device * -get_parent_bridge(struct pci_device *dev) -{ - struct pci_id_match bridge_match = { - PCI_MATCH_ANY, PCI_MATCH_ANY, PCI_MATCH_ANY, PCI_MATCH_ANY, - (PCI_CLASS_BRIDGE << 16) | (PCI_SUBCLASS_BRIDGE_PCI << 8), - 0 - }; - struct pci_device *bridge; - struct pci_device_iterator *iter; - - if (dev == NULL) { - return NULL; - } - - iter = pci_id_match_iterator_create(& bridge_match); - if (iter == NULL) { - return NULL; - } - - while ((bridge = pci_device_next(iter)) != NULL) { - if (bridge->domain == dev->domain) { - const struct pci_bridge_info *info = - pci_device_get_bridge_info(bridge); - - if (info != NULL) { - if (info->secondary_bus == dev->bus) { - break; - } - } - } - } - - pci_iterator_destroy(iter); - - return bridge; -} - -/* - * 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. - */ -static const struct pciSizes { - 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])) - -static const struct pciSizes * -linuxGetSizesStruct(const struct pci_device *dev) -{ - static const struct pciSizes default_size = { - 0, 0, 1U << 16, (unsigned long)(1ULL << 32) - }; - int i; - - /* Look up vendor/device */ - if (dev != NULL) { - for (i = 0; i < NUM_SIZES; i++) { - if ((dev->vendor_id == pciControllerSizes[i].vendor) - && (dev->device_id == pciControllerSizes[i].device)) { - return & pciControllerSizes[i]; - } - } - } - - /* Default to 64KB I/O and 4GB memory. */ - return & default_size; -} - -static __inline__ unsigned long -linuxGetIOSize(const struct pci_device *dev) -{ - const struct pciSizes * const sizes = linuxGetSizesStruct(dev); - return sizes->io_size; -} - -static pointer -linuxMapPci(int ScreenNum, int Flags, struct pci_device *dev, - ADDRESS Base, unsigned long Size, int mmap_ioctl) -{ - /* Align to page boundary */ - const ADDRESS realBase = Base & ~(getpagesize() - 1); - const ADDRESS Offset = Base - realBase; - - do { - unsigned char *result; - int fd, mmapflags, prot; - - xf86InitVidMem(); - - /* If dev is NULL, linuxPciOpenFile will return -1, and this routine - * will fail gracefully. - */ - prot = ((Flags & VIDMEM_READONLY) == 0); - if (((fd = linuxPciOpenFile(dev, prot)) < 0) || - (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 - mmapflags = MAP_SHARED | MAP_NONCACHED; - -#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)) - return NULL; - - 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; -} - -static int -linuxOpenLegacy(struct pci_device *dev, char *name) -{ - static const char PREFIX[] = "/sys/class/pci_bus/%04x:%02x/%s"; - char path[sizeof(PREFIX) + 10]; - int fd = -1; - - while (dev != NULL) { - snprintf(path, sizeof(path) - 1, PREFIX, dev->domain, dev->bus, name); - fd = open(path, O_RDWR); - if (fd >= 0) { - return fd; - } - - dev = get_parent_bridge(dev); - } - - 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). - */ -pointer -xf86MapDomainMemory(int ScreenNum, int Flags, struct pci_device *dev, - ADDRESS Base, unsigned long Size) -{ - int fd = -1; - pointer addr; - - /* - * We use /proc/bus/pci on non-legacy addresses or if the Linux sysfs - * legacy_mem interface is unavailable. - */ - if ((Base > 1024*1024) || ((fd = linuxOpenLegacy(dev, "legacy_mem")) < 0)) - return linuxMapPci(ScreenNum, Flags, dev, Base, Size, - PCIIOC_MMAP_IS_MEM); - else - addr = mmap(NULL, Size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, Base); - - if (fd >= 0) - close(fd); - if (addr == NULL || addr == MAP_FAILED) { - perror("mmap failure"); - FatalError("xf86MapDomainMem(): mmap() failure\n"); - } - return addr; -} - -/** - * Map I/O space in this domain - * - * 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. - * - * 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 - * pointer. Callers will do I/O with small port numbers (<64k values), so - * 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. - * - * This has no means of returning failure, so all errors are fatal - */ -IOADDRESS -xf86MapLegacyIO(struct pci_device *dev) -{ - const int domain = dev->domain; - struct pci_device *bridge = get_parent_bridge(dev); - int fd; - - if (domain >= MAX_DOMAINS) - FatalError("xf86MapLegacyIO(): domain out of range\n"); - - if (DomainMmappedIO[domain] == NULL) { - /* Permanently map all of I/O space */ - fd = linuxOpenLegacy(bridge, "legacy_io"); - if (fd < 0) { - DomainMmappedIO[domain] = linuxMapPci(-1, VIDMEM_MMIO, bridge, - 0, linuxGetIOSize(bridge), - PCIIOC_MMAP_IS_IO); - } - else { /* legacy_io file exists, encode fd */ - DomainMmappedIO[domain] = (pointer)(intptr_t)(fd << 24); - } - } - - return (IOADDRESS)DomainMmappedIO[domain]; -} - +/*
+ * 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.
+ */
+
+#ifdef HAVE_XORG_CONFIG_H
+#include <xorg-config.h>
+#endif
+
+#include <stdio.h>
+#include "xf86_OSlib.h"
+#include "Pci.h"
+
+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
+};
+
+#define MAX_DOMAINS 257
+static pointer DomainMmappedIO[MAX_DOMAINS];
+
+void
+linuxPciInit(void)
+{
+ memset(DomainMmappedIO, 0, sizeof(DomainMmappedIO));
+}
+
+/**
+ * \bug
+ * The generation of the procfs file name for the domain != 0 case may not be
+ * correct.
+ */
+static int
+linuxPciOpenFile(struct pci_device *dev, Bool write)
+{
+ 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 (dev == NULL) {
+ return -1;
+ }
+
+ if (is26 == -1) {
+ is26 = (stat("/sys/bus/pci", &ignored) < 0) ? 0 : 1;
+ }
+
+ if (fd == -1 || (write && (!is_write)) || (last_dev != dev)) {
+ if (fd != -1) {
+ close(fd);
+ fd = -1;
+ }
+
+ 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);
+ }
+ } 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);
+ }
+ }
+ }
+
+ 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;
+ }
+ }
+
+ last_dev = dev;
+ }
+
+ return fd;
+}
+
+/*
+ * 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 */
+static struct pci_device *
+get_parent_bridge(struct pci_device *dev)
+{
+ struct pci_id_match bridge_match = {
+ PCI_MATCH_ANY, PCI_MATCH_ANY, PCI_MATCH_ANY, PCI_MATCH_ANY,
+ (PCI_CLASS_BRIDGE << 16) | (PCI_SUBCLASS_BRIDGE_PCI << 8),
+ 0
+ };
+ struct pci_device *bridge;
+ struct pci_device_iterator *iter;
+
+ if (dev == NULL) {
+ return NULL;
+ }
+
+ iter = pci_id_match_iterator_create(& bridge_match);
+ if (iter == NULL) {
+ return NULL;
+ }
+
+ while ((bridge = pci_device_next(iter)) != NULL) {
+ if (bridge->domain == dev->domain) {
+ const struct pci_bridge_info *info =
+ pci_device_get_bridge_info(bridge);
+
+ if (info != NULL) {
+ if (info->secondary_bus == dev->bus) {
+ break;
+ }
+ }
+ }
+ }
+
+ pci_iterator_destroy(iter);
+
+ return bridge;
+}
+
+/*
+ * 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.
+ */
+static const struct pciSizes {
+ 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]))
+
+static const struct pciSizes *
+linuxGetSizesStruct(const struct pci_device *dev)
+{
+ static const struct pciSizes default_size = {
+ 0, 0, 1U << 16, (unsigned long)(1ULL << 32)
+ };
+ int i;
+
+ /* Look up vendor/device */
+ if (dev != NULL) {
+ for (i = 0; i < NUM_SIZES; i++) {
+ if ((dev->vendor_id == pciControllerSizes[i].vendor)
+ && (dev->device_id == pciControllerSizes[i].device)) {
+ return & pciControllerSizes[i];
+ }
+ }
+ }
+
+ /* Default to 64KB I/O and 4GB memory. */
+ return & default_size;
+}
+
+static __inline__ unsigned long
+linuxGetIOSize(const struct pci_device *dev)
+{
+ const struct pciSizes * const sizes = linuxGetSizesStruct(dev);
+ return sizes->io_size;
+}
+
+static pointer
+linuxMapPci(int ScreenNum, int Flags, struct pci_device *dev,
+ ADDRESS Base, unsigned long Size, int mmap_ioctl)
+{
+ /* Align to page boundary */
+ const ADDRESS realBase = Base & ~(getpagesize() - 1);
+ const ADDRESS Offset = Base - realBase;
+
+ do {
+ unsigned char *result;
+ int fd, mmapflags, prot;
+
+ xf86InitVidMem();
+
+ /* If dev is NULL, linuxPciOpenFile will return -1, and this routine
+ * will fail gracefully.
+ */
+ prot = ((Flags & VIDMEM_READONLY) == 0);
+ if (((fd = linuxPciOpenFile(dev, prot)) < 0) ||
+ (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
+ mmapflags = MAP_SHARED | MAP_NONCACHED;
+
+#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))
+ return NULL;
+
+ 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;
+}
+
+static int
+linuxOpenLegacy(struct pci_device *dev, char *name)
+{
+ static const char PREFIX[] = "/sys/class/pci_bus/%04x:%02x/%s";
+ char path[sizeof(PREFIX) + 10];
+ int fd = -1;
+
+ while (dev != NULL) {
+ snprintf(path, sizeof(path) - 1, PREFIX, dev->domain, dev->bus, name);
+ fd = open(path, O_RDWR);
+ if (fd >= 0) {
+ return fd;
+ }
+
+ dev = get_parent_bridge(dev);
+ }
+
+ 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).
+ */
+pointer
+xf86MapDomainMemory(int ScreenNum, int Flags, struct pci_device *dev,
+ ADDRESS Base, unsigned long Size)
+{
+ int fd = -1;
+ pointer addr;
+
+ /*
+ * We use /proc/bus/pci on non-legacy addresses or if the Linux sysfs
+ * legacy_mem interface is unavailable.
+ */
+ if ((Base > 1024*1024) || ((fd = linuxOpenLegacy(dev, "legacy_mem")) < 0))
+ return linuxMapPci(ScreenNum, Flags, dev, Base, Size,
+ PCIIOC_MMAP_IS_MEM);
+ else
+ addr = mmap(NULL, Size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, Base);
+
+ if (fd >= 0)
+ close(fd);
+ if (addr == NULL || addr == MAP_FAILED) {
+ perror("mmap failure");
+ FatalError("xf86MapDomainMem(): mmap() failure\n");
+ }
+ return addr;
+}
+
+/**
+ * Map I/O space in this domain
+ *
+ * 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.
+ *
+ * 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
+ * pointer. Callers will do I/O with small port numbers (<64k values), so
+ * 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.
+ *
+ * This has no means of returning failure, so all errors are fatal
+ */
+IOADDRESS
+xf86MapLegacyIO(struct pci_device *dev)
+{
+ const int domain = dev->domain;
+ struct pci_device *bridge = get_parent_bridge(dev);
+ int fd;
+
+ if (domain >= MAX_DOMAINS)
+ FatalError("xf86MapLegacyIO(): domain out of range\n");
+
+ if (DomainMmappedIO[domain] == NULL) {
+ /* Permanently map all of I/O space */
+ fd = linuxOpenLegacy(bridge, "legacy_io");
+ if (fd < 0) {
+ DomainMmappedIO[domain] = linuxMapPci(-1, VIDMEM_MMIO, bridge,
+ 0, linuxGetIOSize(bridge),
+ PCIIOC_MMAP_IS_IO);
+ }
+ else { /* legacy_io file exists, encode fd */
+ DomainMmappedIO[domain] = (pointer)(intptr_t)(fd << 24);
+ }
+ }
+
+ return (IOADDRESS)DomainMmappedIO[domain];
+}
+
|