You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
455 lines
11 KiB
455 lines
11 KiB
/* 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 <vxWorks.h>
|
|
#include <hwif/util/hwMemLib.h>
|
|
#include <hwif/vxbus/vxBus.h>
|
|
#include <hwif/vxbus/vxbPlbLib.h>
|
|
#include <hwif/vxbus/hwConf.h>
|
|
|
|
#include "vxbPciLib.h"
|
|
#include <drv/pci/pciConfigLib.h>
|
|
#include <drv/pci/pciIntLib.h>
|
|
#include <drv/pci/pciAutoConfigLib.h>
|
|
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 <bus>
|
|
* 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);
|
|
}
|
|
|
|
|
|
|
|
|