/* * This function is called to emulate the various settings in a P2P or CardBus * bridge's control register using one of a 460GX's SAC host bridges. */ static CARD16 Control460GXBridge(int bus, CARD16 mask, CARD16 value) { pciConfigPtr pPCI; PCITAG tag; CARD16 current = 0; CARD8 tmp; if ((pPCI = Verify460GXBus(bus))) { /* Start with VGA enablement */ tmp = pciReadByte(pPCI->tag, VGASE); if (tmp & 0x01) { current |= PCI_PCI_BRIDGE_VGA_EN; if ((mask & PCI_PCI_BRIDGE_VGA_EN) && !(value & PCI_PCI_BRIDGE_VGA_EN)) pciWriteByte(pPCI->tag, VGASE, tmp & ~0x01); } else { if (mask & value & PCI_PCI_BRIDGE_VGA_EN) pciWriteByte(pPCI->tag, VGASE, tmp | 0x01); } /* Move on to master abort failure enablement */ if (has_err_460gx[pPCI->devnum - 0x10]) { tag = PCI_MAKE_TAG(pPCI->busnum, pPCI->devnum, pPCI->funcnum + 1); tmp = pciReadByte(tag, ERRCMD); if (tmp & 0x01) { current |= PCI_PCI_BRIDGE_MASTER_ABORT_EN; if ((mask & PCI_PCI_BRIDGE_MASTER_ABORT_EN) && !(value & PCI_PCI_BRIDGE_MASTER_ABORT_EN)) pciWriteByte(tag, ERRCMD, tmp & ~0x01); } else { if (mask & value & PCI_PCI_BRIDGE_MASTER_ABORT_EN) pciWriteByte(tag, ERRCMD, tmp | 0x01); } } /* Put emulation of any other P2P bridge control here */ } return (current & ~mask) | (value & mask); }
static Bool ix86PciBusCheck(void) { PCITAG tag; CARD32 id, class; CARD8 device; for (device = 0; device < ix86Pci0.numDevices; device++) { tag = PCI_MAKE_TAG(0, device, 0); id = (*ix86Pci0.funcs->pciReadLong)(tag, PCI_ID_REG); if ((CARD16)(id + 1U) <= (CARD16)1UL) continue; /* The rest of this is inspired by the Linux kernel */ class = (*ix86Pci0.funcs->pciReadLong)(tag, PCI_CLASS_REG); /* Ignore revision id and programming interface */ switch (class >> 16) { case (PCI_CLASS_PREHISTORIC << 8) | PCI_SUBCLASS_PREHISTORIC_MISC: /* Check for vendors of known buggy chipsets */ id &= 0x0000ffff; if ((id == PCI_VENDOR_INTEL) || (id == PCI_VENDOR_COMPAQ)) return TRUE; continue; case (PCI_CLASS_PREHISTORIC << 8) | PCI_SUBCLASS_PREHISTORIC_VGA: case (PCI_CLASS_DISPLAY << 8) | PCI_SUBCLASS_DISPLAY_VGA: case (PCI_CLASS_BRIDGE << 8) | PCI_SUBCLASS_BRIDGE_HOST: return TRUE; default: break; } } return FALSE; }
/* This does some 460GX-related processing after the PCI bus scan */ void xf86PostScan460GX(void) { pciConfigPtr pPCI, *ppPCI; pciBusInfo_t *pBusInfo; int i, j, devno; if (cbn_460gx <= 0) return; /* Set up our extra bus functions */ BusFuncs_460gx = *(pciBusInfo[0]->funcs); BusFuncs_460gx.pciControlBridge = Control460GXBridge; BusFuncs_460gx.pciGetBridgeBusses = Get460GXBridgeBusses; BusFuncs_460gx.pciGetBridgeResources = Get460GXBridgeResources; /* * Mark all host bridges so that they are ignored by the upper-level * xf86GetPciBridgeInfo() function. This marking is later clobbered by the * tail end of xf86scanpci() for those bridges that actually have bus * segments associated with them. */ ppPCI = xf86scanpci(0); /* Recursion is only apparent */ while ((pPCI = *ppPCI++)) { if ((pPCI->pci_base_class == PCI_CLASS_BRIDGE) && (pPCI->pci_sub_class == PCI_SUBCLASS_BRIDGE_HOST)) pPCI->businfo = HOST_NO_BUS; } ppPCI = xf86scanpci(0); /* Recursion is only apparent */ j = 0; /* * Fix up CBN bus linkage. This is somewhat arbitrary. The bridge chosen * for this must be a CBN device so that bus CBN can be recognised as the * root segment. It also cannot be any of the bus expanders (devices * CBN:0x10:0 through CBN:0x17:0 nor any of their functions). For now, we * chose the SAC host bridge at CBN:0:0. */ pBusInfo = pciBusInfo[cbn_460gx]; pBusInfo->bridge = pciBusInfo[0]->bridge; /* Just in case */ while ((pPCI = *ppPCI++)) { if (pPCI->busnum < cbn_460gx) continue; if (pPCI->busnum > cbn_460gx) break; if (pPCI->devnum < 0) continue; if (pPCI->devnum > 0) break; if (pPCI->funcnum < 0) continue; if (pPCI->funcnum > 0) break; pBusInfo->bridge = pPCI; pBusInfo->secondary = FALSE; pBusInfo->primary_bus = cbn_460gx; break; } for (i = 0, devno = 0x10; devno <= 0x17; i++, devno++) { /* Restore ERRCMD registers */ if (err_460gx[i] & 0x01) pciWriteByte(PCI_MAKE_TAG(cbn_460gx, devno, 1), ERRCMD, err_460gx[i]); if (!(cbdevs_460gx & (1 << devno))) { while ((pPCI = *ppPCI++)) { if (pPCI->busnum < cbn_460gx) continue; if (pPCI->busnum > cbn_460gx) break; if (pPCI->devnum < devno) continue; if (pPCI->devnum > devno) break; if (pPCI->funcnum < 0) continue; if (pPCI->funcnum > 0) break; if ((pBusInfo = pciBusInfo[busno_460gx[i]])) break; /* Fix bus linkage */ pBusInfo->bridge = pPCI; pBusInfo->secondary = TRUE; pBusInfo->primary_bus = cbn_460gx; /* Plug in chipset routines */ pBusInfo->funcs = &BusFuncs_460gx; break; } } /* Decode IOR registers */ for(; j <= (ior_460gx[i] & 0x0F); j++) iomap_460gx[j] = devno; } /* The bottom 4k of I/O space is always routed to PCI0a */ iomap_460gx[0] = 0x10; /* Decode IORD register */ for (j = 1; j <= 0x0F; j++) if (iord_460gx & (1 << j)) iomap_460gx[j] = 0x10; }
/* * This checks for, and validates, the presence of the 460GX chipset, and sets * cbn_460gx to a positive value accordingly. This function returns TRUE if * the chipset scan is to be stopped, or FALSE if the scan is to move on to the * next chipset. */ Bool xf86PreScan460GX(void) { pciBusInfo_t *pBusInfo; PCITAG tag; CARD32 tmp; int i, devno; /* Bus zero should already be set up */ if (!(pBusInfo = pciBusInfo[0])) { cbn_460gx = -1; return FALSE; } /* First look for a 460GX's primary host bridge */ tag = PCI_MAKE_TAG(0, 0x10, 0); if (pciReadLong(tag, PCI_ID_REG) != DEVID(INTEL, 460GX_SAC)) { cbn_460gx = -1; return FALSE; } /* Get CBN (Chipset bus number) */ if (!(cbn_460gx = (unsigned int)pciReadByte(tag, CBN))) { /* Sanity check failed */ cbn_460gx = -1; return TRUE; } if (pciNumBuses <= cbn_460gx) pciNumBuses = cbn_460gx + 1; /* Set up bus CBN */ if (!pciBusInfo[cbn_460gx]) { pciBusInfo[cbn_460gx] = xnfalloc(sizeof(pciBusInfo_t)); *pciBusInfo[cbn_460gx] = *pBusInfo; } tag = PCI_MAKE_TAG(cbn_460gx, 0, 0); if (pciReadLong(tag, PCI_ID_REG) != DEVID(INTEL, 460GX_SAC)) { /* Sanity check failed */ cbn_460gx = -1; return TRUE; } /* * Find out which CBN devices the firmware thinks are present. Of these, * we are only interested in devices 0x10 through 0x17. */ cbdevs_460gx = pciReadLong(tag, DEVNPRES); for (i = 0, devno = 0x10; devno <= 0x17; i++, devno++) { tag = PCI_MAKE_TAG(cbn_460gx, devno, 0); if (pciReadLong(tag, PCI_ID_REG) != DEVID(INTEL, 460GX_SAC)) { /* Sanity check failed */ cbn_460gx = -1; return TRUE; } if (devno == 0x10) iord_460gx = pciReadWord(tag, IORD); busno_460gx[i] = (unsigned int)pciReadByte(tag, BUSNO); subno_460gx[i] = (unsigned int)pciReadByte(tag, SUBNO); pcis_460gx[i] = pciReadByte(tag, PCIS); ior_460gx[i] = pciReadByte(tag, IOR); has_err_460gx[i] = err_460gx[i] = 0; /* Insurance */ tag = PCI_MAKE_TAG(cbn_460gx, devno, 1); tmp = pciReadLong(tag, PCI_ID_REG); switch (tmp) { case DEVID(INTEL, 460GX_PXB): case DEVID(INTEL, 460GX_WXB): if (cbdevs_460gx & (1 << devno)) { /* Sanity check failed */ cbn_460gx = -1; return TRUE; } /* * XXX I don't have WXB docs, but PCI register dumps indicate that * the registers we are interested in are consistent with those of * the PXB. */ err_460gx[i] = pciReadByte(tag, ERRCMD); has_err_460gx[i] = 1; break; case DEVID(INTEL, 460GX_GXB_1): if (cbdevs_460gx & (1 << devno)) { /* Sanity check failed */ cbn_460gx = -1; return TRUE; } /* * XXX GXB isn't documented to have an ERRCMD register, nor any * other means of failing master aborts. For now, assume master * aborts are always allowed to complete normally. */ break; default: if (((CARD16)(tmp + 1U) <= (CARD16)1U) && (cbdevs_460gx & (1U << devno))) break; /* Sanity check failed */ cbn_460gx = -1; return TRUE; } } /* Allow master aborts to complete normally */ for (i = 0, devno = 0x10; devno <= 0x17; i++, devno++) { if (!(err_460gx[i] & 0x01)) continue; pciWriteByte(PCI_MAKE_TAG(cbn_460gx, devno, 1), ERRCMD, err_460gx[i] & ~0x01); } /* * The 460GX spec says that any access to busses higher than CBN will be * master-aborted. It seems possible however that this is not the case in * all 460GX implementations. For now, limit the bus scan to CBN, unless * we have already found a higher bus number. */ for (i = 0; subno_460gx[i] < cbn_460gx; ) { if (++i < 8) continue; pciMaxBusNum = cbn_460gx + 1; break; } return TRUE; }