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.

456 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);
}