/* * Copyright (C) 2001-2003 The XFree86 Project, Inc. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * Except as contained in this notice, the name of the XFree86 Project shall * not be used in advertising or otherwise to promote the sale, use or other * dealings in this Software without prior written authorization from the * XFree86 Project. */ #ifdef HAVE_XORG_CONFIG_H #include #endif #include "xf86.h" #include "xf86Priv.h" #include "xf86_OSlib.h" #include "Pci.h" #include "xf86sbusBus.h" #if defined(sun) extern char *apertureDevName; static int apertureFd = -1; /* * A version of xf86MapVidMem() that allows for 64-bit displacements (but not * sizes). Areas thus mapped can be unmapped by xf86UnMapVidMem(). */ static pointer sparcMapAperture(int iScreen, int Flags, unsigned long long Base, unsigned long Size) { pointer result; static int lastFlags = 0; /* Assume both Base & Size are multiples of the page size */ if ((apertureFd < 0) || (Flags != lastFlags)) { if (apertureFd >= 0) close(apertureFd); lastFlags = Flags; apertureFd = open(apertureDevName, (Flags & VIDMEM_READONLY) ? O_RDONLY : O_RDWR); if (apertureFd < 0) FatalError("sparcMapAperture: open failure: %s\n", strerror(errno)); } result = mmap(NULL, Size, (Flags & VIDMEM_READONLY) ? PROT_READ : (PROT_READ | PROT_WRITE), MAP_SHARED, apertureFd, (off_t)Base); if (result == MAP_FAILED) FatalError("sparcMapAperture: mmap failure: %s\n", strerror(errno)); return result; } /* * Platform-specific bus privates. */ typedef struct _sparcDomainRec { unsigned long long io_addr, io_size; unsigned long long mem_addr, mem_size; pointer pci, io; int bus_min, bus_max; unsigned char dfn_mask[256 / 8]; } sparcDomainRec, *sparcDomainPtr; #define SetBitInMap(bit, map) \ do { \ int _bit = (bit); \ (map)[_bit >> 3] |= 1 << (_bit & 7); \ } while (0) #define IsBitSetInMap(bit, map) \ ((map)[(bit) >> 3] & (1 << ((bit) & 7))) /* * Domain 0 is reserved for the one that represents the system as a whole, i.e. * the one without any resource relocations. */ #define MAX_DOMAINS (MAX_PCI_BUSES / 256) static sparcDomainPtr xf86DomainInfo[MAX_DOMAINS]; static int pciNumDomains = 1; /* Variables that are assigned this must be declared volatile */ #define PciReg(base, tag, off, type) \ *(volatile type *)(pointer)((char *)(base) + \ (PCI_TAG_NO_DOMAIN(tag) | (off))) /* Generic SPARC PCI access functions */ static CARD32 sparcPciCfgRead32(PCITAG tag, int off) { pciBusInfo_t *pBusInfo; sparcDomainPtr pDomain; volatile CARD32 result = (CARD32)(-1); /* Must be volatile */ int bus; if ((off >= 0) && (off <= 252) && !(off & 3) && ((bus = PCI_BUS_FROM_TAG(tag)) < pciNumBuses) && (pBusInfo = pciBusInfo[bus]) && (pDomain = pBusInfo->pciBusPriv) && (bus >= pDomain->bus_min) && (bus < pDomain->bus_max) && ((bus > pDomain->bus_min) || IsBitSetInMap(PCI_DFN_FROM_TAG(tag), pDomain->dfn_mask))) { result = PciReg(pDomain->pci, tag, off, CARD32); result = PCI_CPU(result); } return result; } static void sparcPciCfgWrite32(PCITAG tag, int off, CARD32 val) { pciBusInfo_t *pBusInfo; sparcDomainPtr pDomain; int bus; if ((off < 0) || (off > 252) || (off & 3) || ((bus = PCI_BUS_FROM_TAG(tag)) >= pciNumBuses) || !(pBusInfo = pciBusInfo[bus]) || !(pDomain = pBusInfo->pciBusPriv) || (bus < pDomain->bus_min) || (bus >= pDomain->bus_max) || ((bus == pDomain->bus_min) && !IsBitSetInMap(PCI_DFN_FROM_TAG(tag), pDomain->dfn_mask))) return; val = PCI_CPU(val); PciReg(pDomain->pci, tag, off, CARD32) = val; } static void sparcPciCfgSetBits32(PCITAG tag, int off, CARD32 mask, CARD32 bits) { CARD32 PciVal; PciVal = sparcPciCfgRead32(tag, off); PciVal &= ~mask; PciVal |= bits; sparcPciCfgWrite32(tag, off, PciVal); } static pciBusFuncs_t sparcPCIFunctions = { sparcPciCfgRead32, sparcPciCfgWrite32, sparcPciCfgSetBits32, pciAddrNOOP, pciAddrNOOP }; /* * Sabre-specific versions of the above because of its peculiar access size * requirements. */ static CARD32 sabrePciCfgRead32(PCITAG tag, int off) { pciBusInfo_t *pBusInfo; sparcDomainPtr pDomain; volatile CARD32 result; /* Must be volatile */ int bus; if (PCI_BDEV_FROM_TAG(tag)) return sparcPciCfgRead32(tag, off); if (PCI_FUNC_FROM_TAG(tag) || (off < 0) || (off > 252) || (off & 3) || ((bus = PCI_BUS_FROM_TAG(tag)) >= pciNumBuses) || !(pBusInfo = pciBusInfo[bus]) || !(pDomain = pBusInfo->pciBusPriv) || (bus != pDomain->bus_min)) return (CARD32)(-1); if (off < 8) { result = (PciReg(pDomain->pci, tag, off, CARD16) << 16) | PciReg(pDomain->pci, tag, off + 2, CARD16); return PCI_CPU(result); } result = (PciReg(pDomain->pci, tag, off + 3, CARD8) << 24) | (PciReg(pDomain->pci, tag, off + 2, CARD8) << 16) | (PciReg(pDomain->pci, tag, off + 1, CARD8) << 8) | (PciReg(pDomain->pci, tag, off , CARD8) ); return result; } static void sabrePciCfgWrite32(PCITAG tag, int off, CARD32 val) { pciBusInfo_t *pBusInfo; sparcDomainPtr pDomain; int bus; if (PCI_BDEV_FROM_TAG(tag)) sparcPciCfgWrite32(tag, off, val); else if (!PCI_FUNC_FROM_TAG(tag) && (off >= 0) && (off <= 252) && !(off & 3) && ((bus = PCI_BUS_FROM_TAG(tag)) < pciNumBuses) && (pBusInfo = pciBusInfo[bus]) && (pDomain = pBusInfo->pciBusPriv) && (bus == pDomain->bus_min)) { if (off < 8) { val = PCI_CPU(val); PciReg(pDomain->pci, tag, off , CARD16) = val >> 16; PciReg(pDomain->pci, tag, off + 2, CARD16) = val; } else { PciReg(pDomain->pci, tag, off , CARD8) = val; PciReg(pDomain->pci, tag, off + 1, CARD8) = val >> 8; PciReg(pDomain->pci, tag, off + 2, CARD8) = val >> 16; PciReg(pDomain->pci, tag, off + 3, CARD8) = val >> 24; } } } static void sabrePciCfgSetBits32(PCITAG tag, int off, CARD32 mask, CARD32 bits) { CARD32 PciVal; PciVal = sabrePciCfgRead32(tag, off); PciVal &= ~mask; PciVal |= bits; sabrePciCfgWrite32(tag, off, PciVal); } static pciBusFuncs_t sabrePCIFunctions = { sabrePciCfgRead32, sabrePciCfgWrite32, sabrePciCfgSetBits32, pciAddrNOOP, pciAddrNOOP }; static int pagemask; /* Scan PROM for all PCI host bridges in the system */ void sparcPciInit(void) { int node, node2; if (!xf86LinearVidMem()) return; apertureFd = open(apertureDevName, O_RDWR); if (apertureFd < 0) { xf86Msg(X_ERROR, "sparcPciInit: open failure: %s\n", strerror(errno)); return; } sparcPromInit(); pagemask = xf86getpagesize() - 1; for (node = promGetChild(promRootNode); node; node = promGetSibling(node)) { unsigned long long pci_addr; sparcDomainRec domain; sparcDomainPtr pDomain; pciBusFuncs_p pFunctions; char *prop_val; int prop_len, bus; prop_val = promGetProperty("name", &prop_len); /* Some PROMs include the trailing null; some don't */ if (!prop_val || (prop_len < 3) || (prop_len > 4) || strcmp(prop_val, "pci")) continue; prop_val = promGetProperty("model", &prop_len); if (!prop_val || (prop_len <= 0)) { prop_val = promGetProperty("compatible", &prop_len); if (!prop_val || (prop_len <= 0)) continue; } pFunctions = &sparcPCIFunctions; (void)memset(&domain, 0, sizeof(domain)); if (!strncmp("SUNW,sabre", prop_val, prop_len) || !strncmp("pci108e,a000", prop_val, prop_len) || !strncmp("pci108e,a001", prop_val, prop_len)) { /* * There can only be one "Sabre" bridge in a system. It provides * PCI configuration space, a 24-bit I/O space and a 32-bit memory * space, all three of which are at fixed physical CPU addresses. */ static Bool sabre_seen = FALSE; xf86Msg(X_INFO, "Sabre or Hummingbird PCI host bridge found (\"%s\")\n", prop_val); /* There can only be one Sabre */ if (sabre_seen) continue; sabre_seen = TRUE; /* Get "bus-range" property */ prop_val = promGetProperty("bus-range", &prop_len); if (!prop_val || (prop_len != 8) || (((unsigned int *)prop_val)[0]) || (((unsigned int *)prop_val)[1] >= 256)) continue; pci_addr = 0x01fe01000000ull; domain.io_addr = 0x01fe02000000ull; domain.io_size = 0x000001000000ull; domain.mem_addr = 0x01ff00000000ull; domain.mem_size = 0x000100000000ull; domain.bus_min = 0; /* Always */ domain.bus_max = ((int *)prop_val)[1]; pFunctions = &sabrePCIFunctions; } else if (!strncmp("SUNW,psycho", prop_val, prop_len) || !strncmp("pci108e,8000", prop_val, prop_len)) { /* * A "Psycho" host bridge provides two PCI interfaces, each with * its own 16-bit I/O and 31-bit memory spaces. Both share the * same PCI configuration space. Here, they are assigned separate * domain numbers to prevent unintentional I/O and/or memory * resource conflicts. */ xf86Msg(X_INFO, "Psycho PCI host bridge found (\"%s\")\n", prop_val); /* Get "bus-range" property */ prop_val = promGetProperty("bus-range", &prop_len); if (!prop_val || (prop_len != 8) || (((unsigned int *)prop_val)[1] >= 256) || (((unsigned int *)prop_val)[0] > ((unsigned int *)prop_val)[1])) continue; domain.bus_min = ((int *)prop_val)[0]; domain.bus_max = ((int *)prop_val)[1]; /* Get "ranges" property */ prop_val = promGetProperty("ranges", &prop_len); if (!prop_val || (prop_len != 112) || prop_val[0] || (prop_val[28] != 0x01u) || (prop_val[56] != 0x02u) || (prop_val[84] != 0x03u) || (((unsigned int *)prop_val)[4] != 0x01000000u) || ((unsigned int *)prop_val)[5] || ((unsigned int *)prop_val)[12] || (((unsigned int *)prop_val)[13] != 0x00010000u) || ((unsigned int *)prop_val)[19] || (((unsigned int *)prop_val)[20] != 0x80000000u) || ((((unsigned int *)prop_val)[11] & ~0x00010000u) != 0x02000000u) || (((unsigned int *)prop_val)[18] & ~0x80000000u) || (((unsigned int *)prop_val)[3] != ((unsigned int *)prop_val)[10]) || (((unsigned int *)prop_val)[17] != ((unsigned int *)prop_val)[24]) || (((unsigned int *)prop_val)[18] != ((unsigned int *)prop_val)[25]) || (((unsigned int *)prop_val)[19] != ((unsigned int *)prop_val)[26]) || (((unsigned int *)prop_val)[20] != ((unsigned int *)prop_val)[27])) continue; /* Use memcpy() to avoid alignment issues */ (void)memcpy(&pci_addr, prop_val + 12, sizeof(pci_addr)); (void)memcpy(&domain.io_addr, prop_val + 40, sizeof(domain.io_addr)); (void)memcpy(&domain.mem_addr, prop_val + 68, sizeof(domain.mem_addr)); domain.io_size = 0x000000010000ull; domain.mem_size = 0x000080000000ull; } else if (!strncmp("SUNW,schizo", prop_val, prop_len) || !strncmp("pci108e,8001", prop_val, prop_len)) { /* * I have no docs on the "Schizo", but judging from the Linux * kernel, it also provides two PCI domains. Each PCI * configuration space is the usual 16M in size, followed by a * variable-length I/O space. Each domain also provides a * variable-length memory space. The kernel seems to think the I/O * spaces are 16M long, and the memory spaces, 2G, but these * assumptions are actually only present in source code comments. * Sun has, however, confirmed to me the validity of these * assumptions. */ volatile unsigned long long mem_match, mem_mask, io_match, io_mask; unsigned long Offset; pointer pSchizo; xf86Msg(X_INFO, "Schizo PCI host bridge found (\"%s\")\n", prop_val); /* Get "bus-range" property */ prop_val = promGetProperty("bus-range", &prop_len); if (!prop_val || (prop_len != 8) || (((unsigned int *)prop_val)[1] >= 256) || (((unsigned int *)prop_val)[0] > ((unsigned int *)prop_val)[1])) continue; domain.bus_min = ((int *)prop_val)[0]; domain.bus_max = ((int *)prop_val)[1]; /* Get "reg" property */ prop_val = promGetProperty("reg", &prop_len); if (!prop_val || (prop_len != 48)) continue; /* Temporarily map some of Schizo's registers */ pSchizo = sparcMapAperture(-1, VIDMEM_MMIO, ((unsigned long long *)prop_val)[2] - 0x000000010000ull, 0x00010000ul); /* Determine where PCI config, I/O and memory spaces reside */ if ((((unsigned long long *)prop_val)[0] & 0x000000700000ull) == 0x000000600000ull) Offset = 0x0040; else Offset = 0x0060; mem_match = PciReg(pSchizo, 0, Offset, unsigned long long); mem_mask = PciReg(pSchizo, 0, Offset + 8, unsigned long long); io_match = PciReg(pSchizo, 0, Offset + 16, unsigned long long); io_mask = PciReg(pSchizo, 0, Offset + 24, unsigned long long); /* Unmap Schizo registers */ xf86UnMapVidMem(-1, pSchizo, 0x00010000ul); /* Calculate sizes */ mem_mask = (((mem_mask - 1) ^ mem_mask) >> 1) + 1; io_mask = (((io_mask - 1) ^ io_mask ) >> 1) + 1; if (io_mask <= 0x000001000000ull) /* Nothing left for I/O */ continue; domain.mem_addr = mem_match & ~0x8000000000000000ull; domain.mem_size = mem_mask; pci_addr = io_match & ~0x8000000000000000ull; domain.io_addr = pci_addr + 0x0000000001000000ull; domain.io_size = io_mask - 0x0000000001000000ull; } else { xf86Msg(X_WARNING, "Unknown PCI host bridge: \"%s\"\n", prop_val); continue; } /* Only map as much PCI configuration as we need */ domain.pci = (char *)sparcMapAperture(-1, VIDMEM_MMIO, pci_addr + PCI_MAKE_TAG(domain.bus_min, 0, 0), PCI_MAKE_TAG(domain.bus_max - domain.bus_min + 1, 0, 0)) - PCI_MAKE_TAG(domain.bus_min, 0, 0); /* Allocate a domain record */ pDomain = xnfalloc(sizeof(sparcDomainRec)); *pDomain = domain; /* * Allocate and prime pciBusInfo records. These are allocated one at a * time because those for empty buses are eventually released. */ bus = pDomain->bus_min = PCI_MAKE_BUS(pciNumDomains, domain.bus_min); pciNumBuses = pDomain->bus_max = PCI_MAKE_BUS(pciNumDomains, domain.bus_max) + 1; pciBusInfo[bus] = xnfcalloc(1, sizeof(pciBusInfo_t)); pciBusInfo[bus]->configMech = PCI_CFG_MECH_OTHER; pciBusInfo[bus]->numDevices = 32; pciBusInfo[bus]->funcs = pFunctions; pciBusInfo[bus]->pciBusPriv = pDomain; while (++bus < pciNumBuses) { pciBusInfo[bus] = xnfalloc(sizeof(pciBusInfo_t)); *(pciBusInfo[bus]) = *(pciBusInfo[bus - 1]); pciBusInfo[bus]->funcs = &sparcPCIFunctions; } /* Next domain, please... */ xf86DomainInfo[pciNumDomains++] = pDomain; /* * OK, enough of the straight-forward stuff. Time to deal with some * brokenness... * * The PCI specs require that when a bus transaction remains unclaimed * for too long, the master entity on that bus is to cancel the * transaction it issued or passed on with a master abort. Two * outcomes are possible: * * - the master abort can be treated as an error that is propogated * back through the bus tree to the entity that ultimately originated * the transaction; or * - the transaction can be allowed to complete normally, which means * that writes are ignored and reads return all ones. * * In the first case, if the CPU happens to be at the tail end of the * tree path through one of its host bridges, it will be told there is * a hardware mal-function, despite being generated by software. * * For a software function (be it firmware, OS or userland application) * to determine how a PCI bus tree is populated, it must be able to * detect when master aborts occur. Obviously, PCI discovery is much * simpler when master aborts are allowed to complete normally. * * Unfortunately, a number of non-Intel PCI implementations have chosen * to treat master aborts as severe errors. The net effect is to * cripple PCI discovery algorithms in userland. * * On SPARCs, master aborts cause a number of different behaviours, * including delivering a signal to the userland application, rebooting * the system, "dropping down" to firmware, or, worst of all, bus * lockouts. Even in the first case, the SIGBUS signal that is * eventually generated isn't delivered in a timely enough fashion to * allow an application to reliably detect the master abort that * ultimately caused it. * * This can be somewhat mitigated. On all architectures, master aborts * that occur on secondary buses can be forced to complete normally * because the PCI-to-PCI bridges that serve them are governed by an * industry-wide specification. (This is just another way of saying * that whatever justification there might be for erroring out master * aborts is deemed by the industry as insufficient to generate more * PCI non-compliance than there already is...) * * This leaves us with master aborts that occur on primary buses. * There is no specification for host-to-PCI bridges. Bridges used in * SPARCs can be told to ignore all PCI errors, but not specifically * master aborts. Not only is this too coarse-grained, but * master-aborted read transactions on the primary bus end up returning * garbage rather than all ones. * * I have elected to work around this the only way I can think of doing * so right now. The following scans an additional PROM level and * builds a device/function map for the primary bus. I can only hope * this PROM information represents all devices on the primary bus, * rather than only a subset of them. * * Master aborts are useful in other ways too, that are not addressed * here. These include determining whether or not a domain provides * VGA, or if a PCI device actually implements PCI disablement. * * --- TSI @ UQV 2001.09.19 */ for (node2 = promGetChild(node); node2; node2 = promGetSibling(node2)) { /* Get "reg" property */ prop_val = promGetProperty("reg", &prop_len); if (!prop_val || (prop_len % 20)) continue; /* * It's unnecessary to scan the entire "reg" property, but I'll do * so anyway. */ prop_len /= 20; for (; prop_len--; prop_val += 20) SetBitInMap(PCI_DFN_FROM_TAG(*(PCITAG *)prop_val), pDomain->dfn_mask); } /* Assume the host bridge is device 0, function 0 on its bus */ SetBitInMap(0, pDomain->dfn_mask); } sparcPromClose(); close(apertureFd); apertureFd = -1; } #ifndef INCLUDE_XF86_NO_DOMAIN _X_EXPORT int xf86GetPciDomain(PCITAG Tag) { return PCI_DOM_FROM_TAG(Tag); } _X_EXPORT pointer xf86MapDomainMemory(int ScreenNum, int Flags, PCITAG Tag, ADDRESS Base, unsigned long Size) { sparcDomainPtr pDomain; pointer result; int domain = PCI_DOM_FROM_TAG(Tag); if ((domain <= 0) || (domain >= pciNumDomains) || !(pDomain = xf86DomainInfo[domain]) || (((unsigned long long)Base + (unsigned long long)Size) > pDomain->mem_size)) FatalError("xf86MapDomainMemory() called with invalid parameters.\n"); result = sparcMapAperture(ScreenNum, Flags, pDomain->mem_addr + Base, Size); if (apertureFd >= 0) { close(apertureFd); apertureFd = -1; } return result; } _X_EXPORT IOADDRESS xf86MapDomainIO(int ScreenNum, int Flags, PCITAG Tag, IOADDRESS Base, unsigned long Size) { sparcDomainPtr pDomain; int domain = PCI_DOM_FROM_TAG(Tag); if ((domain <= 0) || (domain >= pciNumDomains) || !(pDomain = xf86DomainInfo[domain]) || (((unsigned long long)Base + (unsigned long long)Size) > pDomain->io_size)) FatalError("xf86MapDomainIO() called with invalid parameters.\n"); /* Permanently map all of I/O space */ if (!pDomain->io) { pDomain->io = sparcMapAperture(ScreenNum, Flags, pDomain->io_addr, pDomain->io_size); if (apertureFd >= 0) { close(apertureFd); apertureFd = -1; } } return (IOADDRESS)pDomain->io + Base; } _X_EXPORT int xf86ReadDomainMemory(PCITAG Tag, ADDRESS Base, int Len, unsigned char *Buf) { unsigned char *ptr, *src; ADDRESS offset; unsigned long size; int len; /* Ensure page boundaries */ offset = Base & ~pagemask; size = ((Base + Len + pagemask) & ~pagemask) - offset; ptr = xf86MapDomainMemory(-1, VIDMEM_READONLY, Tag, offset, size); /* Using memcpy() here hangs the system */ src = ptr + (Base - offset); for (len = Len; len-- > 0;) *Buf++ = *src++; xf86UnMapVidMem(-1, ptr, size); return Len; } resPtr xf86BusAccWindowsFromOS(void) { sparcDomainPtr pDomain; resPtr pRes = NULL; resRange range; int domain; for (domain = 1; domain < pciNumDomains; domain++) { if (!(pDomain = xf86DomainInfo[domain])) continue; RANGE(range, 0, pDomain->mem_size - 1, RANGE_TYPE(ResExcMemBlock, domain)); pRes = xf86AddResToList(pRes, &range, -1); RANGE(range, 0, pDomain->io_size - 1, RANGE_TYPE(ResExcIoBlock, domain)); pRes = xf86AddResToList(pRes, &range, -1); } return pRes; } resPtr xf86PciBusAccWindowsFromOS(void) { sparcDomainPtr pDomain; resPtr pRes = NULL; resRange range; int domain; for (domain = 1; domain < pciNumDomains; domain++) { if (!(pDomain = xf86DomainInfo[domain])) continue; RANGE(range, 0, pDomain->mem_size - 1, RANGE_TYPE(ResExcMemBlock, domain)); pRes = xf86AddResToList(pRes, &range, -1); RANGE(range, 0, pDomain->io_size - 1, RANGE_TYPE(ResExcIoBlock, domain)); pRes = xf86AddResToList(pRes, &range, -1); } return pRes; } resPtr xf86AccResFromOS(resPtr pRes) { sparcDomainPtr pDomain; resRange range; int domain; for (domain = 1; domain < pciNumDomains; domain++) { if (!(pDomain = xf86DomainInfo[domain])) continue; /* * At minimum, the top and bottom resources must be claimed, so that * resources that are (or appear to be) unallocated can be relocated. */ 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, pDomain->mem_size - 1, pDomain->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, pDomain->io_size - 1, pDomain->io_size - 1, RANGE_TYPE(ResExcIoBlock, domain)); pRes = xf86AddResToList(pRes, &range, -1); } return pRes; } #endif /* !INCLUDE_XF86_NO_DOMAIN */ #endif /* defined(sun) */ #if defined(ARCH_PCI_PCI_BRIDGE) /* Definitions specific to Sun's APB P2P bridge (a.k.a. Simba) */ #define APB_IO_ADDRESS_MAP 0xDE #define APB_MEM_ADDRESS_MAP 0xDF /* * Simba's can only occur on bus 0. Furthermore, Simba's must have a non-zero * device/function number because the Sabre interface they must connect to * occupies the 0:0:0 slot. Also, there can be only one Sabre interface in the * system, and therefore, only one Simba function can route any particular * resource. Thus, it is appropriate to use a single set of static variables * to hold the tag of the Simba function routing a VGA resource range at any * one time, and to test these variables for non-zero to determine whether or * not the Sabre would master-abort a VGA access (and kill the system). * * The trick is to determine when it is safe to re-route VGA, because doing so * re-routes much more. */ static PCITAG simbavgaIOTag = 0, simbavgaMemTag = 0; static Bool simbavgaRoutingAllow = TRUE; /* * Scan the bus subtree rooted at 'bus' for a non-display device that might be * decoding the bottom 2 MB of I/O space and/or the bottom 512 MB of memory * space. Reset simbavgaRoutingAllow if such a device is found. * * XXX For now, this is very conservative and should be made less so as the * need arises. */ static void simbaCheckBus(CARD16 pcicommand, int bus) { pciConfigPtr pPCI, *ppPCI = xf86scanpci(0); while ((pPCI = *ppPCI++)) { if (pPCI->busnum < bus) continue; if (pPCI->busnum > bus) break; /* XXX Assume all devices respect PCI disablement */ if (!(pcicommand & pPCI->pci_command)) continue; /* XXX This doesn't deal with mis-advertised classes */ switch (pPCI->pci_base_class) { case PCI_CLASS_PREHISTORIC: if (pPCI->pci_sub_class == PCI_SUBCLASS_PREHISTORIC_VGA) continue; /* Ignore VGA */ break; case PCI_CLASS_DISPLAY: continue; case PCI_CLASS_BRIDGE: switch (pPCI->pci_sub_class) { case PCI_SUBCLASS_BRIDGE_PCI: case PCI_SUBCLASS_BRIDGE_CARDBUS: /* Scan secondary bus */ /* XXX First check bridge routing? */ simbaCheckBus(pcicommand & pPCI->pci_command, PCI_SECONDARY_BUS_EXTRACT(pPCI->pci_pp_bus_register, pPCI->tag)); if (!simbavgaRoutingAllow) return; default: break; } default: break; } /* * XXX We could check the device's bases here, but PCI doesn't limit * the device's decoding to them. */ simbavgaRoutingAllow = FALSE; break; } } static pciConfigPtr simbaVerifyBus(int bus) { pciConfigPtr pPCI; if ((bus < 0) || (bus >= pciNumBuses) || !pciBusInfo[bus] || !(pPCI = pciBusInfo[bus]->bridge) || (pPCI->pci_device_vendor != DEVID(VENDOR_SUN, CHIP_SIMBA))) return NULL; return pPCI; } static CARD16 simbaControlBridge(int bus, CARD16 mask, CARD16 value) { pciConfigPtr pPCI; CARD16 current = 0, tmp; CARD8 iomap, memmap; if ((pPCI = simbaVerifyBus(bus))) { /* * The Simba does not implement VGA enablement as described in the P2P * spec. It does however route I/O and memory in large enough chunks * so that we can determine were VGA resources would be routed * (including ISA VGA I/O aliases). We can allow changes to that * routing only under certain circumstances. */ iomap = pciReadByte(pPCI->tag, APB_IO_ADDRESS_MAP); memmap = pciReadByte(pPCI->tag, APB_MEM_ADDRESS_MAP); if (iomap & memmap & 0x01) { current |= PCI_PCI_BRIDGE_VGA_EN; if ((mask & PCI_PCI_BRIDGE_VGA_EN) && !(value & PCI_PCI_BRIDGE_VGA_EN)) { if (!simbavgaRoutingAllow) { xf86MsgVerb(X_WARNING, 3, "Attempt to disable VGA routing" " through Simba at %x:%x:%x disallowed.\n", pPCI->busnum, pPCI->devnum, pPCI->funcnum); value |= PCI_PCI_BRIDGE_VGA_EN; } else { pciWriteByte(pPCI->tag, APB_IO_ADDRESS_MAP, iomap & ~0x01); pciWriteByte(pPCI->tag, APB_MEM_ADDRESS_MAP, memmap & ~0x01); simbavgaIOTag = simbavgaMemTag = 0; } } } else { if (mask & value & PCI_PCI_BRIDGE_VGA_EN) { if (!simbavgaRoutingAllow) { xf86MsgVerb(X_WARNING, 3, "Attempt to enable VGA routing" " through Simba at %x:%x:%x disallowed.\n", pPCI->busnum, pPCI->devnum, pPCI->funcnum); value &= ~PCI_PCI_BRIDGE_VGA_EN; } else { if (pPCI->tag != simbavgaIOTag) { if (simbavgaIOTag) { tmp = pciReadByte(simbavgaIOTag, APB_IO_ADDRESS_MAP); pciWriteByte(simbavgaIOTag, APB_IO_ADDRESS_MAP, tmp & ~0x01); } pciWriteByte(pPCI->tag, APB_IO_ADDRESS_MAP, iomap | 0x01); simbavgaIOTag = pPCI->tag; } if (pPCI->tag != simbavgaMemTag) { if (simbavgaMemTag) { tmp = pciReadByte(simbavgaMemTag, APB_MEM_ADDRESS_MAP); pciWriteByte(simbavgaMemTag, APB_MEM_ADDRESS_MAP, tmp & ~0x01); } pciWriteByte(pPCI->tag, APB_MEM_ADDRESS_MAP, memmap | 0x01); simbavgaMemTag = pPCI->tag; } } } } /* Move on to master abort failure enablement (as per P2P spec) */ tmp = pciReadWord(pPCI->tag, PCI_PCI_BRIDGE_CONTROL_REG); current |= tmp; if (tmp & PCI_PCI_BRIDGE_MASTER_ABORT_EN) { if ((mask & PCI_PCI_BRIDGE_MASTER_ABORT_EN) && !(value & PCI_PCI_BRIDGE_MASTER_ABORT_EN)) pciWriteWord(pPCI->tag, PCI_PCI_BRIDGE_CONTROL_REG, tmp & ~PCI_PCI_BRIDGE_MASTER_ABORT_EN); } else { if (mask & value & PCI_PCI_BRIDGE_MASTER_ABORT_EN) pciWriteWord(pPCI->tag, PCI_PCI_BRIDGE_CONTROL_REG, tmp | PCI_PCI_BRIDGE_MASTER_ABORT_EN); } /* Insert emulation of other P2P controls here */ } return (current & ~mask) | (value & mask); } static void simbaGetBridgeResources(int bus, pointer *ppIoRes, pointer *ppMemRes, pointer *ppPmemRes) { pciConfigPtr pPCI = simbaVerifyBus(bus); resRange range; int i; if (!pPCI) return; if (ppIoRes) { xf86FreeResList(*ppIoRes); *ppIoRes = NULL; if (pPCI->pci_command & PCI_CMD_IO_ENABLE) { unsigned char iomap = pciReadByte(pPCI->tag, APB_IO_ADDRESS_MAP); if (simbavgaRoutingAllow) iomap |= 0x01; for (i = 0; i < 8; i++) { if (iomap & (1 << i)) { RANGE(range, i << 21, ((i + 1) << 21) - 1, RANGE_TYPE(ResExcIoBlock, xf86GetPciDomain(pPCI->tag))); *ppIoRes = xf86AddResToList(*ppIoRes, &range, -1); } } } } if (ppMemRes) { xf86FreeResList(*ppMemRes); *ppMemRes = NULL; if (pPCI->pci_command & PCI_CMD_MEM_ENABLE) { unsigned char memmap = pciReadByte(pPCI->tag, APB_MEM_ADDRESS_MAP); if (simbavgaRoutingAllow) memmap |= 0x01; for (i = 0; i < 8; i++) { if (memmap & (1 << i)) { RANGE(range, i << 29, ((i + 1) << 29) - 1, RANGE_TYPE(ResExcMemBlock, xf86GetPciDomain(pPCI->tag))); *ppMemRes = xf86AddResToList(*ppMemRes, &range, -1); } } } } if (ppPmemRes) { xf86FreeResList(*ppPmemRes); *ppPmemRes = NULL; } } void ARCH_PCI_PCI_BRIDGE(pciConfigPtr pPCI) { static pciBusFuncs_t simbaBusFuncs; pciBusInfo_t *pBusInfo; CARD16 pcicommand; if (pPCI->pci_device_vendor != DEVID(VENDOR_SUN, CHIP_SIMBA)) return; pBusInfo = pPCI->businfo; simbaBusFuncs = *(pBusInfo->funcs); simbaBusFuncs.pciControlBridge = simbaControlBridge; simbaBusFuncs.pciGetBridgeResources = simbaGetBridgeResources; pBusInfo->funcs = &simbaBusFuncs; if (!simbavgaRoutingAllow) return; pcicommand = 0; if (pciReadByte(pPCI->tag, APB_IO_ADDRESS_MAP) & 0x01) { pcicommand |= PCI_CMD_IO_ENABLE; simbavgaIOTag = pPCI->tag; } if (pciReadByte(pPCI->tag, APB_MEM_ADDRESS_MAP) & 0x01) { pcicommand |= PCI_CMD_MEM_ENABLE; simbavgaMemTag = pPCI->tag; } if (!pcicommand) return; simbaCheckBus(pcicommand, PCI_SECONDARY_BUS_EXTRACT(pPCI->pci_pp_bus_register, pPCI->tag)); } #endif /* defined(ARCH_PCI_PCI_BRIDGE) */