2 * Copyright 1998 by Concurrent Computer Corporation
4 * Permission to use, copy, modify, distribute, and sell this software
5 * and its documentation for any purpose is hereby granted without fee,
6 * provided that the above copyright notice appear in all copies and that
7 * both that copyright notice and this permission notice appear in
8 * supporting documentation, and that the name of Concurrent Computer
9 * Corporation not be used in advertising or publicity pertaining to
10 * distribution of the software without specific, written prior
11 * permission. Concurrent Computer Corporation makes no representations
12 * about the suitability of this software for any purpose. It is
13 * provided "as is" without express or implied warranty.
15 * CONCURRENT COMPUTER CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD
16 * TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
17 * AND FITNESS, IN NO EVENT SHALL CONCURRENT COMPUTER CORPORATION BE
18 * LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
19 * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
20 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
21 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
24 * Copyright 1998 by Metro Link Incorporated
26 * Permission to use, copy, modify, distribute, and sell this software
27 * and its documentation for any purpose is hereby granted without fee,
28 * provided that the above copyright notice appear in all copies and that
29 * both that copyright notice and this permission notice appear in
30 * supporting documentation, and that the name of Metro Link
31 * Incorporated not be used in advertising or publicity pertaining to
32 * distribution of the software without specific, written prior
33 * permission. Metro Link Incorporated makes no representations
34 * about the suitability of this software for any purpose. It is
35 * provided "as is" without express or implied warranty.
37 * METRO LINK INCORPORATED DISCLAIMS ALL WARRANTIES WITH REGARD
38 * TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
39 * AND FITNESS, IN NO EVENT SHALL METRO LINK INCORPORATED BE
40 * LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
41 * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
42 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
43 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
47 #ifdef HAVE_XORG_CONFIG_H
48 #include <xorg-config.h>
55 #include "xf86_OSlib.h"
59 static const struct pci_id_match match_host_bridge = {
60 PCI_MATCH_ANY, PCI_MATCH_ANY, PCI_MATCH_ANY, PCI_MATCH_ANY,
61 (PCI_CLASS_BRIDGE << 16) | (PCI_SUBCLASS_BRIDGE_HOST << 8),
65 #define MAX_DOMAINS 257
66 static pointer DomainMmappedIO[MAX_DOMAINS];
71 memset(DomainMmappedIO, 0, sizeof(DomainMmappedIO));
76 * The generation of the procfs file name for the domain != 0 case may not be
80 linuxPciOpenFile(struct pci_device *dev, Bool write)
82 static struct pci_device *last_dev = NULL;
83 static int fd = -1,is_write = 0;
93 is26 = (stat("/sys/bus/pci", &ignored) < 0) ? 0 : 1;
96 if (fd == -1 || (write && (!is_write)) || (last_dev != dev)) {
103 sprintf(file,"/sys/bus/pci/devices/%04u:%02x:%02x.%01x/config",
104 dev->domain, dev->bus, dev->dev, dev->func);
106 if (dev->domain == 0) {
107 sprintf(file,"/proc/bus/pci/%02x", dev->bus);
108 if (stat(file, &ignored) < 0) {
109 sprintf(file, "/proc/bus/pci/0000:%02x/%02x.%1x",
110 dev->bus, dev->dev, dev->func);
112 sprintf(file, "/proc/bus/pci/%02x/%02x.%1x",
113 dev->bus, dev->dev, dev->func);
116 sprintf(file,"/proc/bus/pci/%02x%02x", dev->domain, dev->bus);
117 if (stat(file, &ignored) < 0) {
118 sprintf(file, "/proc/bus/pci/%04x:%04x/%02x.%1x",
119 dev->domain, dev->bus, dev->dev, dev->func);
121 sprintf(file, "/proc/bus/pci/%02x%02x/%02x.%1x",
122 dev->domain, dev->bus, dev->dev, dev->func);
128 fd = open(file,O_RDWR);
129 if (fd != -1) is_write = TRUE;
133 fd = open(file,O_RDWR);
137 fd = open(file,O_RDONLY);
149 * Compiling the following simply requires the presence of <linux/pci.c>.
150 * Actually running this is another matter altogether...
152 * This scheme requires that the kernel allow mmap()'ing of a host bridge's I/O
153 * and memory spaces through its /proc/bus/pci/BUS/DFN entry. Which one is
154 * determined by a prior ioctl().
156 * For the sparc64 port, this means 2.4.12 or later. For ppc, this
157 * functionality is almost, but not quite there yet. Alpha and other kernel
158 * ports to multi-domain architectures still need to implement this.
160 * This scheme is also predicated on the use of an IOADDRESS compatible type to
161 * designate I/O addresses. Although IOADDRESS is defined as an unsigned
162 * integral type, it is actually the virtual address of, i.e. a pointer to, the
163 * I/O port to access. And so, the inX/outX macros in "compiler.h" need to be
164 * #define'd appropriately (as is done on SPARC's).
166 * Another requirement to port this scheme to another multi-domain architecture
167 * is to add the appropriate entries in the pciControllerSizes array below.
169 * TO DO: Address the deleterious reaction some host bridges have to master
170 * aborts. This is already done for secondary PCI buses, but not yet
171 * for accesses to primary buses (except for the SPARC port, where
172 * master aborts are avoided during PCI scans).
175 #include <linux/pci.h>
177 #ifndef PCIIOC_BASE /* Ioctls for /proc/bus/pci/X/Y nodes. */
178 #define PCIIOC_BASE ('P' << 24 | 'C' << 16 | 'I' << 8)
180 /* Get controller for PCI device. */
181 #define PCIIOC_CONTROLLER (PCIIOC_BASE | 0x00)
182 /* Set mmap state to I/O space. */
183 #define PCIIOC_MMAP_IS_IO (PCIIOC_BASE | 0x01)
184 /* Set mmap state to MEM space. */
185 #define PCIIOC_MMAP_IS_MEM (PCIIOC_BASE | 0x02)
186 /* Enable/disable write-combining. */
187 #define PCIIOC_WRITE_COMBINE (PCIIOC_BASE | 0x03)
191 /* This probably shouldn't be Linux-specific */
192 static struct pci_device *
193 get_parent_bridge(struct pci_device *dev)
195 struct pci_id_match bridge_match = {
196 PCI_MATCH_ANY, PCI_MATCH_ANY, PCI_MATCH_ANY, PCI_MATCH_ANY,
197 (PCI_CLASS_BRIDGE << 16) | (PCI_SUBCLASS_BRIDGE_PCI << 8),
200 struct pci_device *bridge;
201 struct pci_device_iterator *iter;
207 iter = pci_id_match_iterator_create(& bridge_match);
212 while ((bridge = pci_device_next(iter)) != NULL) {
213 if (bridge->domain == dev->domain) {
214 const struct pci_bridge_info *info =
215 pci_device_get_bridge_info(bridge);
218 if (info->secondary_bus == dev->bus) {
225 pci_iterator_destroy(iter);
231 * This is ugly, but until I can extract this information from the kernel,
232 * it'll have to do. The default I/O space size is 64K, and 4G for memory.
233 * Anything else needs to go in this table. (PowerPC folk take note.)
235 * Note that Linux/SPARC userland is 32-bit, so 4G overflows to zero here.
237 * Please keep this table in ascending vendor/device order.
239 static const struct pciSizes {
240 unsigned short vendor, device;
241 unsigned long io_size, mem_size;
242 } pciControllerSizes[] = {
244 PCI_VENDOR_SUN, PCI_CHIP_PSYCHO,
248 PCI_VENDOR_SUN, PCI_CHIP_SCHIZO,
249 1U << 24, 1U << 31 /* ??? */
252 PCI_VENDOR_SUN, PCI_CHIP_SABRE,
253 1U << 24, (unsigned long)(1ULL << 32)
256 PCI_VENDOR_SUN, PCI_CHIP_HUMMINGBIRD,
257 1U << 24, (unsigned long)(1ULL << 32)
260 #define NUM_SIZES (sizeof(pciControllerSizes) / sizeof(pciControllerSizes[0]))
262 static const struct pciSizes *
263 linuxGetSizesStruct(const struct pci_device *dev)
265 static const struct pciSizes default_size = {
266 0, 0, 1U << 16, (unsigned long)(1ULL << 32)
270 /* Look up vendor/device */
272 for (i = 0; i < NUM_SIZES; i++) {
273 if ((dev->vendor_id == pciControllerSizes[i].vendor)
274 && (dev->device_id == pciControllerSizes[i].device)) {
275 return & pciControllerSizes[i];
280 /* Default to 64KB I/O and 4GB memory. */
281 return & default_size;
284 static __inline__ unsigned long
285 linuxGetIOSize(const struct pci_device *dev)
287 const struct pciSizes * const sizes = linuxGetSizesStruct(dev);
288 return sizes->io_size;
292 linuxMapPci(int ScreenNum, int Flags, struct pci_device *dev,
293 ADDRESS Base, unsigned long Size, int mmap_ioctl)
295 /* Align to page boundary */
296 const ADDRESS realBase = Base & ~(getpagesize() - 1);
297 const ADDRESS Offset = Base - realBase;
300 unsigned char *result;
301 int fd, mmapflags, prot;
305 /* If dev is NULL, linuxPciOpenFile will return -1, and this routine
306 * will fail gracefully.
308 prot = ((Flags & VIDMEM_READONLY) == 0);
309 if (((fd = linuxPciOpenFile(dev, prot)) < 0) ||
310 (ioctl(fd, mmap_ioctl, 0) < 0))
313 /* Note: IA-64 doesn't compile this and doesn't need to */
316 # ifndef MAP_WRITECOMBINED
317 # define MAP_WRITECOMBINED 0x00010000
319 # ifndef MAP_NONCACHED
320 # define MAP_NONCACHED 0x00020000
323 if (Flags & VIDMEM_FRAMEBUFFER)
324 mmapflags = MAP_SHARED | MAP_WRITECOMBINED;
326 mmapflags = MAP_SHARED | MAP_NONCACHED;
328 #else /* !__ia64__ */
330 mmapflags = (Flags & VIDMEM_FRAMEBUFFER) / VIDMEM_FRAMEBUFFER;
332 if (ioctl(fd, PCIIOC_WRITE_COMBINE, mmapflags) < 0)
335 mmapflags = MAP_SHARED;
337 #endif /* ?__ia64__ */
340 if (Flags & VIDMEM_READONLY)
343 prot = PROT_READ | PROT_WRITE;
345 result = mmap(NULL, Size + Offset, prot, mmapflags, fd, realBase);
347 if (!result || ((pointer)result == MAP_FAILED))
350 xf86MakeNewMapping(ScreenNum, Flags, realBase, Size + Offset, result);
352 return result + Offset;
355 if (mmap_ioctl == PCIIOC_MMAP_IS_MEM)
356 return xf86MapVidMem(ScreenNum, Flags, Base, Size);
362 linuxOpenLegacy(struct pci_device *dev, char *name)
364 static const char PREFIX[] = "/sys/class/pci_bus/%04x:%02x/%s";
365 char path[sizeof(PREFIX) + 10];
368 while (dev != NULL) {
369 snprintf(path, sizeof(path) - 1, PREFIX, dev->domain, dev->bus, name);
370 fd = open(path, O_RDWR);
375 dev = get_parent_bridge(dev);
382 * xf86MapDomainMemory - memory map PCI domain memory
384 * This routine maps the memory region in the domain specified by Tag and
385 * returns a pointer to it. The pointer is saved for future use if it's in
386 * the legacy ISA memory space (memory in a domain between 0 and 1MB).
389 xf86MapDomainMemory(int ScreenNum, int Flags, struct pci_device *dev,
390 ADDRESS Base, unsigned long Size)
396 * We use /proc/bus/pci on non-legacy addresses or if the Linux sysfs
397 * legacy_mem interface is unavailable.
399 if ((Base > 1024*1024) || ((fd = linuxOpenLegacy(dev, "legacy_mem")) < 0))
400 return linuxMapPci(ScreenNum, Flags, dev, Base, Size,
403 addr = mmap(NULL, Size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, Base);
407 if (addr == NULL || addr == MAP_FAILED) {
408 perror("mmap failure");
409 FatalError("xf86MapDomainMem(): mmap() failure\n");
415 * Map I/O space in this domain
417 * Each domain has a legacy ISA I/O space. This routine will try to
418 * map it using the Linux sysfs legacy_io interface. If that fails,
419 * it'll fall back to using /proc/bus/pci.
421 * If the legacy_io interface \b does exist, the file descriptor (\c fd below)
422 * will be saved in the \c DomainMmappedIO array in the upper bits of the
423 * pointer. Callers will do I/O with small port numbers (<64k values), so
424 * the platform I/O code can extract the port number and the \c fd, \c lseek
425 * to the port number in the legacy_io file, and issue the read or write.
427 * This has no means of returning failure, so all errors are fatal
430 xf86MapLegacyIO(struct pci_device *dev)
432 const int domain = dev->domain;
433 struct pci_device *bridge = get_parent_bridge(dev);
436 if (domain >= MAX_DOMAINS)
437 FatalError("xf86MapLegacyIO(): domain out of range\n");
439 if (DomainMmappedIO[domain] == NULL) {
440 /* Permanently map all of I/O space */
441 fd = linuxOpenLegacy(bridge, "legacy_io");
443 DomainMmappedIO[domain] = linuxMapPci(-1, VIDMEM_MMIO, bridge,
444 0, linuxGetIOSize(bridge),
447 else { /* legacy_io file exists, encode fd */
448 DomainMmappedIO[domain] = (pointer)(intptr_t)(fd << 24);
452 return (IOADDRESS)DomainMmappedIO[domain];