initial commit
[profile/ivi/xorg-x11-server.git] / hw / xfree86 / os-support / bus / linuxPci.c
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 "compiler.h"
53 #include "xf86.h"
54 #include "xf86Priv.h"
55 #include "xf86_OSlib.h"
56 #include "Pci.h"
57 #include <dirent.h>
58
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),
62     0x0000ffff00, 0
63 };
64
65 #define MAX_DOMAINS 257
66 static pointer DomainMmappedIO[MAX_DOMAINS];
67
68 void
69 linuxPciInit(void)
70 {
71     memset(DomainMmappedIO, 0, sizeof(DomainMmappedIO));
72 }
73
74 /**
75  * \bug
76  * The generation of the procfs file name for the domain != 0 case may not be 
77  * correct.
78  */
79 static int
80 linuxPciOpenFile(struct pci_device *dev, Bool write)
81 {
82     static struct pci_device *last_dev = NULL;
83     static int  fd = -1,is_write = 0;
84     char                file[64];
85     struct stat ignored;
86     static int is26 = -1;
87
88     if (dev == NULL) {
89         return -1;
90     }
91
92     if (is26 == -1) {
93         is26 = (stat("/sys/bus/pci", &ignored) < 0) ? 0 : 1;
94     }
95         
96     if (fd == -1 || (write && (!is_write)) || (last_dev != dev)) {
97         if (fd != -1) {
98             close(fd);
99             fd = -1;
100         }
101
102         if (is26) {
103             sprintf(file,"/sys/bus/pci/devices/%04u:%02x:%02x.%01x/config",
104                     dev->domain, dev->bus, dev->dev, dev->func);
105         } else {
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);
111                 } else {
112                     sprintf(file, "/proc/bus/pci/%02x/%02x.%1x",
113                             dev->bus, dev->dev, dev->func);
114                 }
115             } else {
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);
120                 } else {
121                     sprintf(file, "/proc/bus/pci/%02x%02x/%02x.%1x",
122                             dev->domain, dev->bus, dev->dev, dev->func);
123                 }
124             }
125         }
126
127         if (write) {
128             fd = open(file,O_RDWR);
129             if (fd != -1) is_write = TRUE;
130         } else {
131             switch (is_write) {
132             case TRUE:
133                 fd = open(file,O_RDWR);
134                 if (fd > -1)
135                     break;
136             default:
137                 fd = open(file,O_RDONLY);
138                 is_write = FALSE;
139             }
140         }
141
142         last_dev = dev;
143     }
144
145     return fd;
146 }
147
148 /*
149  * Compiling the following simply requires the presence of <linux/pci.c>.
150  * Actually running this is another matter altogether...
151  *
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().
155  *
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.
159  *
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).
165  *
166  * Another requirement to port this scheme to another multi-domain architecture
167  * is to add the appropriate entries in the pciControllerSizes array below.
168  *
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).
173  */
174
175 #include <linux/pci.h>
176
177 #ifndef PCIIOC_BASE             /* Ioctls for /proc/bus/pci/X/Y nodes. */
178 #define PCIIOC_BASE             ('P' << 24 | 'C' << 16 | 'I' << 8)
179
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)
188
189 #endif
190
191 /* This probably shouldn't be Linux-specific */
192 static struct pci_device *
193 get_parent_bridge(struct pci_device *dev)
194 {
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),
198         0
199     };
200     struct pci_device *bridge;
201     struct pci_device_iterator *iter;
202
203     if (dev == NULL) {
204         return NULL;
205     }
206
207     iter = pci_id_match_iterator_create(& bridge_match);
208     if (iter == NULL) {
209         return NULL;
210     }
211
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);
216
217             if (info != NULL) {
218                 if (info->secondary_bus == dev->bus) {
219                     break;
220                 }
221             }
222         }
223     }
224
225     pci_iterator_destroy(iter);
226
227     return bridge;
228 }
229
230 /*
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.)
234  *
235  * Note that Linux/SPARC userland is 32-bit, so 4G overflows to zero here.
236  *
237  * Please keep this table in ascending vendor/device order.
238  */
239 static const struct pciSizes {
240     unsigned short vendor, device;
241     unsigned long io_size, mem_size;
242 } pciControllerSizes[] = {
243     {
244         PCI_VENDOR_SUN, PCI_CHIP_PSYCHO,
245         1U << 16, 1U << 31
246     },
247     {
248         PCI_VENDOR_SUN, PCI_CHIP_SCHIZO,
249         1U << 24, 1U << 31      /* ??? */
250     },
251     {
252         PCI_VENDOR_SUN, PCI_CHIP_SABRE,
253         1U << 24, (unsigned long)(1ULL << 32)
254     },
255     {
256         PCI_VENDOR_SUN, PCI_CHIP_HUMMINGBIRD,
257         1U << 24, (unsigned long)(1ULL << 32)
258     }
259 };
260 #define NUM_SIZES (sizeof(pciControllerSizes) / sizeof(pciControllerSizes[0]))
261
262 static const struct pciSizes *
263 linuxGetSizesStruct(const struct pci_device *dev)
264 {
265     static const struct pciSizes default_size = {
266         0, 0, 1U << 16, (unsigned long)(1ULL << 32)
267     };
268     int          i;
269
270     /* Look up vendor/device */
271     if (dev != NULL) {
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];
276             }
277         }
278     }
279
280     /* Default to 64KB I/O and 4GB memory. */
281     return & default_size;
282 }
283
284 static __inline__ unsigned long
285 linuxGetIOSize(const struct pci_device *dev)
286 {
287     const struct pciSizes * const sizes = linuxGetSizesStruct(dev);
288     return sizes->io_size;
289 }
290
291 static pointer
292 linuxMapPci(int ScreenNum, int Flags, struct pci_device *dev,
293             ADDRESS Base, unsigned long Size, int mmap_ioctl)
294 {
295     /* Align to page boundary */
296     const ADDRESS realBase = Base & ~(getpagesize() - 1);
297     const ADDRESS Offset = Base - realBase;
298
299     do {
300         unsigned char *result;
301         int fd, mmapflags, prot;
302
303         xf86InitVidMem();
304
305         /* If dev is NULL, linuxPciOpenFile will return -1, and this routine
306          * will fail gracefully.
307          */
308         prot = ((Flags & VIDMEM_READONLY) == 0);
309         if (((fd = linuxPciOpenFile(dev, prot)) < 0) ||
310             (ioctl(fd, mmap_ioctl, 0) < 0))
311             break;
312
313 /* Note:  IA-64 doesn't compile this and doesn't need to */
314 #ifdef __ia64__
315
316 # ifndef  MAP_WRITECOMBINED
317 #  define MAP_WRITECOMBINED 0x00010000
318 # endif
319 # ifndef  MAP_NONCACHED
320 #  define MAP_NONCACHED     0x00020000
321 # endif
322
323         if (Flags & VIDMEM_FRAMEBUFFER)
324             mmapflags = MAP_SHARED | MAP_WRITECOMBINED;
325         else
326             mmapflags = MAP_SHARED | MAP_NONCACHED;
327
328 #else /* !__ia64__ */
329
330         mmapflags = (Flags & VIDMEM_FRAMEBUFFER) / VIDMEM_FRAMEBUFFER;
331
332         if (ioctl(fd, PCIIOC_WRITE_COMBINE, mmapflags) < 0)
333             break;
334
335         mmapflags = MAP_SHARED;
336
337 #endif /* ?__ia64__ */
338
339
340         if (Flags & VIDMEM_READONLY)
341             prot = PROT_READ;
342         else
343             prot = PROT_READ | PROT_WRITE;
344
345         result = mmap(NULL, Size + Offset, prot, mmapflags, fd, realBase);
346
347         if (!result || ((pointer)result == MAP_FAILED))
348             return NULL;
349
350         xf86MakeNewMapping(ScreenNum, Flags, realBase, Size + Offset, result);
351
352         return result + Offset;
353     } while (0);
354
355     if (mmap_ioctl == PCIIOC_MMAP_IS_MEM)
356         return xf86MapVidMem(ScreenNum, Flags, Base, Size);
357
358     return NULL;
359 }
360
361 static int
362 linuxOpenLegacy(struct pci_device *dev, char *name)
363 {
364     static const char PREFIX[] = "/sys/class/pci_bus/%04x:%02x/%s";
365     char path[sizeof(PREFIX) + 10];
366     int fd = -1;
367
368     while (dev != NULL) {
369         snprintf(path, sizeof(path) - 1, PREFIX, dev->domain, dev->bus, name);
370         fd = open(path, O_RDWR);
371         if (fd >= 0) {
372             return fd;
373         }
374
375         dev = get_parent_bridge(dev);
376     }
377
378     return fd;
379 }
380
381 /*
382  * xf86MapDomainMemory - memory map PCI domain memory
383  *
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).
387  */
388 pointer
389 xf86MapDomainMemory(int ScreenNum, int Flags, struct pci_device *dev,
390                     ADDRESS Base, unsigned long Size)
391 {
392     int fd = -1;
393     pointer addr;
394
395     /*
396      * We use /proc/bus/pci on non-legacy addresses or if the Linux sysfs
397      * legacy_mem interface is unavailable.
398      */
399     if ((Base > 1024*1024) || ((fd = linuxOpenLegacy(dev, "legacy_mem")) < 0))
400         return linuxMapPci(ScreenNum, Flags, dev, Base, Size,
401                            PCIIOC_MMAP_IS_MEM);
402     else
403         addr = mmap(NULL, Size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, Base);
404
405     if (fd >= 0)
406         close(fd);
407     if (addr == NULL || addr == MAP_FAILED) {
408         perror("mmap failure");
409         FatalError("xf86MapDomainMem():  mmap() failure\n");
410     }
411     return addr;
412 }
413
414 /**
415  * Map I/O space in this domain
416  *
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.
420  *
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.
426  *
427  * This has no means of returning failure, so all errors are fatal
428  */
429 IOADDRESS
430 xf86MapLegacyIO(struct pci_device *dev)
431 {
432     const int domain = dev->domain;
433     struct pci_device *bridge = get_parent_bridge(dev);
434     int fd;
435
436     if (domain >= MAX_DOMAINS)
437         FatalError("xf86MapLegacyIO():  domain out of range\n");
438
439     if (DomainMmappedIO[domain] == NULL) {
440         /* Permanently map all of I/O space */
441         fd = linuxOpenLegacy(bridge, "legacy_io");
442         if (fd < 0) {
443             DomainMmappedIO[domain] = linuxMapPci(-1, VIDMEM_MMIO, bridge,
444                                                   0, linuxGetIOSize(bridge),
445                                                   PCIIOC_MMAP_IS_IO);
446         }
447         else { /* legacy_io file exists, encode fd */
448             DomainMmappedIO[domain] = (pointer)(intptr_t)(fd << 24);
449         }
450     }
451
452     return (IOADDRESS)DomainMmappedIO[domain];
453 }
454