/* pciCfgIntStub.c - BSP stub for PCI shared interrupts */ /* * * This program is OPEN SOURCE software: you can redistribute it and/or modify it; * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * */ #include #include #include #include #include #include "vxbPciLib.h" #include #include #include IMPORT VXB_DEVICE_ID globalBusCtrlID; /* macros */ /* * PCI_INT_BASE: PCI base IRQ number (not intNum) is * - IRQ 0 in the PIC or VIRTUAL_WIRE mode * - IRQ 0 in the SYMMETRIC_IO mode */ #define PCI_INT_BASE (0) /* typedefs */ /* globals */ typedef struct _PIRQ_ENABLE_ARG { BOOL enable; } PIRQ_ENABLE_ARG; VOID sysPciPirqEnable (BOOL enable); /* locals */ /* * These globals (glbMpApicNioint, glbMpApicNloint, glbMpApicInterruptTable) * are used in pciConfigIntStub.c, they avoid calling * through vxbus and reduces overhead, potential spinLock nesting... * * They are only present for INCLUDE_SYMMETRIC_IO_MODE, used with IO APIC. */ /* store PCI bridge device information */ struct pciBridgeDevice { int bus; /* bridge bus number */ int dev; int func; int priBus; /* priBus bus number */ int secBus; /* child bsu number*/ int pin; /* bridge's int pin */ int type; /* P2P or Card Bus */ struct pciBridgeDevice *pNext; /* next bridge pointer */ }; /* pci bridge device list pointer */ LOCAL struct pciBridgeDevice * pPciBridgeList = NULL ; LOCAL UCHAR sysPciIntRoute [4][4] = { { 1, 2, 3, 4 }, { 2, 3, 4, 1 }, { 3, 4, 1, 2 }, { 4, 1, 2, 3 } }; /***************************************************************************** * * sysPciRootFind - find the root of a PCI<->PCI bridge tree * * This function returns the root of a PCI<->PCI bridge tree, given the * number of a leaf PCI bus. The MPtable data contains interrupt pin to IRQ * mappings based soley on the root slot where a PCI<->PCI bridge is attached, * so for a given device on the other side of a bridge (or group of bridges), * we have to climb up the tree from the leaf bridge to get the information * for the root node. * * RETURNS: pointer to root PCI bridge device, or NULL if not found * * ERRNO: N/A */ struct pciBridgeDevice * sysPciRootFind ( VXB_DEVICE_ID ctrl, int bus, UINT8 pin ) { struct pciBridgeDevice * pList; struct pciBridgeDevice * pNext; pList = pPciBridgeList; while (pList != NULL) { if (pList->secBus == bus) break; pList = pList->pNext; } if (pList != NULL) { pNext = sysPciRootFind (ctrl, pList->priBus, pin); if (pNext != NULL) pList = pNext; } return (pList); } /***************************************************************************** * * sysPciPinSwizzle - apply PCI<->PCI bridge swizzle to an interrupt pin * * This function modifies the interrupt pin value for a PCI device using * the "swizzle" algorithm required when a device is on the other side of a * PCI<->PCI bridge. For devices on the other side of a bridge, a * transformation must be applied to the pin number if the slot number is * not 0 and not a multiple of 4. For those cases, the four INTA/B/C/D * interrupt pins are rotated in a "barber pole" pattern, sometimes also * called a swizzle * * The critical thing here is that the transformation must be applied at * all bridging levels, all the way up to the root slot (pri bus == 0). * This means that if a device is separated from the root slot by several * PCI<->PCI brides, multiple rounds of swizzling may be needed to obtain * the INTx pin number for the root slot. * * RETURNS: swizzled pin number * * ERRNO: N/A */ LOCAL int sysPciPinSwizzle ( VXB_DEVICE_ID ctrl, int bus, int dev, int func, int pin ) { struct pciBridgeDevice * pList; int finalpin = -1; /* If we're already on the root bus, no swizzle is needed. */ if (bus == 0) return (pin); /* * If we reached a node with an MP entry, then we can stop * swizzling here too. */ finalpin = sysPciIntRoute[dev & 3][pin - 1]; pList = pPciBridgeList; while (pList != NULL) { if (pList->secBus == bus) break; pList = pList->pNext; } if (pList != NULL) finalpin = sysPciPinSwizzle (ctrl, pList->priBus, pList->dev, pList->func, finalpin); return (finalpin); } /******************************************************************************* * * sysPciProgIrq - program a interrupt line register * * This function is responsible for write interrupt line register in * symmetric IO mode. * * RETURNS: return ERROR/irq number * */ LOCAL void sysPciProgIrq ( VXB_DEVICE_ID ctrl, int bus, int dev, int func, void * arg, int irq ) { PIRQ_ENABLE_ARG * pArg = arg; if (irq > 0xff) return; if (pArg->enable) { if (vxbPciConfigOutByte (ctrl, bus, dev, func, PCI_CFG_DEV_INT_LINE, irq) == ERROR) { return; } } } /******************************************************************************* * * sysPciFindBridge - scan and store the bridge device information * * This function is responsible for find bridge * * RETURNS: OK * ERROR on PCI Config Space read failure or memory allocation failure * */ LOCAL STATUS sysPciFindBridge ( VXB_DEVICE_ID ctrl, int bus, int dev, int func, void * arg ) { UINT16 pciClass; /* Class field of function */ UINT8 priBus, secBus, subBus, pin; struct pciBridgeDevice *pList; static struct pciBridgeDevice *pListPri = NULL; if (pciConfigInWord (bus, dev, func, PCI_CFG_SUBCLASS, &pciClass) != OK) { return ERROR; /* PCI read failure */ } if ((pciClass == ((PCI_CLASS_BRIDGE_CTLR << 8) | PCI_SUBCLASS_P2P_BRIDGE)) || (pciClass == ((PCI_CLASS_BRIDGE_CTLR << 8) | PCI_SUBCLASS_CARDBUS_BRIDGE))) { pList = (struct pciBridgeDevice *) hwMemAlloc(sizeof(struct pciBridgeDevice)); if (pList != NULL) { if (pListPri != NULL) { pListPri->pNext = pList; } } else { return ERROR; } (void) vxbPciConfigInByte (ctrl, bus, dev, func, PCI_CFG_PRIMARY_BUS, (UINT8 *)&priBus); (void) vxbPciConfigInByte (ctrl, bus, dev, func, PCI_CFG_SECONDARY_BUS, (UINT8 *)&secBus); (void) vxbPciConfigInByte (ctrl, bus, dev, func, PCI_CFG_SUBORDINATE_BUS, (UINT8 *)&subBus); (void) vxbPciConfigInByte (ctrl, bus, dev, func, PCI_CFG_BRG_INT_PIN, (UINT8 *)&pin); pList->bus = bus; pList->dev = dev; pList->func = func; pList->priBus = priBus; pList->secBus = secBus; pList->pNext = NULL; pList->pin = pin; pList->type = pciClass; pListPri = pList; if (pPciBridgeList == NULL) pPciBridgeList = pList; } return OK; } /******************************************************************************* * * sysPciPirqEnable2 - scan mptable and configure device interrupt line * * This function is responsible configure device interrupt line base on mptable * * RETURNS: OK or ERROR * */ LOCAL UCHAR sysPeu0IntRoute[4] = {60, 61, 62, 63}; LOCAL UCHAR sysPeu1IntRoute[4] = {60, 61, 62, 63}; LOCAL STATUS sysPciPirqEnable2 ( VXB_DEVICE_ID ctrl, int bus, int dev, int func, void * arg ) { UINT8 pin, pinPri; int irq = 0, copyBus, copyDev, copyPin; struct pciBridgeDevice * pList = pPciBridgeList ; struct pciBridgeDevice * pRoot; if (vxbPciConfigInByte (ctrl, bus, dev, func, PCI_CFG_DEV_INT_PIN, (UINT8 *)&pin) == ERROR) { return ERROR; } if ((pin > 4) || (pin == 0)) return OK; { copyBus = bus; copyDev = dev; copyPin = pin; /* try to use PCI specification method */ while (pList) { /* TODO, if ARI enable dev = 0 */ /* Swizzle the pin number. */ pinPri = sysPciPinSwizzle (ctrl, copyBus, copyDev, 0, copyPin); while (pList) { if (pList->secBus == copyBus) break; pList = pList->pNext; } if (pList == NULL) return OK; if ((pList->type == ((PCI_CLASS_BRIDGE_CTLR << 8) | PCI_SUBCLASS_CARDBUS_BRIDGE)) && (pList->pin != 0)) pinPri = pList->pin; pRoot = sysPciRootFind (ctrl, copyBus, pinPri); if (pRoot == NULL) irq = ERROR; else irq = sysPeu0IntRoute[pinPri-1]; if (irq != ERROR ) { sysPciProgIrq (ctrl, bus, dev, func, arg, irq); return OK; } if ((pList->bus == 0) && (pList->priBus == 0)) return OK; /* * MP entry not found for this intermediate bridge. * Prepare to climb up the bridge tree. (VXW6-8220) */ copyPin = pinPri + 1; copyDev = pList->dev; copyBus = pList->bus; pList = pPciBridgeList; /* rescan from a root bridge */ } } return OK; } /*********************************************************************** * * sysPciPirqEnable - enable or disbable PCI PIRQ direct handling * * This routine enables or disbales the PCI PIRQ direct handling. * * RETURNS: N/A */ VOID sysPciPirqEnable ( BOOL enable /* TRUE to enable, FALSE to disable */ ) { /* * Can't use pciConfigInLong() because it depends upon the * software device lists which haven't been initialized yet. */ PIRQ_ENABLE_ARG arg; VXB_DEVICE_ID ctrl = globalBusCtrlID; arg.enable = enable; /* Take action based on each and every device/function/pin combination */ /* return value of vxbPciConfigForeachFunc() not used */ (void) vxbPciConfigForeachFunc (ctrl, 0, TRUE, (VXB_PCI_FOREACH_FUNC)sysPciFindBridge, &arg,0); (void) vxbPciConfigForeachFunc (ctrl, 0, TRUE, (VXB_PCI_FOREACH_FUNC)sysPciPirqEnable2, &arg,0); }