1/* 2 * Copyright 1998 by Concurrent Computer Corporation 3 * 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. 14 * 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 22 * SOFTWARE. 23 * 24 * Copyright 1998 by Metro Link Incorporated 25 * 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. 36 * 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 44 * SOFTWARE. 45 */ 46 47#ifdef HAVE_XORG_CONFIG_H 48#include <xorg-config.h> 49#endif 50 51#include <stdio.h> 52#include "xf86_OSlib.h" 53#include "Pci.h" 54 55static const struct pci_id_match match_host_bridge = { 56 PCI_MATCH_ANY, PCI_MATCH_ANY, PCI_MATCH_ANY, PCI_MATCH_ANY, 57 (PCI_CLASS_BRIDGE << 16) | (PCI_SUBCLASS_BRIDGE_HOST << 8), 58 0x0000ffff00, 0 59}; 60 61#define MAX_DOMAINS 257 62static pointer DomainMmappedIO[MAX_DOMAINS]; 63 64void 65linuxPciInit(void) 66{ 67 memset(DomainMmappedIO, 0, sizeof(DomainMmappedIO)); 68} 69 70/** 71 * \bug 72 * The generation of the procfs file name for the domain != 0 case may not be 73 * correct. 74 */ 75static int 76linuxPciOpenFile(struct pci_device *dev, Bool write) 77{ 78 static struct pci_device *last_dev = NULL; 79 static int fd = -1,is_write = 0; 80 char file[64]; 81 struct stat ignored; 82 static int is26 = -1; 83 84 if (dev == NULL) { 85 return -1; 86 } 87 88 if (is26 == -1) { 89 is26 = (stat("/sys/bus/pci", &ignored) < 0) ? 0 : 1; 90 } 91 92 if (fd == -1 || (write && (!is_write)) || (last_dev != dev)) { 93 if (fd != -1) { 94 close(fd); 95 fd = -1; 96 } 97 98 if (is26) { 99 sprintf(file,"/sys/bus/pci/devices/%04u:%02x:%02x.%01x/config", 100 dev->domain, dev->bus, dev->dev, dev->func); 101 } else { 102 if (dev->domain == 0) { 103 sprintf(file,"/proc/bus/pci/%02x", dev->bus); 104 if (stat(file, &ignored) < 0) { 105 sprintf(file, "/proc/bus/pci/0000:%02x/%02x.%1x", 106 dev->bus, dev->dev, dev->func); 107 } else { 108 sprintf(file, "/proc/bus/pci/%02x/%02x.%1x", 109 dev->bus, dev->dev, dev->func); 110 } 111 } else { 112 sprintf(file,"/proc/bus/pci/%02x%02x", dev->domain, dev->bus); 113 if (stat(file, &ignored) < 0) { 114 sprintf(file, "/proc/bus/pci/%04x:%04x/%02x.%1x", 115 dev->domain, dev->bus, dev->dev, dev->func); 116 } else { 117 sprintf(file, "/proc/bus/pci/%02x%02x/%02x.%1x", 118 dev->domain, dev->bus, dev->dev, dev->func); 119 } 120 } 121 } 122 123 if (write) { 124 fd = open(file,O_RDWR); 125 if (fd != -1) is_write = TRUE; 126 } else { 127 switch (is_write) { 128 case TRUE: 129 fd = open(file,O_RDWR); 130 if (fd > -1) 131 break; 132 default: 133 fd = open(file,O_RDONLY); 134 is_write = FALSE; 135 } 136 } 137 138 last_dev = dev; 139 } 140 141 return fd; 142} 143 144/* 145 * Compiling the following simply requires the presence of <linux/pci.c>. 146 * Actually running this is another matter altogether... 147 * 148 * This scheme requires that the kernel allow mmap()'ing of a host bridge's I/O 149 * and memory spaces through its /proc/bus/pci/BUS/DFN entry. Which one is 150 * determined by a prior ioctl(). 151 * 152 * For the sparc64 port, this means 2.4.12 or later. For ppc, this 153 * functionality is almost, but not quite there yet. Alpha and other kernel 154 * ports to multi-domain architectures still need to implement this. 155 * 156 * This scheme is also predicated on the use of an IOADDRESS compatible type to 157 * designate I/O addresses. Although IOADDRESS is defined as an unsigned 158 * integral type, it is actually the virtual address of, i.e. a pointer to, the 159 * I/O port to access. And so, the inX/outX macros in "compiler.h" need to be 160 * #define'd appropriately (as is done on SPARC's). 161 * 162 * Another requirement to port this scheme to another multi-domain architecture 163 * is to add the appropriate entries in the pciControllerSizes array below. 164 * 165 * TO DO: Address the deleterious reaction some host bridges have to master 166 * aborts. This is already done for secondary PCI buses, but not yet 167 * for accesses to primary buses (except for the SPARC port, where 168 * master aborts are avoided during PCI scans). 169 */ 170 171#include <linux/pci.h> 172 173#ifndef PCIIOC_BASE /* Ioctls for /proc/bus/pci/X/Y nodes. */ 174#define PCIIOC_BASE ('P' << 24 | 'C' << 16 | 'I' << 8) 175 176/* Get controller for PCI device. */ 177#define PCIIOC_CONTROLLER (PCIIOC_BASE | 0x00) 178/* Set mmap state to I/O space. */ 179#define PCIIOC_MMAP_IS_IO (PCIIOC_BASE | 0x01) 180/* Set mmap state to MEM space. */ 181#define PCIIOC_MMAP_IS_MEM (PCIIOC_BASE | 0x02) 182/* Enable/disable write-combining. */ 183#define PCIIOC_WRITE_COMBINE (PCIIOC_BASE | 0x03) 184 185#endif 186 187/* This probably shouldn't be Linux-specific */ 188static struct pci_device * 189get_parent_bridge(struct pci_device *dev) 190{ 191 struct pci_id_match bridge_match = { 192 PCI_MATCH_ANY, PCI_MATCH_ANY, PCI_MATCH_ANY, PCI_MATCH_ANY, 193 (PCI_CLASS_BRIDGE << 16) | (PCI_SUBCLASS_BRIDGE_PCI << 8), 194 0 195 }; 196 struct pci_device *bridge; 197 struct pci_device_iterator *iter; 198 199 if (dev == NULL) { 200 return NULL; 201 } 202 203 iter = pci_id_match_iterator_create(& bridge_match); 204 if (iter == NULL) { 205 return NULL; 206 } 207 208 while ((bridge = pci_device_next(iter)) != NULL) { 209 if (bridge->domain == dev->domain) { 210 const struct pci_bridge_info *info = 211 pci_device_get_bridge_info(bridge); 212 213 if (info != NULL) { 214 if (info->secondary_bus == dev->bus) { 215 break; 216 } 217 } 218 } 219 } 220 221 pci_iterator_destroy(iter); 222 223 return bridge; 224} 225 226/* 227 * This is ugly, but until I can extract this information from the kernel, 228 * it'll have to do. The default I/O space size is 64K, and 4G for memory. 229 * Anything else needs to go in this table. (PowerPC folk take note.) 230 * 231 * Note that Linux/SPARC userland is 32-bit, so 4G overflows to zero here. 232 * 233 * Please keep this table in ascending vendor/device order. 234 */ 235static const struct pciSizes { 236 unsigned short vendor, device; 237 unsigned long io_size, mem_size; 238} pciControllerSizes[] = { 239 { 240 PCI_VENDOR_SUN, PCI_CHIP_PSYCHO, 241 1U << 16, 1U << 31 242 }, 243 { 244 PCI_VENDOR_SUN, PCI_CHIP_SCHIZO, 245 1U << 24, 1U << 31 /* ??? */ 246 }, 247 { 248 PCI_VENDOR_SUN, PCI_CHIP_SABRE, 249 1U << 24, (unsigned long)(1ULL << 32) 250 }, 251 { 252 PCI_VENDOR_SUN, PCI_CHIP_HUMMINGBIRD, 253 1U << 24, (unsigned long)(1ULL << 32) 254 } 255}; 256#define NUM_SIZES (sizeof(pciControllerSizes) / sizeof(pciControllerSizes[0])) 257 258static const struct pciSizes * 259linuxGetSizesStruct(const struct pci_device *dev) 260{ 261 static const struct pciSizes default_size = { 262 0, 0, 1U << 16, (unsigned long)(1ULL << 32) 263 }; 264 int i; 265 266 /* Look up vendor/device */ 267 if (dev != NULL) { 268 for (i = 0; i < NUM_SIZES; i++) { 269 if ((dev->vendor_id == pciControllerSizes[i].vendor) 270 && (dev->device_id == pciControllerSizes[i].device)) { 271 return & pciControllerSizes[i]; 272 } 273 } 274 } 275 276 /* Default to 64KB I/O and 4GB memory. */ 277 return & default_size; 278} 279 280static __inline__ unsigned long 281linuxGetIOSize(const struct pci_device *dev) 282{ 283 const struct pciSizes * const sizes = linuxGetSizesStruct(dev); 284 return sizes->io_size; 285} 286 287static pointer 288linuxMapPci(int ScreenNum, int Flags, struct pci_device *dev, 289 ADDRESS Base, unsigned long Size, int mmap_ioctl) 290{ 291 /* Align to page boundary */ 292 const ADDRESS realBase = Base & ~(getpagesize() - 1); 293 const ADDRESS Offset = Base - realBase; 294 295 do { 296 unsigned char *result; 297 int fd, mmapflags, prot; 298 299 xf86InitVidMem(); 300 301 /* If dev is NULL, linuxPciOpenFile will return -1, and this routine 302 * will fail gracefully. 303 */ 304 prot = ((Flags & VIDMEM_READONLY) == 0); 305 if (((fd = linuxPciOpenFile(dev, prot)) < 0) || 306 (ioctl(fd, mmap_ioctl, 0) < 0)) 307 break; 308 309/* Note: IA-64 doesn't compile this and doesn't need to */ 310#ifdef __ia64__ 311 312# ifndef MAP_WRITECOMBINED 313# define MAP_WRITECOMBINED 0x00010000 314# endif 315# ifndef MAP_NONCACHED 316# define MAP_NONCACHED 0x00020000 317# endif 318 319 if (Flags & VIDMEM_FRAMEBUFFER) 320 mmapflags = MAP_SHARED | MAP_WRITECOMBINED; 321 else 322 mmapflags = MAP_SHARED | MAP_NONCACHED; 323 324#else /* !__ia64__ */ 325 326 mmapflags = (Flags & VIDMEM_FRAMEBUFFER) / VIDMEM_FRAMEBUFFER; 327 328 if (ioctl(fd, PCIIOC_WRITE_COMBINE, mmapflags) < 0) 329 break; 330 331 mmapflags = MAP_SHARED; 332 333#endif /* ?__ia64__ */ 334 335 336 if (Flags & VIDMEM_READONLY) 337 prot = PROT_READ; 338 else 339 prot = PROT_READ | PROT_WRITE; 340 341 result = mmap(NULL, Size + Offset, prot, mmapflags, fd, realBase); 342 343 if (!result || ((pointer)result == MAP_FAILED)) 344 return NULL; 345 346 xf86MakeNewMapping(ScreenNum, Flags, realBase, Size + Offset, result); 347 348 return result + Offset; 349 } while (0); 350 351 if (mmap_ioctl == PCIIOC_MMAP_IS_MEM) 352 return xf86MapVidMem(ScreenNum, Flags, Base, Size); 353 354 return NULL; 355} 356 357static int 358linuxOpenLegacy(struct pci_device *dev, char *name) 359{ 360 static const char PREFIX[] = "/sys/class/pci_bus/%04x:%02x/%s"; 361 char path[sizeof(PREFIX) + 10]; 362 int fd = -1; 363 364 while (dev != NULL) { 365 snprintf(path, sizeof(path) - 1, PREFIX, dev->domain, dev->bus, name); 366 fd = open(path, O_RDWR); 367 if (fd >= 0) { 368 return fd; 369 } 370 371 dev = get_parent_bridge(dev); 372 } 373 374 return fd; 375} 376 377/* 378 * xf86MapDomainMemory - memory map PCI domain memory 379 * 380 * This routine maps the memory region in the domain specified by Tag and 381 * returns a pointer to it. The pointer is saved for future use if it's in 382 * the legacy ISA memory space (memory in a domain between 0 and 1MB). 383 */ 384pointer 385xf86MapDomainMemory(int ScreenNum, int Flags, struct pci_device *dev, 386 ADDRESS Base, unsigned long Size) 387{ 388 int fd = -1; 389 pointer addr; 390 391 /* 392 * We use /proc/bus/pci on non-legacy addresses or if the Linux sysfs 393 * legacy_mem interface is unavailable. 394 */ 395 if ((Base > 1024*1024) || ((fd = linuxOpenLegacy(dev, "legacy_mem")) < 0)) 396 return linuxMapPci(ScreenNum, Flags, dev, Base, Size, 397 PCIIOC_MMAP_IS_MEM); 398 else 399 addr = mmap(NULL, Size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, Base); 400 401 if (fd >= 0) 402 close(fd); 403 if (addr == NULL || addr == MAP_FAILED) { 404 perror("mmap failure"); 405 FatalError("xf86MapDomainMem(): mmap() failure\n"); 406 } 407 return addr; 408} 409 410/** 411 * Map I/O space in this domain 412 * 413 * Each domain has a legacy ISA I/O space. This routine will try to 414 * map it using the Linux sysfs legacy_io interface. If that fails, 415 * it'll fall back to using /proc/bus/pci. 416 * 417 * If the legacy_io interface \b does exist, the file descriptor (\c fd below) 418 * will be saved in the \c DomainMmappedIO array in the upper bits of the 419 * pointer. Callers will do I/O with small port numbers (<64k values), so 420 * the platform I/O code can extract the port number and the \c fd, \c lseek 421 * to the port number in the legacy_io file, and issue the read or write. 422 * 423 * This has no means of returning failure, so all errors are fatal 424 */ 425IOADDRESS 426xf86MapLegacyIO(struct pci_device *dev) 427{ 428 const int domain = dev->domain; 429 struct pci_device *bridge = get_parent_bridge(dev); 430 int fd; 431 432 if (domain >= MAX_DOMAINS) 433 FatalError("xf86MapLegacyIO(): domain out of range\n"); 434 435 if (DomainMmappedIO[domain] == NULL) { 436 /* Permanently map all of I/O space */ 437 fd = linuxOpenLegacy(bridge, "legacy_io"); 438 if (fd < 0) { 439 DomainMmappedIO[domain] = linuxMapPci(-1, VIDMEM_MMIO, bridge, 440 0, linuxGetIOSize(bridge), 441 PCIIOC_MMAP_IS_IO); 442 } 443 else { /* legacy_io file exists, encode fd */ 444 DomainMmappedIO[domain] = (pointer)(intptr_t)(fd << 24); 445 } 446 } 447 448 return (IOADDRESS)DomainMmappedIO[domain]; 449} 450 451