#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 ADDRESS linuxTransAddrBusToHost(PCITAG tag, PciAddrType type, ADDRESS addr);
#if defined(__powerpc__)
static ADDRESS linuxPpcBusAddrToHostAddr(PCITAG, PciAddrType, ADDRESS);
#endif
static pciBusFuncs_t linuxFuncs0 = {
#if defined(__powerpc__)
linuxPpcBusAddrToHostAddr,
#else
#if defined(__sparc__)
pciAddrNOOP,
#else
linuxTransAddrBusToHost,
#endif
#endif
};
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)
{
struct stat st;
memset(DomainMmappedIO, 0, sizeof(DomainMmappedIO));
if (-1 == stat("/proc/bus/pci", &st)) {
return;
}
pciBusFuncs = &linuxFuncs0;
}
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;
}
ADDRESS linuxTransAddrBusToHost(PCITAG tag, PciAddrType type, ADDRESS addr)
{
ADDRESS ret = xf86GetOSOffsetFromPCI(tag, PCI_MEM|PCI_IO, addr);
if (ret)
return ret;
return addr;
}
#if defined(__powerpc__)
#ifndef __NR_pciconfig_iobase
#define __NR_pciconfig_iobase 200
#endif
static ADDRESS
linuxPpcBusAddrToHostAddr(PCITAG tag, PciAddrType type, ADDRESS addr)
{
if (type == PCI_MEM)
{
ADDRESS membase = syscall(__NR_pciconfig_iobase, 1,
PCI_BUS_FROM_TAG(tag), PCI_DFN_FROM_TAG(tag));
return (addr + membase);
}
else if (type == PCI_IO)
{
ADDRESS iobase = syscall(__NR_pciconfig_iobase, 2,
PCI_BUS_FROM_TAG(tag), PCI_DFN_FROM_TAG(tag));
return (addr + iobase);
}
else return addr;
}
#endif
#include <linux/pci.h>
#ifndef PCIIOC_BASE
#define PCIIOC_BASE ('P' << 24 | 'C' << 16 | 'I' << 8)
#define PCIIOC_CONTROLLER (PCIIOC_BASE | 0x00)
#define PCIIOC_MMAP_IS_IO (PCIIOC_BASE | 0x01)
#define PCIIOC_MMAP_IS_MEM (PCIIOC_BASE | 0x02)
#define PCIIOC_WRITE_COMBINE (PCIIOC_BASE | 0x03)
#endif
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;
}
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;
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];
}
}
}
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)
{
const ADDRESS realBase = Base & ~(getpagesize() - 1);
const ADDRESS Offset = Base - realBase;
do {
unsigned char *result;
int fd, mmapflags, prot;
xf86InitVidMem();
prot = ((Flags & VIDMEM_READONLY) == 0);
if (((fd = linuxPciOpenFile(dev, prot)) < 0) ||
(ioctl(fd, mmap_ioctl, 0) < 0))
break;
#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
mmapflags = (Flags & VIDMEM_FRAMEBUFFER) / VIDMEM_FRAMEBUFFER;
if (ioctl(fd, PCIIOC_WRITE_COMBINE, mmapflags) < 0)
break;
mmapflags = MAP_SHARED;
#endif
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;
}
_X_EXPORT pointer
xf86MapDomainMemory(int ScreenNum, int Flags, struct pci_device *dev,
ADDRESS Base, unsigned long Size)
{
int fd = -1;
pointer addr;
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;
}
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) {
fd = linuxOpenLegacy(bridge, "legacy_io");
if (fd < 0) {
DomainMmappedIO[domain] = linuxMapPci(-1, VIDMEM_MMIO, bridge,
0, linuxGetIOSize(bridge),
PCIIOC_MMAP_IS_IO);
}
else {
DomainMmappedIO[domain] = (pointer)(fd << 24);
}
}
return (IOADDRESS)DomainMmappedIO[domain];
}
resPtr
xf86AccResFromOS(resPtr pRes)
{
struct pci_device *dev;
struct pci_device_iterator *iter;
resRange range;
iter = pci_id_match_iterator_create(& match_host_bridge);
while ((dev = pci_device_next(iter)) != NULL) {
const int domain = dev->domain;
const struct pciSizes * const sizes = linuxGetSizesStruct(dev);
RANGE(range, 0x00000000u, 0x0009ffffu,
RANGE_TYPE(ResExcMemBlock, domain));
pRes = xf86AddResToList(pRes, &range, -1);
RANGE(range, 0x000c0000u, 0x000effffu,
RANGE_TYPE(ResExcMemBlock, domain));
pRes = xf86AddResToList(pRes, &range, -1);
RANGE(range, 0x000f0000u, 0x000fffffu,
RANGE_TYPE(ResExcMemBlock, domain));
pRes = xf86AddResToList(pRes, &range, -1);
RANGE(range, (ADDRESS)(sizes->mem_size - 1),
(ADDRESS)(sizes->mem_size - 1),
RANGE_TYPE(ResExcMemBlock, domain));
pRes = xf86AddResToList(pRes, &range, -1);
RANGE(range, 0x00000000u, 0x00000000u,
RANGE_TYPE(ResExcIoBlock, domain));
pRes = xf86AddResToList(pRes, &range, -1);
RANGE(range, (IOADDRESS)(sizes->io_size - 1),
(IOADDRESS)(sizes->io_size - 1),
RANGE_TYPE(ResExcIoBlock, domain));
pRes = xf86AddResToList(pRes, &range, -1);
}
pci_iterator_destroy(iter);
return pRes;
}