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.
 
 
 

5053 lines
144 KiB

/* vxbPci.c - PCI bus support module for vxBus */
/*
* Copyright (c) 2005-2016 Wind River Systems, Inc.
*
* The right to copy, distribute, modify or otherwise make use
* of this software may be licensed only pursuant to the terms
* of an applicable Wind River license agreement.
*/
/*
modification history
--------------------
25may16,l_z fix nested spinLock. (VXW6-85582)
04k,14mar16,dee fix static analysis issues CHECKED_RETURN
04j,12jan15,xms fix 64-bit BAR size calculation incorrectly. (VXW6-7145)
04i,08may14,p_x fix CHECKED_RETURN error. (VXW6-82577)
04h,20mar14,d_l fix hwMemAlloc memory leak by adding pciFreeSubDev. (VXW6-80736)
04g,04sep13,xms fix CONSTANT_EXPRESSION_RESULT error. (WIND00414265)
04f,21may13,y_y add comment for vxbPciIntConnect/vxbPciIntEnable. (WIND00416690)
04e,28apr13,y_y compatibility issue, remove "NOMANUAL" on vxbPciMSICtl/
vxbPciMSIDisable/vxbPciMSIEnable/vxbPciMSIProgram. (WIND00414143)
04d,15jan13,j_z add multiple MSI/MSI-X routines.(WIND00333514)
04c,31oct12,l_z Add vxbPciMSIGet. (WIND00373602)
04b,04sep12,l_z Add vxbPciIntEnable/Disable. (WIND365027)
04a,24aug12,l_z Add comment for MSI functions. (WIND00177749)
03z,13aug12,jjk WIND00343781 - Remove previously added range check in
vxbPciIntConnect from version 03x back to 03w
03y,04jun12,sye fixed hwMem leak. (WIND00322388)
03x,24apr12,jjk WIND00343781 - Add PCI_INT_LINES range checks, increase number
of interrupt lines allowed and make them configurable by target.
03w,17oct11,h_k added minimum bus option. (CQ:288091)
added a max bus test in foreach functions.
03v,17may11,jjk Processor class in PCI bus should not be advertised in the
vxBus system if they are of root complex type (WIND00276381)
03u,13Jan11,y_c Add support for PCI_INT_HANDLER_UNBIND.(WIND00245984)
03t,22apr10,pch small footprint
03s,13apr10,cwl add interface that can enable MSI from BSP. (WIND00204365)
03r,09apr10,jpb Removed import of logMsg.
03q,17mar10,h_k fixed PCI-to-PCI bridge base address config space access.
moved vxbPciAccessCookieSet() and vxbAccessFree() from
vxbUsrCmdLine.c.
03p,02mar10,wap Fix PCIe support on Freescale PPC boards: do not announce
root complex nodes, since doing so will cause writes to BAR
registers that will disable access to devices
03o,23feb10,wqi Fix vxbPciIntDisconnect2() invalid status (WIND00185585)
03n,09dec09,h_k added added pRegBasePhys and regBaseSize for ILP32.
03m,14oct09,rgo pciDeviceAnnounce does not modify PCI graphic cards by
default in IA( CQ:177753)
03l,08sep09,rgo Check if the PCI status-read is valid. (CQ:179311)
03k,11sep09,h_k added 64-bit physical BAR support.
added size set for PCI IO for non-Intel Arch.
03j,04sep09,h_k fixed physical base address.
03i,26aug09,h_k added regBaseSize and pRegBasePhys.
03h,21apr09,h_k updated vxbPciIntConnect() for LP64.
03g,04mar09,h_k updated for LP64 support.
03f,16jun09,x_s restriction hwMemFree() for small footprint
03e,03mar09,to remove hard-coded device announce
03d,17dec08,to include vsbConfig.h for guest
03c,23oct08,to use _WRS_CONFIG_WRHV_GUEST for guest OS build
03b,26sep08,mmi enabled only selected devices for razor
03a,18dec08,rgo Intro MSI check Capability(wind00145088)
02z,31oct08,h_k fixed vxbPciMSIProgram() to pass the right size buffer for
PCI_MSI_DATA_64 and PCI_MSI_DATA_32 to vxbPciBusCfgWrite().
(CQ:142275)
removed PCI_AUTO_DEBUG code. (CQ:141771)
02y,24oct08,h_k corrected the access size for PCI_MSI_DATA_64 and
PCI_MSI_DATA_32.
changed VXB_PCI_BUS_CFG_XXX() to vxbPciBusCfgXxx().
02x,22oct08,wap Always initialize MSI capablities to prevent stale values
from being preserved across reboots (WIND00135786)
02w,21oct08,h_k added APIs doc in the vxbPci.bc.
02v,14oct08,wap Correct usage of vxbIntVectorGet() API (WIND00123216)
02u,25aug08,h_k fixed coverity error
02t,31jul08,h_k fixed PCI_LOG_MSG and PCI_AUTO_DEBUG_MSG. (CQ:129417)
fixed dependency between drivers and vxbPci. (CQ:125581)
02s,09jul08,dtr Fix MSIProgram.
02r,08jul08,tor moved conditional code to vxbpci.bc
02q,25jun08,h_k removed pAccess.
removed redundant zero clear.
02p,18jun08,pmr resource documentation
02o,14may08,h_k workaround for the incompatibility issue between plbIntrEntry
and pciIntrEntry. (CQ:123299)
02n,08may08,pmr check msiOffset in vxbPciMSICtl.
02m,01may08,h_k converted busCfgRead/Write to vxbPciDevCfgRead/Write.
02l,24apr08,tor optimization
02k,10mar08,h_k Change vxbPciConfigLibInit API and remove some dead code.
02j,09oct07,h_k enabled reset status bits for host bridge in
vxbPciAutoFuncEnable(). (CQ:107497)
fixed vxbPciFindDevice().
removed vxbMatchDevice() no longer used.
02i,14sep07,h_k removed debug code.
removed vxbPciBusAlloc() from non-auto-config.
removed pciDevShow() from no show routines configuration.
removed dependency between pentiumPci driver and auto-config.
02h,04sep07,pdg corrected apigen errors
02g,14aug07,dtr Use bus controller methods for reads/writes.
Change intLock to intCpuLock.
Move MSIProgram out of show routines.
Fix bridge interrupt routing support.
02f,14aug07,dtr Remove floating point usage in PcieDevCap by display as
power.
02e,31jul07,tor WIND00099495: end-of-list error
02d,19jul07,dtr Remove method call from MSIProgram.
02c,11jul07,dtr Minor tidy up.
02b,11jul07,dtr Create MSI specific funcs.
02a,13jun07,dtr Add support for pciFindClass.
01z,30may07,tor WIND00095533: missing pciIntDisconnect2
01y,05may07,dtr Fix some PCi legacy function support.
01x,04may07,wap Save BAR attributes so they aren't lost later
01w,20mar07,h_k reverted the vxbPciConfigOutWord() and vxbPciConfigOutByte()
and passed to correct data size.
01v,16mar07,wap Add extra sanity check to vxbGetPciDevice()
01u,15mar07,wap Fix vxbPciConfigOutWord() and vxbPciConfigOutByte() on
ARM/XSCALE arch
01t,08mar07,h_k moved type declarations to vxbPciLib.h.
01s,07mar07,h_k corrected access size from pciConfigOutLong().
01r,07mar07,dtr Remove some print statements.
01q,07mar07,dtr Don't annonce device until final update is done after auto
config.
01p,06mar07,dtr Check for NULL ptr busCtrlID in all relevent functions.
01o,05mar07,dtr Wrap an legacy auto config routine up with defne.
01n,07feb07,dtr Brought in pci config/auto config/interrupt libraries and
modified for vxBus. Will break old PCI bus controller
drivers. Removed old libraries from includes.
01m,30jan07,wap Enable bus mastering, memory and io access before probing
01l,30nov06,wap Correct static initialization to match structure change
01k,19sep06,pdg replaced method constant with an address
01j,09aug06,h_k fixed missing comment termination.
01i,03aug06,mdo Add PCI source files removed from BSPs.
01h,19Jul06,tor add vxbPciAutoConfig()
01g,13jul06,wap Properly scan for BARs, don't always assume that
bus0/dev0/func0 is a bridge device
01f,26oct05,mdo add upstream bus retrieval
01e,20sep05,pdg Fix for vxbus access routines errors (SPR #112197) &
Fix for pci interrupts not working (SPR #112678)
01d,01sep05,mdo Add vxb prefix
01c,10aug05,mdo Phase in new access method
01b,18jul05,mdo Fix per coding standards
Fix compilation warnings
01a,10Feb05,tor created
*/
/*
DESCRIPTION
This library contains the support routines for PCI
host controllers on the vxBus.
INCLUDE FILES: vxBus.h vxbPciBus.h pciConfigShow.h pciHeaderDefs.h
pciConfigLib.h pciAutoConfigLib.h pciIntLib.h
TARGET-SPECIFIC PARAMETERS
The parameters are retrieved from hwconf.c in the target BSP.
\is
\i <pcieMsiNoDisable>
The parameter is used to enable the pcie MSI interrupt from target BSP.
The parameter is optional in the sysInstParamTable[], it is usually set
to be a BOOL values(TRUE/FALSE). If the parameter is absent in target BSP,
the MSI interrupt is always disable by default when do a pciDeviceAnnounce.
The parameter type is VXB_PARAM_INT32, but it is usually set to be BOOL.
The following format is an example for customer config from target BSP.
{"usrOptions", 0, "pcieMsiNoDisable", VXB_PARAM_INT32, {(void *)TRUE}}
\ie
*/
/* includes */
#include <vxWorks.h>
#include <vsbConfig.h>
#include "../h/vxbus/vxbAccess.h"
#include <hwif/vxbus/vxBus.h>
#include <hwif/vxbus/hwConf.h>
#include <hwif/util/hwMemLib.h>
#include <hwif/util/vxbParamSys.h>
#include "vxbPciLib.h"
#include "../h/vxbus/vxbPciBus.h"
#include "../intCtlr/vxbIntDynaCtlrLib.h"
#include <drv/pci/pciConfigShow.h>
#include <drv/pci/pciHeaderDefs.h>
#include <pciClass.h>
#include <drv/pci/pciConfigLib.h>
#include <drv/pci/pciAutoConfigLib.h>
#include <drv/pci/pciIntLib.h>
#include <errnoLib.h>
#include <dllLib.h>
#include <iv.h>
#include <intLib.h>
#include <string.h>
#include <stdio.h>
#ifdef DOC
#define INCLUDE_PCI_BUS
#define INCLUDE_PCI_BUS_AUTOCONF
#define INCLUDE_PCI_BUS_SHOW
#include "../config/comps/src/hwif/vxbPci.bc"
#endif /* DOC */
/* defines for ConfigLib */
#define PCI_CONFIG_ABSENT_WORD_F 0xffff
#define PCI_CONFIG_ABSENT_WORD_0 0x0000
/*
* Provide intConnect via a macro so that an alternate interrupt binding
* mechanism can be specified
*
*/
#ifndef PCI_INT_HANDLER_BIND
#define PCI_INT_HANDLER_BIND(vector, routine, param, pResult) \
do { \
*pResult = intConnect ((vector),(routine),(param)); \
} while (FALSE)
#endif /* PCI_INT_HANDLER_BIND */
/*
* Provide an unbind macro to remove an interrupt binding.
*/
#ifndef PCI_INT_HANDLER_UNBIND
#define PCI_INT_HANDLER_UNBIND(vector, routine, param, pResult) \
do {\
*pResult = intDisconnect ( (vector), (routine), (int)(param) ); \
} while (FALSE)
#endif /* PCI_INT_HANDLER_UNBIND */
/*
* Provide intEnable via a macro so that an alternate interrupt enable
* mechanism can be specified
*/
#ifndef PCI_INT_HANDLER_ENABLE
#define PCI_INT_HANDLER_ENABLE(level, pResult) \
do { \
*pResult = intEnable (level); \
} while (FALSE)
#endif /* PCI_INT_HANDLER_ENABLE */
/*
* Provide intDisable via a macro so that an alternate interrupt disable
* mechanism can be specified
*/
#ifndef PCI_INT_HANDLER_DISABLE
#define PCI_INT_HANDLER_DISABLE(level, pResult) \
do { \
*pResult = intDisable (level); \
} while (FALSE)
#endif /* PCI_INT_HANDLER_DISABLE */
/* typedefs */
typedef struct pciIntRtn
{
DL_NODE node; /* double linked list */
void (*routine) (_Vx_usr_arg_t); /* interrupt handler */
_Vx_usr_arg_t parameter; /* parameter of the handler */
BOOL enable; /* enable or disable the interrupt */
int refCnt;
} PCI_INT_RTN;
/* local defines */
#define NO_ALLOCATION 0xffffffff
#define PCI_CONFIG_ABSENT_F 0xffff
#define PCI_CONFIG_ABSENT_0 0x0000
/* local configuration defines */
#undef PCI_AUTO_STATIC_LIST
#undef PCI_AUTO_RECLAIM_LIST
#ifndef PCI_AUTO_MAX_FUNCTIONS
# define PCI_AUTO_MAX_FUNCTIONS 32
#endif /* PCI_AUTO_MAX_FUNCTIONS */
/* globals */
STATUS (*_func_pciAutoCfgCtl) (void * pCookie, int cmd, void * pArg);
STATUS (*_func_vxbPciAutoConfig) (VXB_DEVICE_ID busCtrlID);
STATUS (*_func_PCIeRootForeach)
(
VXB_DEVICE_ID busCtrlID,
int pciLocBus, /* PCI bus of PCIe root */
int pciLocDevice, /* PCI device of PCIe root */
int pciLocFunction, /* PCI function of PCIe root */
VXB_PCI_FOREACH_FUNC funcCheckRtn, /* routine to call for each PCI func */
void *pArg /* argument to funcCheckRtn */
);
/* externs */
IMPORT STATUS vxbPciBusCfgRead
(
VXB_DEVICE_ID pDev, /* device info */
UINT32 byteOffset, /* offset into cfg space */
UINT32 transactionSize, /* transaction size */
void * pDataBuf /* buffer to read/write */
);
IMPORT STATUS vxbPciBusCfgWrite
(
VXB_DEVICE_ID pDev, /* device info */
UINT32 byteOffset, /* offset into cfg space */
UINT32 transactionSize, /* transaction size */
void * pDataBuf /* buffer to read/write */
);
IMPORT STATUS vxbDevAccessAnnounce
(
VXB_DEVICE_ID pDev,
struct pciDevAnnounceInfo * p
);
IMPORT void pciCfgInit(void);
IMPORT int pciIntVectorToIRQ
(
VOIDFUNCPTR * vector
);
IMPORT STATUS vxbPciConfigExtCapFind
(
VXB_DEVICE_ID busCtrlID,
UINT8 extCapFindId, /* Extended capabilities ID to search for */
int bus, /* PCI bus number */
int device, /* PCI device number */
int function, /* PCI function number */
UINT8 * pOffset /* returned config space offset */
);
/* locals */
STATUS vxbPciSpecialCycle
(
VXB_DEVICE_ID busCtrlID,
int busNo, /* bus number */
UINT32 message /* data driven onto AD[31:0] */
);
LOCAL STATUS pciERootForeach
(
VXB_DEVICE_ID busCtrlID,
int pciLocBus, /* PCI bus of PCIe root */
int pciLocDevice, /* PCI device of PCIe root */
int pciLocFunction, /* PCI function of PCIe root */
VXB_PCI_FOREACH_FUNC funcCheckRtn, /* routine to call for each PCI func */
void *pArg /* argument to funcCheckRtn */
);
LOCAL STATUS vxbPciMSIMultiProgram (VXB_DEVICE_ID, int, struct vxbIntDynaVecInfo *);
LOCAL STATUS vxbPciMSIXMultiProgram (VXB_DEVICE_ID, int, struct vxbIntDynaVecInfo *);
LOCAL STATUS vxbPciMSIXCtl (VXB_DEVICE_ID, struct vxbIntDynaVecInfo *, BOOL);
LOCAL struct vxbLock pciIntListLock;
VXB_DEVICE_ID globalBusCtrlID = NULL;
#define PCI_VXB_BUSID 0x40
#undef SPECIAL_CYCLE_SUPPORTED
#ifdef PCILIB_DEBUG
int usrPciBusDebugLevel = 0;
int pciDeviceCount = 0;
#ifdef DEBUG_USING_PRINTF
#define PCILIB_DEBUG_MSG(lvl,fmt,a,b,c,d,e,f) \
if (usrPciBusDebugLevel >= lvl) printf(fmt,a,b,c,d,e,f)
#else /* DEBUG_USING_PRINTF */
#define PCILIB_DEBUG_MSG(lvl,fmt,a,b,c,d,e,f) \
if (usrPciBusDebugLevel >= lvl) logMsg(fmt,a,b,c,d,e,f)
#endif /* DEBUG_USING_PRINTF */
#define PCILIB_ENABLE_ERROR_MSG
#else /* PCILIB_DEBUG */
#undef PCILIB_DEBUG_MSG
#define PCILIB_DEBUG_MSG(lvl,fmt,a,b,c,d,e,f)
#undef PCILIB_ENABLE_ERROR_MSG
#endif /* PCILIB_DEBUG */
#ifdef PCILIB_ENABLE_ERROR_MSG
# define PCILIB_ERR(msg) printf(msg)
# define PCILIB_LOG(msg,a,b,c,d,e,f) logMsg(msg,a,b,c,d,e,f)
#else /* PCILIB_ENABLE_ERROR_MSG */
# define PCILIB_ERR(msg)
# define PCILIB_LOG(msg,a,b,c,d,e,f)
#endif /* PCILIB_ENABLE_ERROR_MSG */
/* globals */
/* locals */
/* forward declarations */
LOCAL STATUS pciInit1 (void);
LOCAL STATUS pciInit2 (void);
LOCAL STATUS pciConnect (void);
LOCAL BOOL pciDevMatch
(
struct vxbDevRegInfo * pDriver,
struct vxbDev * pDev
);
#ifdef VXB_LEGACY_ACCESS
LOCAL void vxbAccessFree
(
VXB_DEVICE_ID pDev
);
LOCAL void vxbPciAccessCookieSet
(
VXB_DEVICE_ID pDev,
UINT32 * pBaseAddr
);
#endif /* VXB_LEGACY_ACCESS */
/* local data */
LOCAL BOOL pciBusLibInitialized = FALSE;
LOCAL struct vxbBusTypeInfo pciBusType =
{
NULL, /* pNext */
VXB_BUSID_PCI, /* busID */
pciInit1, /* busTypeInit1 */
pciInit2, /* busTypeInit2 */
pciConnect, /* busTypeConnect */
noDev, /* busTypeNewDevPresent */
pciDevMatch /* vxbDevMatch */
};
/* arch-provided data */
/*********************************************************************
*
* pciRegister - register PCI bus type
*
* This routine registers the PCI bus type with the vxBus subsystem.
*
* RETURNS: OK, always
*
* ERRNO
*/
STATUS pciRegister(void)
{
if ( pciBusLibInitialized )
return(ERROR);
_func_PCIeRootForeach = pciERootForeach;
_func_vxbPciDevCfgRead = vxbPciDevCfgRead;
_func_vxbPciDevCfgWrite = vxbPciDevCfgWrite;
pciCfgInit();
/* register PCI as a valid bus type */
(void) vxbBusTypeRegister(&pciBusType);
pciBusLibInitialized = TRUE;
return(OK);
}
/*********************************************************************
*
* pciHcfRecordFind - find device's HCF pciSlot record
*
* This routine finds HCF pciSlot record for the specified bus
* and device.
*
* RETURNS: Pointer to record, or NULL
*
* ERRNO
*/
HCF_DEVICE * pciHcfRecordFind
(
int pciBus, /* PCI Bus number */
int pciDevice /* PCI device number */
)
{
int i;
HCF_DEVICE * pHcf;
STATUS got;
int bus;
int device;
void * pParam;
PCILIB_DEBUG_MSG(1,"pciHcfRecordFind() called\n", 1,2,3,4,5,6);
/*
* now populate the bus with devices from the table created by the
* configuration tool
*/
for ( i = 0 ; i < hcfDeviceNum ; i++ )
{
if ( hcfDeviceList[i].busType != VXB_BUSID_PCI )
{
PCILIB_DEBUG_MSG(1,
"pciHcfRecordFind(): skipping non-PCI device %s\n",
(_Vx_usr_arg_t)hcfDeviceList[i].devName,
2,3,4,5,6);
}
else
{
PCILIB_DEBUG_MSG(1, "pciHcfRecordFind(): processing device %s\n",
(_Vx_usr_arg_t)hcfDeviceList[i].devName,
2,3,4,5,6);
pHcf = &hcfDeviceList[i];
/* check device type */
if ( strcmp(hcfDeviceList[i].devName, "pciSlot") == 0 )
{
PCILIB_DEBUG_MSG(1,"pciHcfRecordFind(): found pciSlot @ %p\n",
(_Vx_usr_arg_t)pHcf,2,3,4,5,6);
/*
* resourceDesc {
* The pciSlot device represents a slot into which a
* PCI card can be plugged in. The "bus" resource
* indicates which PCI bus number is assigned to
* the slot, which must match the results of the PCI
* configuration process. Two resources are required
* for each pciSlot device: "bus" and "device".
* The third number making up a PCI triple depends
* only on the device, and should not be specified
* as a resource. }
*/
pParam = &bus;
got = devResourceGet(pHcf, "bus", HCF_RES_INT, pParam);
if ( got != OK )
continue;
/*
* resourceDesc {
* The pciSlot device represents a slot into which
* a PCI card can be plugged in. The "device"
* resource indicates which PCI device number is
* assigned to the slot, which must match the results
* of the PCI configuration process. See the "bus"
* resource for additional information.
*/
pParam = &device;
got = devResourceGet(pHcf, "device", HCF_RES_INT, pParam);
if ( got != OK )
continue;
if ( ( pciBus != bus ) || ( pciDevice != device ) )
continue;
PCILIB_DEBUG_MSG(1, "pciHcfRecordFind(): found slot "
"matching device\n", 1,2,3,4,5,6);
return(&hcfDeviceList[i]);
}
}
}
return(NULL);
}
/*********************************************************************
*
* pciIntrInfoFind - find device interrupt information
*
* This routine finds interrupt information for specified device.
*
* RETURNS: N/A
*
* ERRNO
*/
LOCAL void pciIntrInfoFind
(
struct vxbDev * pDev /* Device information */
)
{
int i;
HCF_DEVICE * pHcf;
STATUS got;
int bus;
int device;
struct pciIntrEntry * pIntrInfo;
void * pParam;
UINT8 intLine;
struct vxbPciDevice * pPciDev = pDev->pBusSpecificDevInfo;
UINT8 msiBase;
UINT8 msiCtl;
BOOL msiNoDisable = FALSE;
PCILIB_DEBUG_MSG(1,"pciIntrInfoFind() called\n", 1,2,3,4,5,6);
/*
* Check whether controller supplies the intVecGet routine.
* If so, use it. Otherwise, use pciSlot entries in
* hcfDeviceList[]
*/
/* allocate vector structures */
/*
* Note: The pIntrInfo is accessed in vxbIntEnble() and it assumes
* the pIntrInfo points to a plbIntrEntry structure, the size is
* 20 bytes which is greater than pciIntrEntry structure (12 bytes),
* and the 5th field, pIntCtlrTable, is NULL if the device requires
* a specific method to enable its interrupt through the bus
* where the device is on.
* The PCI bus is the case, so that allocate the field and leave
* it NULL.
*/
pIntrInfo = (struct pciIntrEntry *)hwMemAlloc(sizeof(struct plbIntrEntry));
if ( pIntrInfo == NULL )
return;
/* read the interrupt line */
vxbPciBusCfgRead (pDev, PCI_CFG_DEV_INT_LINE, 1, &intLine);
/* save interrupt info in vxbDev structure */
pDev->pIntrInfo = pIntrInfo;
pIntrInfo->numVectors = 1;
/*
* identify the index into the array of interrupt lines maintained by
* the bus controller
*/
pIntrInfo->intVecInfo[0].index = intLine;
pIntrInfo->intVecInfo[0].intVector = vxbIntVectorGet (pDev, 0);
/*
* if the controller doesn't provide the vectorGet routine, then
* populate the vectors with slots from the table created by the
* configuration tool
*/
for ( i = 0 ; i < hcfDeviceNum ; i++ )
{
if ( hcfDeviceList[i].busType != VXB_BUSID_PCI )
{
PCILIB_DEBUG_MSG(1,
"pciIntrInfoFind(): skipping non-PCI device %s\n",
(_Vx_usr_arg_t)hcfDeviceList[i].devName,
2,3,4,5,6);
}
else
{
PCILIB_DEBUG_MSG(1, "pciIntrInfoFind(): processing device %s\n",
(_Vx_usr_arg_t)hcfDeviceList[i].devName,
2,3,4,5,6);
pHcf = &hcfDeviceList[i];
/* check device type */
if ( strcmp(hcfDeviceList[i].devName, "pciSlot") == 0 )
{
PCILIB_DEBUG_MSG(1,"pciIntrInfoFind(): found pciSlot @ %p\n",
(_Vx_usr_arg_t)pHcf,2,3,4,5,6);
/*
* resourceDesc {
* See the previous description for the bus resource. }
*/
pParam = &bus;
got = devResourceGet(pHcf, "bus", HCF_RES_INT, &pParam);
if ( got != OK )
continue;
/*
* resourceDesc {
* See the previous description for the device resource. }
*/
pParam = &device;
got = devResourceGet(pHcf, "device", HCF_RES_INT, &pParam);
if ( got != OK )
continue;
if ( pPciDev->pciBus != bus || pPciDev->pciDev != device )
continue;
PCILIB_DEBUG_MSG(1,
"pciIntrInfoFind(): found slot matching "
"device, pDev = %p, pHcf = %p\n",
(_Vx_usr_arg_t)pDev,
(_Vx_usr_arg_t)pHcf, 3,4,5,6);
/*
* found matching slot, now get interrupt info.
* devResourceIntrGet() fills in the pDev->pIntrInfo field.
*/
devResourceIntrGet(pDev, pHcf);
PCILIB_DEBUG_MSG(1, "pciIntrInfoFind(): interrupt record "
"@ 0x%08x\n",
(_Vx_usr_arg_t)pDev->pIntrInfo, 2,3,4,5,6);
return;
}
}
}
/*
* Check for MSI capability, and if it's present, turn it off.
* This guards against the case where, say, a bootrom with
* MSI support loads a VxWorks image that only uses legacy
* PCI interrupts on the PCIe bus. Leaving the MSI enable bit
* set on the device will cause it to continue to send MSI
* messages instead of triggering the PCI interrupt line.
* If MSI support is really needed, it will be enabled later.
*/
/* get the MSI enable flag from target BSP */
vxbInstUsrOptionGet("pcieMsiNoDisable", VXB_PARAM_INT32, (void *)&msiNoDisable);
/*
* If "pcieMsiNoDisable" parameter is set to be TRUE,
* skip disabling MSI.
*/
if (msiNoDisable)
return;
msiBase = 0;
vxbPciConfigExtCapFind (pDev->pParentBus->pCtlr, PCI_EXT_CAP_MSI,
pPciDev->pciBus, pPciDev->pciDev, pPciDev->pciFunc, &msiBase);
if (msiBase)
{
UINT32 vecAddr = 0;
UINT16 vecVal = 0;
/*
* This offset is for PCI_MSI_CTL which is 2bytes in advance vs value
* expected by defs so subtract.
*/
msiBase -= PCI_MSI_CTL;
vxbPciBusCfgRead (pDev, (UINT32)(msiBase + PCI_MSI_CTL), 1, &msiCtl);
msiCtl &= ~PCI_MSI_CTL_ENABLE;
/* disable MSI */
vxbPciBusCfgWrite (pDev, (UINT32)(msiBase + PCI_MSI_CTL), 1, &msiCtl);
/* clear PCI_MSI_ADDR_LO */
vxbPciBusCfgWrite (pDev, (UINT32)(msiBase + PCI_MSI_ADDR_LO), 4,
&vecAddr);
if (msiCtl & PCI_MSI_CTL_64BIT)
{
/* clear PCI_MSI_ADDR_HI and PCI_MSI_DATA_64 */
vxbPciBusCfgWrite (pDev, (UINT32)(msiBase + PCI_MSI_ADDR_HI), 4,
&vecAddr);
vxbPciBusCfgWrite (pDev, (UINT32)(msiBase + PCI_MSI_DATA_64), 2,
&vecVal);
}
else
{
/* clear PCI_MSI_DATA_32 */
vxbPciBusCfgWrite (pDev, (UINT32)(msiBase + PCI_MSI_DATA_32), 2,
&vecVal);
}
}
}
/*********************************************************************
*
* pciDevMatch - check whether device and driver go together
*
* This routine checks that the specified device and device driver
* are a matched pair.
*
* RETURNS: TRUE if the device and driver match, FALSE otherwise
*
* ERRNO
*/
LOCAL BOOL pciDevMatch
(
struct vxbDevRegInfo * pDriver, /* Pointer to device driver */
struct vxbDev * pDev /* Device information */
)
{
struct vxbPciDevice * pPciDev;
struct vxbPciRegister * pPciDriver;
struct vxbPciID * pDevVend;
int i;
PCILIB_DEBUG_MSG(20,"pciDevMatch((0x%08x,%s),(0x%08x,0x%08x)) called\n",
(_Vx_usr_arg_t)pDriver, (_Vx_usr_arg_t)pDriver->drvName,
(_Vx_usr_arg_t)pDev, (_Vx_usr_arg_t)pDev->pRegBase[0],
5, 6);
/* the following two checks are almost duplicates of vxBus.c code */
if ( pDriver->busID != VXB_BUSID_PCI || pDev->busID != VXB_BUSID_PCI )
return(FALSE);
pPciDev = (struct vxbPciDevice *)pDev->pBusSpecificDevInfo;
pPciDriver = (struct vxbPciRegister *)pDriver;
/*
* Before checking the deviceID and vendorID of the device
* against the values registered by the driver, we check to
* see if the driver lets us know not to do that. In some
* cases, drivers may want to match against a class of devices,
* rather than against a devID/vendID pair. In this case,
* the driver must specify devID and vendID of -1, and include
* a driver probe routine. The values -1 are not legal values
* according to the PCI spec, so this should not cause any
* problems.
*/
if ( ( pPciDriver->idList[0].pciDevId == 0xffff ) &&
( pPciDriver->idList[0].pciVendId == 0xffff ) &&
( pPciDriver->b.devProbe != NULL ) )
{
PCILIB_DEBUG_MSG(25,"pciDevMatch(): match unspecified devID/vendID\n",
1,2,3,4,5,6);
return(TRUE);
}
pDevVend = pPciDriver->idList;
for ( i = 0 ; i < pPciDriver->idListLen ; i++ )
{
PCILIB_DEBUG_MSG(20, "pciDevMatch: Trying to match DID/VID(0x%x/0x%x) "
"with DID/VID(0x%x/0x%x)\n",
pPciDev->pciDevId, pPciDev->pciVendId,
pDevVend->pciDevId, pDevVend->pciVendId,
5, 6);
if ( ( pPciDev->pciDevId == pDevVend->pciDevId ) &&
( pPciDev->pciVendId == pDevVend->pciVendId ) )
{
PCILIB_DEBUG_MSG(25,"pciDevMatch(): found match\n", 0,1,2,3,4,5);
return(TRUE);
}
pDevVend++;
}
PCILIB_DEBUG_MSG(25,"pciDevMatch(): no match found\n", 0,1,2,3,4,5);
return(FALSE);
}
/*******************************************************************************
*
* pciInit - first-pass bus type initialization
*
* This function currently does nothing.
*
* RETURNS: OK, always
*
* ERRNO
*/
LOCAL STATUS pciInit1 (void)
{
return(OK);
}
/*******************************************************************************
*
* pciInit2 - second-pass bus type initialization
*
* This function currently does nothing.
*
* RETURNS: OK, always
*
* ERRNO
*/
LOCAL STATUS pciInit2 (void)
{
vxbLockInit (&pciIntListLock);
return(OK);
}
/*******************************************************************************
*
* pciConnect - connect PCI bus type to bus subsystem
*
* This function currently does nothing.
*
* RETURNS: OK, always
*
* ERRNO
*/
LOCAL STATUS pciConnect (void)
{
return(OK);
}
/**********************************************************************
*
* pciFreeSubDev - free the resources of the devices on PCI bus
*
* pciFreeSubDev() frees the resources of the devices on this PCI bus
*
* ERRNO: not set
*
* RETURNS: OK, always
*
* \NOMANUAL
*/
LOCAL STATUS pciFreeSubDev
(
VXB_DEVICE_ID pDev,
VXB_DEVICE_ID busCtlr,
char * pArg
)
{
if (pDev->pBusSpecificDevInfo != NULL)
{
if (strncmp(pDev->pName, "miiBus",6) == 0)
{
free((char *)(pDev->pBusSpecificDevInfo));
}
else
{
#ifndef _VXBUS_BASIC_HWMEMLIB
hwMemFree((char *)(pDev->pBusSpecificDevInfo));
#endif /* !_VXBUS_BASIC_HWMEMLIB */
}
pDev->pBusSpecificDevInfo = NULL;
}
if (pDev->pIntrInfo != NULL)
{
#ifndef _VXBUS_BASIC_HWMEMLIB
hwMemFree((char *)(pDev->pIntrInfo));
#endif /* !_VXBUS_BASIC_HWMEMLIB */
pDev->pIntrInfo = NULL;
}
return OK;
}
/**********************************************************************
*
* vxbPciFreeSubDev - free the resources of the devices on PCI bus
*
* vxbPciFreeSubDev() frees the resources of the devices on this PCI bus
*
* ERRNO: not set
*
* RETURNS: NULL
*
* \NOMANUAL
*/
void vxbPciFreeSubDev
(
VXB_DEVICE_ID busCtrlID
)
{
/* Don't care return value. */
(void)vxbSubDevAction(busCtrlID, pciFreeSubDev, 0,
VXB_ITERATE_INSTANCES | VXB_ITERATE_ORPHANS);
}
/*******************************************************************************
*
* pciDeviceAnnounce - notify the bus subsystem of a device on PCI
*
* This routine tells the vxBus subsystem that a PCI device has been found
* on the PCI bus.
*
* RETURNS: OK, or ERROR
*
* ERRNO
*/
STATUS pciDeviceAnnounce
(
VXB_DEVICE_ID busCtrlID,
UINT8 bus, /* PCI bus number */
UINT8 dev, /* PCI device number */
UINT8 func, /* PCI function number */
void * pArg /* pDev */
)
{
struct pciDevAnnounceInfo * p = (struct pciDevAnnounceInfo *)pArg;
struct vxbPciDevice *pPciDev;
struct vxbDev * pDev;
int i;
int baseAddrNum;
UINT32 * pBaseAddr;
FUNCPTR pFuncBaseAddressConvert;
struct vxbDev * pUpstreamDev;
UINT16 command;
FUNCPTR method;
STATUS status;
#if (CPU_FAMILY == I80X86)
UINT8 classCode; /* Class Code Byte in PCI configuration */
#endif /* CPU_FAMILY == I80X86 */
UINT8 headerType; /* Header Type in PCI configuration */
if(busCtrlID == NULL)
return(ERROR);
PCILIB_DEBUG_MSG(1,"pciDeviceAnnounce(): entry at [%d,%d,%d]\n",
bus, dev, func,4,5,6);
/* initialize bus-specific info */
pPciDev = (struct vxbPciDevice *)hwMemAlloc(sizeof(*pPciDev));
if ( pPciDev == NULL )
return(ERROR);
method = vxbDevMethodGet(busCtrlID,PCI_CONTROLLER_METHOD_CFG_INFO);
if(method == NULL)
{
PCILIB_ERR("pciDeviceAnnounce Failed: No PCI method for ConfigInfo\n");
#ifndef _VXBUS_BASIC_HWMEMLIB
hwMemFree((char *)pPciDev); /* Free previous allocated ptr */
#endif /* !_VXBUS_BASIC_HWMEMLIB */
return(ERROR);
}
status = (*method)(busCtrlID,(char *)&pPciDev->pPciConfig);
if(status == ERROR)
{
PCILIB_ERR("pciDeviceAnnounce Failed: "
"Method for ConfigInfo doesn't work\n");
#ifndef _VXBUS_BASIC_HWMEMLIB
hwMemFree((char *)pPciDev); /* Free previous allocated ptr */
#endif /* !_VXBUS_BASIC_HWMEMLIB */
return(ERROR);
}
if(pPciDev->pPciConfig == NULL)
{
PCILIB_ERR("pciDeviceAnnounce Failed: pPciConfig is NULL\n");
#ifndef _VXBUS_BASIC_HWMEMLIB
hwMemFree((char *)pPciDev); /* Free previous allocated ptr */
#endif /* !_VXBUS_BASIC_HWMEMLIB */
return(ERROR);
}
method = vxbDevMethodGet(busCtrlID,PCI_CONTROLLER_METHOD_INTERRUPT_INFO);
if(method == NULL)
{
PCILIB_ERR("pciDeviceAnnounce Failed: No PCI method for IntInfo\n");
#ifndef _VXBUS_BASIC_HWMEMLIB
hwMemFree((char *)pPciDev); /* Free previous allocated ptr */
#endif /* !_VXBUS_BASIC_HWMEMLIB */
return(ERROR);
}
status = (*method)(busCtrlID,&pPciDev->pIntInfo);
if(status == ERROR)
{
PCILIB_ERR("pciDeviceAnnounce Failed: "
"Method for IntInfo doesn't work\n");
#ifndef _VXBUS_BASIC_HWMEMLIB
hwMemFree((char *)pPciDev); /* Free previous allocated ptr */
#endif /* !_VXBUS_BASIC_HWMEMLIB */
return(ERROR);
}
if(pPciDev->pIntInfo==NULL)
{
PCILIB_ERR("pciDeviceAnnounce Failed: pIntInfo is NULL\n");
#ifndef _VXBUS_BASIC_HWMEMLIB
hwMemFree((char *)pPciDev); /* Free previous allocated ptr */
#endif /* !_VXBUS_BASIC_HWMEMLIB */
return(ERROR);
}
pPciDev->pciBus = bus;
pPciDev->pciDev = dev;
pPciDev->pciFunc = func;
/* initialize generic bus info */
pDev = vxbDevStructAlloc(WAIT_FOREVER);
if ( pDev == NULL )
{
#ifndef _VXBUS_BASIC_HWMEMLIB
hwMemFree((char *)pPciDev);
#endif /* !_VXBUS_BASIC_HWMEMLIB */
return(ERROR);
}
pDev->pNext = NULL;
pDev->pParentBus = p->pDev->u.pSubordinateBus;
pDev->u.pSubordinateBus = NULL;
pDev->busID = VXB_BUSID_PCI;
pDev->pBusSpecificDevInfo = (void *)pPciDev;
pDev->pIntrInfo = NULL;
pDev->pName = NULL;
pDev->pDriver = NULL;
pDev->pDrvCtrl = NULL;
pPciDev->devID = pDev;
if ( vxbDevAccessAnnounce(pDev, p) != OK )
{
#ifndef _VXBUS_BASIC_HWMEMLIB
hwMemFree((char *)pPciDev);
#endif /* !_VXBUS_BASIC_HWMEMLIB */
return(ERROR);
}
#ifdef PCILIB_DEBUG
/* increment PCI device counter for debug purposes */
pciDeviceCount++;
#endif /* PCILIB_DEBUG */
/* get device ID and vendor ID */
vxbPciBusCfgRead (pDev, PCI_CFG_DEVICE_ID, 2,
&(pPciDev->pciDevId));
vxbPciBusCfgRead (pDev, PCI_CFG_VENDOR_ID, 2,
&(pPciDev->pciVendId));
if((pPciDev->pciDevId == 0xffff) || (pPciDev->pciVendId == 0xffff))
{
#ifndef _VXBUS_BASIC_HWMEMLIB
hwMemFree((char *)pPciDev);
#ifdef VXB_LEGACY_ACCESS
vxbAccessFree(pDev);
#endif /* VXB_LEGACY_ACCESS */
vxbDevStructFree(pDev);
#endif /* !_VXBUS_BASIC_HWMEMLIB */
return(ERROR);
}
PCILIB_DEBUG_MSG(1, "pciDeviceAnnounce(): found devVend %04x%04x "
"at [%d,%d,%d]\n",
pPciDev->pciDevId, pPciDev->pciVendId,
pPciDev->pciBus, pPciDev->pciDev, pPciDev->pciFunc, 6);
/* allocate memory for the base address array */
pBaseAddr = (UINT32 *)hwMemAlloc(6 * sizeof(UINT32));
/* return ERROR if memory allocation fails */
if (pBaseAddr == NULL)
{
#ifndef _VXBUS_BASIC_HWMEMLIB
hwMemFree((char *)pPciDev);
#ifdef VXB_LEGACY_ACCESS
vxbAccessFree(pDev);
#endif /* VXB_LEGACY_ACCESS */
vxbDevStructFree(pDev);
#endif /* !_VXBUS_BASIC_HWMEMLIB */
return(ERROR);
}
vxbPciBusCfgRead (pDev, PCI_CFG_HEADER_TYPE, 1, &headerType);
if (headerType & PCI_HEADER_TYPE_BRIDGE)
baseAddrNum = 2; /* PCI-to-PCI bridge */
else if ((headerType & PCI_HEADER_TYPE_MASK) == PCI_HEADER_PCI_CARDBUS)
baseAddrNum = 1; /* PCI Card Bus */
else
baseAddrNum = 6;
/* get BAR values */
for (i = 0; i < baseAddrNum; i++)
{
vxbPciBusCfgRead (pDev, PCI_CFG_BASE_ADDRESS_0 + (4*i),
4, &(pBaseAddr[i]));
}
for (i = (baseAddrNum - 1); i >= 0; i--)
{
ULONG baseAddrTemp;
UINT32 pciSize;
/*
* Don't count unimplemented BARs.
*/
if (pBaseAddr[i] == 0 || pBaseAddr[i] == 0xFFFFFFFF)
{
pDev->regBaseFlags[i] = VXB_REG_NONE;
continue;
}
/*
* Don't count the upper 32-bit of the 64-bit BARs.
*/
if ((i & 1) &&
((pBaseAddr[i-1] & PCI_BAR_SPACE_MASK) != PCI_BAR_SPACE_IO) &&
((pBaseAddr[i-1] & PCI_BAR_MEM_TYPE_MASK) == PCI_BAR_MEM_ADDR64))
{
pDev->regBaseFlags[i] = VXB_REG_NONE;
continue;
}
baseAddrTemp = (ULONG)pBaseAddr[i];
pDev->pRegBase[i] = (void *) baseAddrTemp;
/* retrieve the upstream bus device info */
VXBAF_RETRIEVE_UPSTREAM_BUS_PTR(pDev, pUpstreamDev);
/* if the parent device is invalid, return ERROR */
if (pUpstreamDev == NULL)
{
#ifndef _VXBUS_BASIC_HWMEMLIB
hwMemFree((char *)pPciDev);
hwMemFree((char *)pBaseAddr);
#ifdef VXB_LEGACY_ACCESS
vxbAccessFree(pDev);
#endif /* VXB_LEGACY_ACCESS */
vxbDevStructFree(pDev);
#endif /* !_VXBUS_BASIC_HWMEMLIB */
return ERROR;
}
/* Get the base address register contents */
pciSize = 0xffffffff;
/* get length from PCI configuration space */
/* set address program mode */
vxbPciBusCfgWrite (pDev, PCI_CFG_BASE_ADDRESS_0 + (4*i),
4, &pciSize);
/* fetch address size value */
vxbPciBusCfgRead (pDev, PCI_CFG_BASE_ADDRESS_0 + (4*i),
4, &pciSize);
/* restore current value */
vxbPciBusCfgWrite (pDev, PCI_CFG_BASE_ADDRESS_0 + (4*i),
4, &(pBaseAddr[i]));
/* Store the resource attributes so they aren't lost later. */
if ((pBaseAddr[i] & PCI_BAR_SPACE_MASK) == PCI_BAR_SPACE_IO)
{
/* store the BAR size */
pDev->regBaseSize[i] = (size_t)0xffffffff -
(pciSize & (UINT32)~0x3) + 1;
pDev->regBaseFlags[i] = VXB_REG_IO;
}
else
{
/* store the BAR size */
pDev->regBaseSize[i] = (size_t)0xffffffff -
(pciSize & (UINT32)~0xf) + 1;
pDev->regBaseFlags[i] = VXB_REG_MEM;
}
/* retrieve the method for converting the base address */
pFuncBaseAddressConvert = vxbDevMethodGet(pUpstreamDev,
PCI_CONTROLLER_METHOD_CONVERT_BASE_ADDR);
/*
* if the function pointer is valid,
* call the function to convert the base address
*/
if (pFuncBaseAddressConvert != NULL)
{
if ((*pFuncBaseAddressConvert)(pUpstreamDev, &pDev->pRegBase[i])
!= OK)
{
#ifndef _VXBUS_BASIC_HWMEMLIB
hwMemFree((char *)pPciDev);
hwMemFree((char *)pBaseAddr);
#ifdef VXB_LEGACY_ACCESS
vxbAccessFree(pDev);
#endif /* VXB_LEGACY_ACCESS */
vxbDevStructFree(pDev);
#endif /* !_VXBUS_BASIC_HWMEMLIB */
return ERROR;
}
}
else
{
ULONG regBaseTemp;
/*
* based on the base address contents, update the base address which
* is to be used by the driver.
*/
if ((pBaseAddr[i] & PCI_BAR_SPACE_MASK) == PCI_BAR_SPACE_IO)
{
regBaseTemp = (ULONG)(pBaseAddr[i] & (UINT32)PCI_IOBASE_MASK);
}
else
{
regBaseTemp = (ULONG)(pBaseAddr[i] & (UINT32)PCI_MEMBASE_MASK);
#ifdef _WRS_CONFIG_LP64
/* set the upper 32-bit for 64-bit PCI mem */
if (((i & 1) == 0) &&
((pBaseAddr[i] & PCI_BAR_MEM_TYPE_MASK) == PCI_BAR_MEM_ADDR64))
{
int n=i+1;
size_t regSizeTemp;
/* Get the base address register contents */
pciSize = 0xffffffff;
/* get length from PCI configuration space */
/* set address program mode */
vxbPciBusCfgWrite (pDev, PCI_CFG_BASE_ADDRESS_0 + (4*n),
4, &pciSize);
/* fetch address size value */
vxbPciBusCfgRead (pDev, PCI_CFG_BASE_ADDRESS_0 + (4*n),
4, &pciSize);
/* restore current value */
vxbPciBusCfgWrite (pDev, PCI_CFG_BASE_ADDRESS_0 + (4*n),
4, &(pBaseAddr[n]));
pDev->regBaseSize[i] &= 0xffffffff;
regSizeTemp = (size_t)0xffffffff - pciSize + 1;
pDev->regBaseSize[i] |= regSizeTemp << 32;
regBaseTemp |= ((ULONG) pBaseAddr[i+1] << 32);
}
#endif /* _WRS_CONFIG_LP64 */
}
pDev->pRegBase[i] = (void *)regBaseTemp;
pDev->pRegBasePhys[i] = (void *)regBaseTemp;
}
}
#ifdef VXB_LEGACY_ACCESS
/* store the pointer to the array in the access structure */
vxbPciAccessCookieSet(pDev, pBaseAddr);
#else /* VXB_LEGACY_ACCESS */
#ifndef _VXBUS_BASIC_HWMEMLIB
/* free the buffer */
hwMemFree ((char *) pBaseAddr);
#endif /* !_VXBUS_BASIC_HWMEMLIB */
#endif /* VXB_LEGACY_ACCESS */
/* attach interrupt info */
pciIntrInfoFind (pDev);
#if (CPU_FAMILY == I80X86)
/* In IA, Enable any PCI device, but graphic one. */
vxbPciBusCfgRead (pDev, PCI_CFG_CLASS, 1,&classCode);
if ( classCode != PCI_CLASS_DISPLAY_CTLR )
#endif /* (CPU_FAMILY == I80X86) */
{
command = PCI_CMD_IO_ENABLE|PCI_CMD_MEM_ENABLE|PCI_CMD_MASTER_ENABLE;
vxbPciBusCfgWrite (pDev, PCI_CFG_COMMAND, 2, &command);
}
/* insure pDriver is initialized */
pDev->pDriver = NULL;
(void) vxbDeviceAnnounce(pDev);
return(OK);
}
/*******************************************************************************
*
* pciBusAnnounceDevices - Notify the bus subsystem of all devices on PCI
*
* RETURNS: N/A
*
* ERRNO
*/
void pciBusAnnounceDevices
(
pVXB_ACCESS_LIST pArg,
VXB_DEVICE_ID busCtrlID,
void * pCookie
)
{
struct pciDevAnnounceInfo p;
FUNCPTR method;
STATUS status;
struct vxbPciConfig * pPciConfig = NULL;
method = vxbDevMethodGet (busCtrlID, PCI_CONTROLLER_METHOD_CFG_INFO);
if ((method == NULL) ||
((status = (*method) (busCtrlID, &pPciConfig)) == ERROR))
{
PCILIB_ERR("pciBusAnnounceDevices Failed: Method for ConfigInfo doesn't work\n");
return;
}
p.pDev = busCtrlID;
p.pMethods = pArg;
p.pCookie = pCookie;
(void)vxbPciConfigForeachFunc (busCtrlID,
pPciConfig->pciMinBus,
TRUE,
(VXB_PCI_FOREACH_FUNC) pciDeviceAnnounce,
&p,0);
}
/**** ConfigLibStarts */
/*******************************************************************************
*
* vxbPciConfigLibInit - initialize the PCI configuration structure
*
* This routine initializes the PCI configuration structure.
*
* RETURNS: always OK
*
* ERRNO
*
*/
STATUS vxbPciConfigLibInit
(
struct vxbPciConfig * pPciConfig,
int pciMaxBus /* Max number of sub-busses */
)
{
return (vxbPciConfigLibWithMinBusInit (pPciConfig, pciMaxBus, 0));
}
/*******************************************************************************
*
* vxbPciConfigLibWithMinBusInit - initialize the PCI configuration structure
*
* This routine initializes the PCI configuration structure.
* This is an extention version of the vxbPciConfigLibInit() and the minimum
* number of sub-busses can be additionally configured.
*
* RETURNS: always OK
*
* ERRNO
*
*/
STATUS vxbPciConfigLibWithMinBusInit
(
struct vxbPciConfig * pPciConfig,
int pciMaxBus, /* Max number of sub-busses */
int pciMinBus /* Minimum number of sub-busses */
)
{
pPciConfig->pciMaxBus = pciMaxBus; /* Max number of sub-busses */
pPciConfig->pciMinBus = pciMinBus; /* Minimum number of sub-busses */
pPciConfig->pciLibInitStatus = OK;
return(pPciConfig->pciLibInitStatus);
}
/*******************************************************************************
*
* vxbPciFindDevice - find the nth device with the given device & vendor ID
*
* This routine finds the nth device with the given device & vendor ID.
*
* RETURNS:
* OK, or ERROR if the deviceId and vendorId didn't match.
*
* ERRNO
*
*/
STATUS vxbPciFindDevice
(
VXB_DEVICE_ID busCtrlID,
int vendorId, /* vendor ID */
int deviceId, /* device ID */
int index, /* desired instance of device */
int * pBusNo, /* bus number */
int * pDeviceNo, /* device number */
int * pFuncNo /* function number */
)
{
STATUS status = ERROR;
BOOL cont = TRUE;
int busNo;
int deviceNo;
int funcNo;
UINT32 device;
UINT32 vendor;
UINT8 header;
FUNCPTR method = NULL;
struct vxbPciConfig * pPciConfig = NULL;
if(busCtrlID == NULL)
return(ERROR);
method = vxbDevMethodGet(busCtrlID,PCI_CONTROLLER_METHOD_CFG_INFO);
if(method == NULL)
{
PCILIB_ERR("FindClass Failed: No PCI method for ConfigInfo\n");
return(ERROR);
}
status = (*method)(busCtrlID,&pPciConfig);
if(status == ERROR)
{
PCILIB_ERR("FindClass Failed: Method for ConfigInfo doesn't work\n");
return(ERROR);
}
status = ERROR;
if(pPciConfig==NULL)
{
PCILIB_ERR("FindClass Failed: pPciConfig is NULL\n");
return(ERROR);
}
if (pPciConfig->pciLibInitStatus != OK) /* sanity check */
cont = FALSE;
for (busNo = pPciConfig->pciMinBus;
(cont == TRUE) && (busNo <= pPciConfig->pciMaxBus);
busNo++)
{
for (deviceNo = 0;
((cont == TRUE) && (deviceNo < PCI_MAX_DEV));
++deviceNo)
{
for (funcNo = 0;
(cont == TRUE) && (funcNo < PCI_MAX_FUNC);
funcNo++)
{
/* avoid a special bus cycle */
if ((deviceNo == 0x1f) && (funcNo == 0x07))
continue;
vxbPciConfigInLong (busCtrlID, busNo, deviceNo, funcNo,
PCI_CFG_VENDOR_ID,
&vendor);
/*
* If nonexistent device, skip to next, only look at
* vendor ID field for existence check
*/
if ((vendor & 0xffff) == 0xffff)
{
if (funcNo == 0)
break; /* next device */
continue; /* next function */
}
device = vendor >> 16;
device &= 0x0000FFFF;
vendor &= 0x0000FFFF;
if ((vendor == (UINT32)vendorId) &&
(device == (UINT32)deviceId) &&
(index-- == 0))
{
*pBusNo = busNo;
*pDeviceNo = deviceNo;
*pFuncNo = funcNo;
status = OK;
cont = FALSE; /* terminate all loops */
continue;
}
/* go to next if current device is single function */
vxbPciConfigInByte (busCtrlID,busNo, deviceNo, funcNo,
PCI_CFG_HEADER_TYPE,
&header);
if (((header & PCI_HEADER_MULTI_FUNC) !=
PCI_HEADER_MULTI_FUNC) &&
(funcNo == 0))
break;
}
}
}
return (status);
}
/*******************************************************************************
*
* vxbPciFindClass - find the nth occurrence of a device by PCI class code.
*
* This routine finds the nth device with the given 24-bit PCI class code
* (class subclass prog_if).
*
* The classcode arg of must be carefully constructed from class and sub-class
* macros.
*
* Example : To find an ethernet class device, construct the classcode arg
* as follows:
*
* \cs
* ((PCI_CLASS_NETWORK_CTLR << 16 | PCI_SUBCLASS_NET_ETHERNET << 8))
* \ce
*
* RETURNS:
* OK, or ERROR if the class didn't match.
*
* ERRNO
*
*/
STATUS vxbPciFindClass
(
VXB_DEVICE_ID busCtrlID,
int classCode, /* 24-bit class code */
int index, /* desired instance of device */
int * pBusNo, /* bus number */
int * pDeviceNo, /* device number */
int * pFuncNo /* function number */
)
{
STATUS status = ERROR;
BOOL cont = TRUE;
int busNo;
int deviceNo;
int funcNo;
UINT32 classCodeReg;
UINT32 vendor;
UINT8 header;
FUNCPTR method = NULL;
struct vxbPciConfig * pPciConfig = NULL;
if(busCtrlID == NULL)
return(ERROR);
method = vxbDevMethodGet(busCtrlID,PCI_CONTROLLER_METHOD_CFG_INFO);
if(method == NULL)
{
PCILIB_ERR("FindClass Failed: No PCI method for ConfigInfo\n");
return(ERROR);
}
status = (*method)(busCtrlID,&pPciConfig);
if(status == ERROR)
{
PCILIB_ERR("FindClass Failed: Method for ConfigInfo doesn't work\n");
return(ERROR);
}
status = ERROR;
if(pPciConfig==NULL)
{
PCILIB_ERR("FindClass Failed: pPciConfig is NULL\n");
return(ERROR);
}
if (pPciConfig->pciLibInitStatus != OK) /* sanity check */
cont = FALSE;
for (busNo = pPciConfig->pciMinBus;
(cont == TRUE) && (busNo <= pPciConfig->pciMaxBus);
busNo++)
{
for (deviceNo = 0;
((cont == TRUE) && (deviceNo < PCI_MAX_DEV));
++deviceNo)
{
for (funcNo = 0; cont == TRUE && funcNo < PCI_MAX_FUNC; funcNo++)
{
/* avoid a special bus cycle */
if ((deviceNo == 0x1f) && (funcNo == 0x07))
continue;
vxbPciConfigInLong (busCtrlID,busNo, deviceNo, funcNo,
PCI_CFG_VENDOR_ID, &vendor);
/*
* If nonexistent device, skip to next, only look at
* vendor ID field for existence check
*/
if ((vendor & 0xffff) == 0xffff)
{
if (funcNo == 0)
break; /* next device */
continue; /* next function */
}
vxbPciConfigInLong (busCtrlID,busNo, deviceNo, funcNo,
PCI_CFG_REVISION, &classCodeReg);
if ((((classCodeReg >> 8) & 0x00ffffff) == classCode) &&
(index-- == 0))
{
*pBusNo = busNo;
*pDeviceNo = deviceNo;
*pFuncNo = funcNo;
status = OK;
cont = FALSE; /* terminate all loops */
continue;
}
/* go to next if current device is single function */
vxbPciConfigInByte (busCtrlID, busNo, deviceNo, funcNo,
PCI_CFG_HEADER_TYPE, &header);
if ((header & PCI_HEADER_MULTI_FUNC) != PCI_HEADER_MULTI_FUNC &&
funcNo == 0)
break;
}
}
}
return(status);
}
/*******************************************************************************
*
* vxbPciFindBridge - find the nth occurrence of a device by PCI class code.
*
* This routine finds the nth device with the given 24-bit PCI class code
* (class subclass prog_if).
*
* The classcode arg of must be carefully constructed from class and sub-class
* macros.
*
* Example : To find an ethernet class device, construct the classcode arg
* as follows:
*
* \cs
* ((PCI_CLASS_NETWORK_CTLR << 16 | PCI_SUBCLASS_NET_ETHERNET << 8))
* \ce
*
* RETURNS:
* OK, or ERROR if the class didn't match.
*
* ERRNO
*
*/
STATUS vxbPciFindPriSkip
(
VXB_DEVICE_ID busCtrlID,
int secBusNo, /* sec bus */
int *skip
)
{
STATUS status = ERROR;
BOOL cont = TRUE;
int busNo;
int deviceNo;
int funcNo;
UINT32 classCodeReg;
UINT32 vendor;
UINT8 header;
UINT8 secBus; /* secondary bus, for recursion */
FUNCPTR method = NULL;
UINT16 pciClass;
UINT16 hostBridge = (PCI_CLASS_BRIDGE_CTLR<<8) |
PCI_SUBCLASS_HOST_PCI_BRIDGE;
struct vxbPciConfig * pPciConfig = NULL;
if(busCtrlID == NULL)
return(ERROR);
method = vxbDevMethodGet(busCtrlID,PCI_CONTROLLER_METHOD_CFG_INFO);
if(method == NULL)
{
PCILIB_ERR("FindClass Failed: No PCI method for ConfigInfo\n");
return(ERROR);
}
status = (*method)(busCtrlID,&pPciConfig);
if(status == ERROR)
{
PCILIB_ERR("FindClass Failed: Method for ConfigInfo doesn't work\n");
return(ERROR);
}
status = ERROR;
*skip = 0;
if(pPciConfig==NULL)
{
PCILIB_ERR("FindClass Failed: pPciConfig is NULL\n");
return(ERROR);
}
if (pPciConfig->pciLibInitStatus != OK) /* sanity check */
cont = FALSE;
for (busNo = pPciConfig->pciMinBus;
(cont == TRUE) && (busNo <= pPciConfig->pciMaxBus);
busNo++)
{
for (deviceNo = 0;
((cont == TRUE) && (deviceNo < PCI_MAX_DEV));
++deviceNo)
{
for (funcNo = 0; cont == TRUE && funcNo < PCI_MAX_FUNC; funcNo++)
{
/* Check for a valid device/vendor number */
status = vxbPciConfigInLong(busCtrlID, busNo, deviceNo, funcNo,
PCI_CFG_VENDOR_ID, (UINT32 *)&vendor);
/* If function 0 then check next dev else check next function */
if ( ((vendor & 0x0ffff) == PCI_CONFIG_ABSENT_WORD_F) ||
((vendor & 0x0ffff) == PCI_CONFIG_ABSENT_WORD_0) )
{
if (funcNo == 0)
{
break; /* non-existent device, go to next device */
}
else
{
continue; /* function empty, try the next function */
}
}
status = vxbPciConfigInWord(busCtrlID, busNo, deviceNo, funcNo,
PCI_CFG_SUBCLASS, (UINT16 *)&pciClass);
/* if P2P bridge, check that bus recursively */
if (pciClass == (PCI_CLASS_BRIDGE_CTLR << 8)
+ PCI_SUBCLASS_P2P_BRIDGE
|| pciClass == (PCI_CLASS_BRIDGE_CTLR << 8)
+ PCI_SUBCLASS_CARDBUS_BRIDGE)
{
status = vxbPciConfigInByte(busCtrlID,busNo,deviceNo,funcNo,
PCI_CFG_SECONDARY_BUS,(UINT8 *)&secBus);
/*
* If the second bus is greater than min bus and does not
* exceed the max bus, check the second bus recursively.
*
* This test is required in case a BSP wants to control
* the bus topology by configuring the start and end bus
* numers and creating a bus access limitation per bus
* controller.
* e.g. A BSP needs to support two different spec boards.
* One of them has a PCI-to-PCI controller bridges to
* bus X and the other board does not have such a bridge
* controller but both boards have a PCI device on
* the bus X. The BSP assigns bus 0 - (X-1) to the 1st bus
* controller device instance and bus X - 255 to the 2nd
* bus controller device instance to support the two
* boards.
*/
if(secBusNo ==secBus )
{
unsigned char pos, id;
pos = 0x34;
while(1) {
vxbPciConfigInByte(busCtrlID,busNo,deviceNo,funcNo, pos, &pos);
if (pos < 0x40)
break;
pos &= ~3;
vxbPciConfigInByte(busCtrlID,busNo,deviceNo,funcNo, pos, &id);
if (id == 0xff)
break;
/* 0x10: PCI Express Capability List */
if (id == 0x10) {
unsigned short capreg;
unsigned char port_type;
vxbPciConfigInWord(busCtrlID,busNo,deviceNo,funcNo,pos+2, &capreg);
/*
bit[7:4] Device/Port Type �C Indicates the specific type of this PCI
Express Function. Note that different Functions in a multi-
Function device can generally be of different types.
Defined encodings are:
0000b PCI Express Endpoint
0001b Legacy PCI Express Endpoint
0100b Root Port of PCI Express Root Complex*
0101b Upstream Port of PCI Express Switch*
0110b Downstream Port of PCI Express Switch*
0111b PCI Express to PCI/PCI-X Bridge*
1000b PCI/PCI-X to PCI Express Bridge*
1001b Root Complex Integrated Endpoint
1010b Root Complex Event Collector
*/
port_type = (capreg >> 4) & 0xf;
if (((port_type == 0x6) || (port_type == 0x4)))
*skip=1;
else
*skip=0;
}
pos += 1;
}
return OK;
}
if ( status != OK )
return(ERROR);
}
}
}
}
return(status);
}
/******************************************************************************
*
* vxbPciDevConfig - configure a device on a PCI bus
*
* This routine configures a device that is on a Peripheral Component
* Interconnect (PCI) bus by writing to the configuration header of the
* selected device.
*
* It first disables the device by clearing the command register in the
* configuration header. It then sets the I/O and/or memory space base
* address registers, the latency timer value and the cache line size.
* Finally, it re-enables the device by loading the command register with
* the specified command.
*
* NOTE: This routine is designed for Type 0 PCI Configuration Headers ONLY.
* It is NOT usable for configuring, for example, a PCI-to-PCI bridge.
*
* RETURNS: OK always.
*
* ERRNO
*
*/
STATUS vxbPciDevConfig
(
VXB_DEVICE_ID busCtrlID,
int pciBusNo, /* PCI bus number */
int pciDevNo, /* PCI device number */
int pciFuncNo, /* PCI function number */
UINT32 devIoBaseAdrs, /* device IO base address */
UINT32 devMemBaseAdrs, /* device memory base address */
UINT32 command /* command to issue */
)
{
INT32 ix;
UINT32 tmp32;
if(busCtrlID == NULL)
return(ERROR);
/*
* Disable device by clearing its command register field in its
* configuration header. Write 0 clears command and preserves status.
*/
vxbPciConfigOutWord (busCtrlID, pciBusNo, pciDevNo, pciFuncNo,
PCI_CFG_COMMAND, 0x0);
vxbPciConfigOutWord (busCtrlID, pciBusNo, pciDevNo, pciFuncNo,
PCI_CFG_STATUS, 0xffff);
/*
* Due to system constraints, this is a partial implementation
* of enabling the Base Address Registers (BARs).
* It is hoped that all PCI devices only require at most one
* I/O space and/or one memory space.
* If not, this code will re-allocate the device's memory to
* each BAR implemented. Sounds like future trouble!
*/
for (ix = PCI_CFG_BASE_ADDRESS_0; ix <= PCI_CFG_BASE_ADDRESS_5; ix+=4)
{
/* Write all f's and read back value */
vxbPciConfigOutLong (busCtrlID, pciBusNo, pciDevNo, pciFuncNo, ix,
0xffffffff);
vxbPciConfigInLong (busCtrlID, pciBusNo, pciDevNo, pciFuncNo, ix,
&tmp32);
/* BAR implemented? */
if (tmp32 == 0)
{
/*
* No! According to the spec, BARs must be implemented
* in sequence starting at BAR 0. So, all done.
*/
break;
}
/* I/O space requested? */
/* Yes, set specified I/O space base address */
if (tmp32 & 0x1)
{
vxbPciConfigOutLong (busCtrlID, pciBusNo, pciDevNo, pciFuncNo, ix,
devIoBaseAdrs | 0x1);
}
/* No, memory space required, set specified base address */
else
{
vxbPciConfigOutLong (busCtrlID, pciBusNo, pciDevNo, pciFuncNo, ix,
devMemBaseAdrs & (UINT32)~0x1);
}
}
/* Configure Cache Line Size Register */
vxbPciConfigOutByte (busCtrlID, pciBusNo, pciDevNo, pciFuncNo,
PCI_CFG_CACHE_LINE_SIZE, PCI_CACHE_LINE_SIZE);
/* Configure Latency Timer */
vxbPciConfigOutByte (busCtrlID, pciBusNo, pciDevNo, pciFuncNo,
PCI_CFG_LATENCY_TIMER, PCI_LATENCY_TIMER);
/*
* Enable the device's capabilities as specified, do not
* reset any status bits in doing so.
*/
vxbPciConfigModifyWord (busCtrlID, pciBusNo, pciDevNo, pciFuncNo,
PCI_CFG_COMMAND, command, command);
vxbPciConfigOutWord (busCtrlID, pciBusNo, pciDevNo, pciFuncNo,
PCI_CFG_STATUS, 0xffff);
return(OK);
}
/*******************************************************************************
*
* vxbPciConfigBdfPack - pack parameters for the Configuration Address Register
*
* This routine packs three parameters into one integer for accessing the
* Configuration Address Register
*
* RETURNS: packed integer encoded version of bus, device, and function numbers.
*
* ERRNO
*
*/
int vxbPciConfigBdfPack
(
int busNo, /* bus number */
int deviceNo, /* device number */
int funcNo /* function number */
)
{
return(((busNo << 16) & 0x00ff0000) |
((deviceNo << 11) & 0x0000f800) |
((funcNo << 8) & 0x00000700));
}
/*******************************************************************************
*
* vxbPciConfigExtCapFind - find extended capability in ECP linked list
*
* This routine searches for an extended capability in the linked list of
* capabilities in config space. If found, the offset of the first byte
* of the capability of interest in config space is returned via pOffset.
*
* RETURNS: OK if Extended Capability found, ERROR otherwise
*
* ERRNO
*
*/
STATUS vxbPciConfigExtCapFind
(
VXB_DEVICE_ID busCtrlID,
UINT8 extCapFindId, /* Extended capabilities ID to search for */
int bus, /* PCI bus number */
int device, /* PCI device number */
int function, /* PCI function number */
UINT8 * pOffset /* returned config space offset */
)
{
STATUS retStat = ERROR;
UINT16 tmpStat;
UINT8 tmpOffset;
UINT8 capOffset = 0x00;
UINT8 capId = 0x00;
if(busCtrlID == NULL)
return(ERROR);
/*
* Check to see if the device has any extended capabilities.
* Also, check if the PCI status is read a valid value.
*/
retStat = vxbPciConfigInWord (busCtrlID, bus, device, function, PCI_CFG_STATUS,
&tmpStat);
VXB_ASSERT(retStat != ERROR, ERROR);
if ((tmpStat == (UINT16)0xFFFF) || ((tmpStat & PCI_STATUS_NEW_CAP) == 0))
{
return ERROR;
}
/* Get the initial ECP offset and make longword aligned */
vxbPciConfigInByte(busCtrlID, bus, device, function, PCI_CFG_CAP_PTR,
&capOffset);
capOffset &= ~0x02;
/* Bounds check the ECP offset */
if (capOffset < 0x40)
{
return retStat;
}
/* Look for the specified Extended Cap item in the linked list */
while (capOffset != 0x00)
{
/* Get the Capability ID and check */
vxbPciConfigInByte(busCtrlID, bus, device, function, (int)capOffset,
&capId);
if (capId == extCapFindId)
{
*pOffset = (capOffset + (UINT8)0x02);
retStat = OK;
break;
}
/* Get the offset to the next New Capabilities item */
tmpOffset = capOffset + (UINT8)0x01;
vxbPciConfigInByte(busCtrlID, bus, device, function, (int)tmpOffset,
&capOffset);
}
return retStat;
}
/**********************************************************************
*
* pciERootForeach - check condition on root PCIe bus
*
* pciERootForeach() discovers the PCIe functions present on the
* bus and calls a specified C-function for each one. If the
* function returns ERROR, further processing stops.
*
* ERRNO: not set
*
* RETURNS: OK normally, or ERROR if funcCheckRtn() doesn't return OK.
*
*/
LOCAL STATUS pciERootForeach
(
VXB_DEVICE_ID busCtrlID,
int bus, /* PCI bus of PCIe root */
int device, /* PCI device of PCIe root */
int function, /* PCI function of PCIe root */
VXB_PCI_FOREACH_FUNC funcCheckRtn, /* routine to call for each PCI func */
void *pArg /* argument to funcCheckRtn */
)
{
UINT16 pciClass; /* PCI class/subclass contents */
STATUS status;
UINT8 secBus; /* secondary bus, for recursion */
FUNCPTR method;
struct vxbPciConfig * pPciConfig = NULL;
status = vxbPciConfigInWord(busCtrlID, bus, device, function,
PCI_CFG_SUBCLASS, &pciClass);
VXB_ASSERT(status != ERROR, ERROR);
method = vxbDevMethodGet (busCtrlID, PCI_CONTROLLER_METHOD_CFG_INFO);
if ((method == NULL) ||
((status = (*method) (busCtrlID, &pPciConfig)) == ERROR))
{
return(ERROR);
}
/*
* Allow probing beyond primary bus in case acting as
* PCIe root - should be harmless
*/
if((pciClass & 0xff00) == (PCI_CLASS_PROCESSOR << 8))
{
UINT8 devCapID,cap_id,cap_id_next;
/*Check for PCI Express ROOT Complex */
vxbPciConfigInByte (busCtrlID, bus, device, function, PCI_CFG_CAP_PTR,
&devCapID);
/* Longword Align */
devCapID &= ~0x03;
while (devCapID)
{
vxbPciConfigInByte (busCtrlID,
bus, device, function,
devCapID, &cap_id);
vxbPciConfigInByte (busCtrlID,
bus, device, function,
devCapID+1, &cap_id_next);
/* longword align */
cap_id_next &= ~0x03;
/* Enhancement: Dump specific capabilities regs */
if (cap_id == PCI_EXT_CAP_EXP)
{
UINT16 capReg,type;
/* Root complex */
status = vxbPciConfigInWord (busCtrlID,
bus, device, function,
devCapID | PCI_EXP_CAP_REG,
&capReg);
VXB_ASSERT(status != ERROR, ERROR);
type = (capReg & PCI_EXP_CAP_PORT_TYPE) >> 4;
if(type == PCI_EXP_TYPE_ROOT_PORT)
{
PCILIB_DEBUG_MSG (1, "Found PCIe Root Complex host\n",
1,2,3,4,5,6);
status = vxbPciConfigInByte(busCtrlID,
bus, device, function,
PCI_CFG_SECONDARY_BUS,
(UINT8 *)&secBus);
VXB_ASSERT(status!=ERROR, ERROR);
/*
* If the second bus is greater than min bus and does not
* exceed the max bus, check the second bus recursively.
*
* This test is required in case a BSP wants to control
* the bus topology by configuring the start and end bus
* numers and creating a bus access limitation per bus
* controller.
* e.g. A BSP needs to support two different spec boards.
* One of them has a PCI-to-PCI controller bridges to
* bus X and the other board does not have such a bridge
* controller but both boards have a PCI device on
* the bus X. The BSP assigns bus 0 - (X-1) to the 1st bus
* controller device instance and bus X - 255 to the 2nd
* bus controller device instance to support the two
* boards.
*/
if ((secBus > (UINT8)pPciConfig->pciMinBus) &&
(secBus <= (UINT8)pPciConfig->pciMaxBus))
status = vxbPciConfigForeachFunc(
busCtrlID,secBus, TRUE,
funcCheckRtn, pArg,0);
else
status = OK;
break;
}
}
devCapID = cap_id_next;
}
}
return(status);
}
/**********************************************************************
*
* vxbPciConfigForeachFunc - check condition on specified bus
*
* vxbPciConfigForeachFunc() discovers the PCI functions present on the
* bus and calls a specified C-function for each one. If the
* function returns ERROR, further processing stops.
*
* vxbPciConfigForeachFunc() does not affect any HOST<->PCI
* bridge on the system.
*
* ERRNO: not set
*
* RETURNS: OK normally, or ERROR if funcCheckRtn() doesn't return OK.
*
*/
STATUS vxbPciConfigForeachFunc
(
VXB_DEVICE_ID busCtrlID,
UINT8 bus, /* bus to start on */
BOOL recurse, /* if TRUE, do subordinate busses */
VXB_PCI_FOREACH_FUNC funcCheckRtn, /* routine to call for each PCI func */
void *pArg , /* argument to funcCheckRtn */
int skip
)
{
int pciLocBus; /* PCI bus/device/function structure */
int pciLocDevice; /* PCI bus/device/function structure */
int pciLocFunction; /* PCI bus/device/function structure */
int device; /* loop over devices */
int function; /* loop over functions */
UINT32 devVend;
UINT16 pciClass; /* PCI class/subclass contents */
STATUS status;
UINT8 btemp;
UINT8 secBus; /* secondary bus, for recursion */
UINT16 hostBridge = (PCI_CLASS_BRIDGE_CTLR<<8) |
PCI_SUBCLASS_HOST_PCI_BRIDGE;
FUNCPTR method;
struct vxbPciConfig * pPciConfig = NULL;
if(busCtrlID == NULL)
return(ERROR);
method = vxbDevMethodGet (busCtrlID, PCI_CONTROLLER_METHOD_CFG_INFO);
if ((method == NULL) ||
((status = (*method) (busCtrlID, &pPciConfig)) == ERROR))
{
return(ERROR);
}
/* Begin the scanning process at [bus,0,0] */
pciLocBus = bus;
pciLocDevice = 0;
pciLocFunction = 0;
/* discover devices and perform check */
/* Locate each active function on the current bus */
for (device = 0; device < PCI_MAX_DEV; device++)
{
pciLocDevice = device;
if (skip&&(device!=0))
continue;
/* Check each function until an unused one is detected */
for (function = 0; function < PCI_MAX_FUNC; function++)
{
pciLocFunction = function;
/* Check for a valid device/vendor number */
status = vxbPciConfigInLong(busCtrlID, bus, device, function,
PCI_CFG_VENDOR_ID, (UINT32 *)&devVend);
/* If function 0 then check next dev else check next function */
if ( ((devVend & 0x0ffff) == PCI_CONFIG_ABSENT_WORD_F) ||
((devVend & 0x0ffff) == PCI_CONFIG_ABSENT_WORD_0) )
{
if (function == 0)
{
break; /* non-existent device, go to next device */
}
else
{
continue; /* function empty, try the next function */
}
}
status = vxbPciConfigInWord(busCtrlID, bus, device, function,
PCI_CFG_SUBCLASS, (UINT16 *)&pciClass);
/*
* Do not announce host bridges or 'processor (of type ROOT_PORT)' class devices.
*/
if (pciClass != hostBridge)
{
UINT8 devCapID, cap_id, cap_id_next, root_port;
/*
* Check for PCI Express ROOT Complex
* Traverse capabilities until we see the TYPE to
* be of type PCI_EXP_TYPE_ROOT_PORT
*/
root_port = 0;
if ((pciClass & 0xFF00) >> 8 == PCI_CLASS_PROCESSOR)
{
vxbPciConfigInByte (busCtrlID, bus, device, function, PCI_CFG_CAP_PTR,
&devCapID);
/* Longword Align */
devCapID &= ~0x03;
/*
* Search through the capabilities and make sure it's not
* a ROOT Port, if it is, set the flag
*/
while (devCapID)
{
vxbPciConfigInByte (busCtrlID, bus, device, function, devCapID,
&cap_id);
vxbPciConfigInByte (busCtrlID, bus, device, function, devCapID + 1,
&cap_id_next);
/* Longword Align */
devCapID &= ~0x03;
if (cap_id == PCI_EXT_CAP_EXP)
{
UINT16 capReg, type;
/* Root complex */
status = vxbPciConfigInWord (busCtrlID, bus, device, function,
devCapID | PCI_EXP_CAP_REG,&capReg);
VXB_ASSERT(status != ERROR, ERROR);
type = (capReg & PCI_EXP_CAP_PORT_TYPE) >> 4;
if (type == PCI_EXP_TYPE_ROOT_PORT)
{
root_port++;
}
}
devCapID = cap_id_next;
}
} /* End check for PCI_CLASS_PROCESSOR case */
/*
* If we are not a host bridge and we do not have a
* class PCI_CLASS_PROCESSOR that is of type ROOT_PORT
* then we can go ahead with the function call.
*/
if (root_port == 0)
{
status = (*funcCheckRtn)(busCtrlID,pciLocBus, pciLocDevice, pciLocFunction, pArg);
if ( status != OK )
{
return(ERROR);
}
}
}
if ( recurse )
{
/* handle PCIe root, if configured */
if ( _func_PCIeRootForeach )
{
status = (*_func_PCIeRootForeach)(busCtrlID, bus, device,
function, funcCheckRtn,
pArg);
if ( status != OK )
return(ERROR);
}
/* if P2P bridge, check that bus recursively */
if (pciClass == (PCI_CLASS_BRIDGE_CTLR << 8)
+ PCI_SUBCLASS_P2P_BRIDGE
|| pciClass == (PCI_CLASS_BRIDGE_CTLR << 8)
+ PCI_SUBCLASS_CARDBUS_BRIDGE)
{
int skip=0;
status = vxbPciConfigInByte(busCtrlID,bus,device,function,
PCI_CFG_SECONDARY_BUS,(UINT8 *)&secBus);
/*
* If the second bus is greater than min bus and does not
* exceed the max bus, check the second bus recursively.
*
* This test is required in case a BSP wants to control
* the bus topology by configuring the start and end bus
* numers and creating a bus access limitation per bus
* controller.
* e.g. A BSP needs to support two different spec boards.
* One of them has a PCI-to-PCI controller bridges to
* bus X and the other board does not have such a bridge
* controller but both boards have a PCI device on
* the bus X. The BSP assigns bus 0 - (X-1) to the 1st bus
* controller device instance and bus X - 255 to the 2nd
* bus controller device instance to support the two
* boards.
*/
{
unsigned char pos, id;
pos = 0x34;
while(1) {
vxbPciConfigInByte(busCtrlID,bus,device,function, pos, &pos);
if (pos < 0x40)
break;
pos &= ~3;
vxbPciConfigInByte(busCtrlID,bus,device,function, pos, &id);
if (id == 0xff)
break;
/* spec: 7.8.1. PCI Express Capability List Register */
if (id == PCI_EXT_CAP_EXP) {
unsigned short capreg;
unsigned char port_type;
vxbPciConfigInWord(busCtrlID,bus,device,function,pos+2, &capreg);
port_type = (capreg >> 4) & 0xf;
/*
bit[7:4] Device/Port Type ��C Indicates the specific type of this PCI
Express Function. Note that different Functions in a multi-
Function device can generally be of different types.
Defined encodings are:
0000b PCI Express Endpoint
0001b Legacy PCI Express Endpoint
0100b Root Port of PCI Express Root Complex*
0101b Upstream Port of PCI Express Switch*
0110b Downstream Port of PCI Express Switch*
0111b PCI Express to PCI/PCI-X Bridge*
1000b PCI/PCI-X to PCI Express Bridge*
1001b Root Complex Integrated Endpoint
1010b Root Complex Event Collector
*/
if (((port_type == 0x6) || (port_type == 0x4)))
skip=1;
else
skip=0;
}
pos += 1;
}
}
if ((secBus > (UINT8)pPciConfig->pciMinBus) &&
(secBus <= (UINT8)pPciConfig->pciMaxBus))
status = vxbPciConfigForeachFunc(busCtrlID, secBus,
recurse, funcCheckRtn,
pArg,skip);
else
status = OK;
if ( status != OK )
return(ERROR);
}
}
/* Proceed to next device if this is a single function device */
if (function == 0)
{
status = vxbPciConfigInByte(busCtrlID, bus, device, function,
PCI_CFG_HEADER_TYPE,
(UINT8 *)&btemp);
if ((btemp & PCI_HEADER_MULTI_FUNC) == 0)
{
break; /* No more functions - proceed to next PCI device */
}
}
}
}
return(OK);
}
/**********************************************************************
*
* vxbPciConfigForeachFunc2 - check condition on specified bus
*
* vxbPciConfigForeachFunc() discovers the PCI functions present on the
* bus and calls a specified C-function for each one. If the
* function returns ERROR, further processing stops.
*
* vxbPciConfigForeachFunc() does not affect any HOST<->PCI
* bridge on the system.
*
* ERRNO: not set
*
* RETURNS: OK normally, or ERROR if funcCheckRtn() doesn't return OK.
*
*/
STATUS vxbPciConfigForeachFunc2
(
VXB_DEVICE_ID busCtrlID,
UINT8 bus, /* bus to start on */
BOOL recurse, /* if TRUE, do subordinate busses */
VXB_PCI_FOREACH_FUNC funcCheckRtn, /* routine to call for each PCI func */
void *pArg , /* argument to funcCheckRtn */
int skip
)
{
int pciLocBus; /* PCI bus/device/function structure */
int pciLocDevice; /* PCI bus/device/function structure */
int pciLocFunction; /* PCI bus/device/function structure */
int device; /* loop over devices */
int function; /* loop over functions */
UINT32 devVend;
UINT16 pciClass; /* PCI class/subclass contents */
STATUS status;
UINT8 btemp;
UINT8 secBus; /* secondary bus, for recursion */
UINT16 hostBridge = (PCI_CLASS_BRIDGE_CTLR<<8) |
PCI_SUBCLASS_HOST_PCI_BRIDGE;
FUNCPTR method;
struct vxbPciConfig * pPciConfig = NULL;
if(busCtrlID == NULL)
return(ERROR);
method = vxbDevMethodGet (busCtrlID, PCI_CONTROLLER_METHOD_CFG_INFO);
if ((method == NULL) ||
((status = (*method) (busCtrlID, &pPciConfig)) == ERROR))
{
return(ERROR);
}
/* Begin the scanning process at [bus,0,0] */
pciLocBus = bus;
pciLocDevice = 0;
pciLocFunction = 0;
/* discover devices and perform check */
/* Locate each active function on the current bus */
for (device = 0; device < PCI_MAX_DEV; device++)
{
pciLocDevice = device;
if (skip&&(device!=0))
continue;
/* Check each function until an unused one is detected */
for (function = 0; function < PCI_MAX_FUNC; function++)
{
pciLocFunction = function;
/* Check for a valid device/vendor number */
status = vxbPciConfigInLong(busCtrlID, bus, device, function,
PCI_CFG_VENDOR_ID, (UINT32 *)&devVend);
/* If function 0 then check next dev else check next function */
if ( ((devVend & 0x0ffff) == PCI_CONFIG_ABSENT_WORD_F) ||
((devVend & 0x0ffff) == PCI_CONFIG_ABSENT_WORD_0) )
{
if (function == 0)
{
break; /* non-existent device, go to next device */
}
else
{
continue; /* function empty, try the next function */
}
}
status = vxbPciConfigInWord(busCtrlID, bus, device, function,
PCI_CFG_SUBCLASS, (UINT16 *)&pciClass);
/*
* Do not announce host bridges or 'processor (of type ROOT_PORT)' class devices.
*/
if (pciClass != hostBridge)
{
UINT8 devCapID, cap_id, cap_id_next, root_port;
/*
* Check for PCI Express ROOT Complex
* Traverse capabilities until we see the TYPE to
* be of type PCI_EXP_TYPE_ROOT_PORT
*/
root_port = 0;
if ((pciClass & 0xFF00) >> 8 == PCI_CLASS_PROCESSOR)
{
vxbPciConfigInByte (busCtrlID, bus, device, function, PCI_CFG_CAP_PTR,
&devCapID);
/* Longword Align */
devCapID &= ~0x03;
/*
* Search through the capabilities and make sure it's not
* a ROOT Port, if it is, set the flag
*/
while (devCapID)
{
vxbPciConfigInByte (busCtrlID, bus, device, function, devCapID,
&cap_id);
vxbPciConfigInByte (busCtrlID, bus, device, function, devCapID + 1,
&cap_id_next);
/* Longword Align */
devCapID &= ~0x03;
if (cap_id == PCI_EXT_CAP_EXP)
{
UINT16 capReg, type;
/* Root complex */
status = vxbPciConfigInWord (busCtrlID, bus, device, function,
devCapID | PCI_EXP_CAP_REG,&capReg);
VXB_ASSERT(status != ERROR, ERROR);
type = (capReg & PCI_EXP_CAP_PORT_TYPE) >> 4;
if (type == PCI_EXP_TYPE_ROOT_PORT)
{
root_port++;
}
}
devCapID = cap_id_next;
}
} /* End check for PCI_CLASS_PROCESSOR case */
/*
* If we are not a host bridge and we do not have a
* class PCI_CLASS_PROCESSOR that is of type ROOT_PORT
* then we can go ahead with the function call.
*/
if (root_port == 0)
{
status = (*funcCheckRtn)(busCtrlID,pciLocBus, pciLocDevice, pciLocFunction, pArg);
if ( status != OK )
{
return(ERROR);
}
}
}
if ( recurse )
{
/* handle PCIe root, if configured */
if ( _func_PCIeRootForeach )
{
status = (*_func_PCIeRootForeach)(busCtrlID, bus, device,
function, funcCheckRtn,
pArg);
if ( status != OK )
return(ERROR);
}
/* if P2P bridge, check that bus recursively */
if (pciClass == (PCI_CLASS_BRIDGE_CTLR << 8)
+ PCI_SUBCLASS_P2P_BRIDGE
|| pciClass == (PCI_CLASS_BRIDGE_CTLR << 8)
+ PCI_SUBCLASS_CARDBUS_BRIDGE)
{
int skip=0;
status = vxbPciConfigInByte(busCtrlID,bus,device,function,
PCI_CFG_SECONDARY_BUS,(UINT8 *)&secBus);
/*
* If the second bus is greater than min bus and does not
* exceed the max bus, check the second bus recursively.
*
* This test is required in case a BSP wants to control
* the bus topology by configuring the start and end bus
* numers and creating a bus access limitation per bus
* controller.
* e.g. A BSP needs to support two different spec boards.
* One of them has a PCI-to-PCI controller bridges to
* bus X and the other board does not have such a bridge
* controller but both boards have a PCI device on
* the bus X. The BSP assigns bus 0 - (X-1) to the 1st bus
* controller device instance and bus X - 255 to the 2nd
* bus controller device instance to support the two
* boards.
*/
{
unsigned char pos, id;
pos = 0x34;
while(1) {
vxbPciConfigInByte(busCtrlID,bus,device,function, pos, &pos);
if (pos < 0x40)
break;
pos &= ~3;
vxbPciConfigInByte(busCtrlID,bus,device,function, pos, &id);
if (id == 0xff)
break;
if (id == 0x10) {
unsigned short capreg;
unsigned char port_type;
vxbPciConfigInWord(busCtrlID,bus,device,function,pos+2, &capreg);
port_type = (capreg >> 4) & 0xf;
if (((port_type == 0x6) || (port_type == 0x4)))
skip=1;
else
skip=0;
}
pos += 1;
}
}
if ((secBus > (UINT8)pPciConfig->pciMinBus) &&
(secBus <= (UINT8)pPciConfig->pciMaxBus))
status = vxbPciConfigForeachFunc(busCtrlID, secBus,
recurse, funcCheckRtn,
pArg,skip);
else
status = OK;
if ( status != OK )
return(ERROR);
}
}
/* Proceed to next device if this is a single function device */
if (function == 0)
{
status = vxbPciConfigInByte(busCtrlID, bus, device, function,
PCI_CFG_HEADER_TYPE,
(UINT8 *)&btemp);
if ((btemp & PCI_HEADER_MULTI_FUNC) == 0)
{
break; /* No more functions - proceed to next PCI device */
}
}
}
}
return(OK);
}
/**********************************************************************
*
* pciFuncDisable - disable the specified function
*
* pciFuncDisable() disables the function by turning off various
* enable bits of the command register
*
* ERRNO: not set
*
* RETURNS: OK, always
*
* \NOMANUAL
*/
LOCAL STATUS vxbPciFuncDisable
(
VXB_DEVICE_ID busCtrlID,
int bus, /* bus */
int device, /* device */
int function, /* function */
void * pArg /* ignored */
)
{
UINT16 mask;
if (busCtrlID == NULL)
return(ERROR);
mask = PCI_CMD_IO_ENABLE | PCI_CMD_MEM_ENABLE |
PCI_CMD_MASTER_ENABLE;
vxbPciConfigModifyWord (busCtrlID, bus, device, function,
PCI_CFG_COMMAND, mask, 0);
vxbPciConfigOutWord (busCtrlID, bus, device, function,
PCI_CFG_STATUS, 0xffff);
return(OK);
}
/**********************************************************************
*
* vxbPciConfigReset - disable cards for warm boot
*
* vxbPciConfigReset() goes through the list of PCI functions
* at the top-level bus and disables them, preventing them
* from writing to memory while the system is trying to reboot.
*
* ERRNO: Not set
*
* RETURNS: OK, always
*/
STATUS vxbPciConfigReset
(
VXB_DEVICE_ID busCtrlID,
int startType /* for reboot hook, ignored */
)
{
FUNCPTR method;
STATUS status;
struct vxbPciConfig * pPciConfig = NULL;
if(busCtrlID == NULL)
return(ERROR);
method = vxbDevMethodGet (busCtrlID, PCI_CONTROLLER_METHOD_CFG_INFO);
if ((method == NULL) ||
((status = (*method) (busCtrlID, &pPciConfig)) == ERROR))
{
PCILIB_ERR("vxbPciConfigReset Failed: Method for ConfigInfo doesn't work\n");
return (ERROR);
}
/* disable all functions on top-level bus */
(void)vxbPciConfigForeachFunc (busCtrlID,
pPciConfig->pciMinBus,
FALSE,
(VXB_PCI_FOREACH_FUNC) vxbPciFuncDisable,
NULL,0);
return(OK);
}
/*******************************************************************************
*
* vxbPciIntrExclusiveEnable - enable a kind of PCI interrupt mutual exclusively
*
* This routine enables a kind of PCI interrupt mutual exclusively.
*
* MSI-X/MSI/INTx are mutual exclusive. When enabling INTx, we need to make
* sure MSI/MSI-X are disabled; when enabling MSI, we need to make sure MSI-X
* and INTx are disabled; when enabling MSI-X, we need to make sure MSI and
* INTx are disabled.
*
* Clearing/setting PCI_CMD_INTX_DISABLE bit of command register can enable/disable
* INTx interrupt. MSI/MSI-X has its own control register and can be globally
* enabled/disabled. MSI-X has per-mask capability, MSI may have per-mask capability.
* refer to PCI Local Bus Specification MSI-X ECN to get the details.
*
* RETURNS: OK/ERROR
*
* ERRNO: N/A
*
* \NOMANUAL
*/
LOCAL STATUS vxbPciIntrExclusiveEnable
(
VXB_DEVICE_ID pDev,
UINT32 intrType
)
{
VXB_DEVICE_ID busCtrlID = vxbDevParent(pDev);
struct vxbPciDevice * pPciDevice = pDev->pBusSpecificDevInfo;
UINT16 command = 0;
UINT16 msiCtl = 0;
UINT8 msiOffset = 0;
UINT8 pciBus = pPciDevice->pciBus;
UINT8 pciDev = pPciDevice->pciDev;
UINT8 pciFunc = pPciDevice->pciFunc;
if (vxbPciConfigInWord (busCtrlID, pciBus, pciDev, pciFunc,
PCI_CFG_COMMAND, &command)!= OK)
return ERROR;
if (intrType == VXB_INT_PCI_INTX)
{
command &= ~(PCI_CMD_INTX_DISABLE);
PCILIB_DEBUG_MSG(10,
"vxbPciIntrExclusiveEnable - enable INTx\n",
1, 2, 3, 4, 5, 6);
}
else
{
command |= (PCI_CMD_INTX_DISABLE);
PCILIB_DEBUG_MSG(10,
"vxbPciIntrExclusiveEnable - enable MSI/MSI-X\n",
1, 2, 3, 4, 5, 6);
}
vxbPciConfigOutWord (busCtrlID, pciBus, pciDev, pciFunc,
PCI_CFG_COMMAND, command);
if ((intrType == VXB_INT_PCI_INTX) ||
(intrType == VXB_INT_PCI_MSIX))
{
msiOffset = 0;
vxbPciConfigExtCapFind(busCtrlID, PCI_EXT_CAP_MSI,
pciBus,
pciDev,
pciFunc,
&msiOffset);
if (msiOffset != 0)
{
vxbPciBusCfgRead (pDev, (UINT32)msiOffset, 2, &msiCtl);
msiCtl &= ~(PCI_MSI_CTL_ENABLE);
PCILIB_DEBUG_MSG(10,
"vxbPciIntrExclusiveEnable - disable MSI for MSI-X/INTx\n",
1, 2, 3, 4, 5, 6);
vxbPciBusCfgWrite (pDev, (UINT32)msiOffset , 2, &msiCtl);
}
}
if ((intrType == VXB_INT_PCI_INTX) ||
(intrType == VXB_INT_PCI_MSI))
{
msiOffset = 0;
vxbPciConfigExtCapFind(busCtrlID, PCI_EXT_CAP_MSIX,
pciBus,
pciDev,
pciFunc,
&msiOffset);
if (msiOffset != 0)
{
vxbPciBusCfgRead (pDev, (UINT32)msiOffset, 2, &msiCtl);
msiCtl &= ~(PCI_MSIX_CTL_ENABLE);
PCILIB_DEBUG_MSG(10,
"vxbPciIntrExclusiveEnable - disable MSI-X for MSI/INTx\n",
1, 2, 3, 4, 5, 6);
vxbPciBusCfgWrite (pDev, (UINT32)msiOffset, 2, &msiCtl);
}
}
return OK;
}
/*******************************************************************************
*
* vxbPciMSIProgram - programm multiple MSI/MSI-X vector
*
* This routine programs multiple MSI/MSI-X vector, writes address and data register,
* set control register to enable MSI interrupt then disable INTx.
*
* RETURNS: ERROR when MSI/MSI-X is unavailable or programed MSI/MSI-X count exceed
* the maxium MSI interrupts device have, otherwise return OK.
*
* ERRNO: N/A
*/
STATUS vxbPciMSIProgram
(
VXB_DEVICE_ID pDev,
struct vxbIntDynaVecInfo * dynaVec
)
{
UINT32 msiCount;
UINT32 vecType;
if (dynaVec == NULL)
return ERROR;
vecType = dynaVec->vecType;
if ((dynaVec->count & VXB_MULTIPLE_MSI_CNT_FLAG)
== VXB_MULTIPLE_MSI_CNT_FLAG)
msiCount = dynaVec->count & VXB_MULTIPLE_MSI_CNT_MASK;
else
msiCount = 1;
if (vecType == VXB_INT_PCI_MSIX)
return vxbPciMSIXMultiProgram (pDev, msiCount, dynaVec);
else
return vxbPciMSIMultiProgram (pDev, msiCount, dynaVec);
return OK;
}
/*******************************************************************************
*
* vxbPciMSIMultiProgram - programm multiple MSI vectors
*
* This routine programs multiple MSI vectors. MSI can only support 1,2,4,8,16,32
* interrupts, all the interrupts share the same address and data register.
* So MSI data must be increase progressively based the data of the first MSI.
* The MSI control register is set to enable the MSI with the specified number.
*
* RETURNS: ERROR when MSI is unavailable, else OK
*
* ERRNO: N/A
*
* \NOMANUAL
*/
LOCAL STATUS vxbPciMSIMultiProgram
(
VXB_DEVICE_ID pDev,
int numVectors,
struct vxbIntDynaVecInfo * pDynaVec
)
{
VXB_DEVICE_ID busCtrlID = vxbDevParent(pDev);
struct vxbPciDevice * pPciDevice = pDev->pBusSpecificDevInfo;
UINT8 msiOffset = 0;
UINT16 msiCtl;
UINT32 vecAddr;
UINT16 vecVal;
UINT8 msiEn;
PCILIB_DEBUG_MSG(20,"vxbPciMSIMultiProgram: dev 0x%x bus Ctrl 0x%x"
"vecAddr 0x%x Val 0x%x\n",
(_Vx_usr_arg_t)pDev,
(_Vx_usr_arg_t)busCtrlID,
(_Vx_usr_arg_t)pDynaVec->vecAddr ,
(_Vx_usr_arg_t)pDynaVec->vecVal,5,6);
if (busCtrlID == NULL)
return(ERROR);
/* MSI can support only 1, 2, 4, 8, 16, 32 number of vectors */
switch (numVectors)
{
case 1:
msiEn = 0;
break;
case 2:
msiEn = 1;
break;
case 4:
msiEn = 2;
break;
case 8:
msiEn = 3;
break;
case 16:
msiEn = 4;
break;
case 32:
msiEn = 5;
break;
default:
return(ERROR);
}
vxbPciConfigExtCapFind(busCtrlID, PCI_EXT_CAP_MSI,
pPciDevice->pciBus,
pPciDevice->pciDev,
pPciDevice->pciFunc,
&msiOffset);
if (msiOffset == 0)
return(ERROR);
/* This offset is for MSI_CTL which is 2 bytes on so subtract to use defs */
msiOffset -= PCI_MSI_CTL;
vxbPciBusCfgRead (pDev, (UINT32)(msiOffset + PCI_MSI_CTL), 2, &msiCtl);
vecAddr = (UINT32)pDynaVec->vecAddr;
vxbPciBusCfgWrite (pDev, (UINT32)(msiOffset + PCI_MSI_ADDR_LO), 4,
&(vecAddr));
vecVal = (UINT16) pDynaVec->vecVal;
if (msiCtl & PCI_MSI_CTL_64BIT)
{
vecAddr = pDynaVec->vecAddr >> 32;
vxbPciBusCfgWrite (pDev, (UINT32)(msiOffset + PCI_MSI_ADDR_HI), 4,
&vecAddr);
vxbPciBusCfgWrite (pDev, (UINT32)(msiOffset + PCI_MSI_DATA_64), 2,
&vecVal);
}
else
{
vxbPciBusCfgWrite (pDev, (UINT32)(msiOffset + PCI_MSI_DATA_32), 2,
&vecVal);
}
msiCtl &= ~PCI_MSI_CTL_MSG_ALLOC;
msiCtl |= msiEn << 4;
vxbPciBusCfgWrite (pDev, (UINT32)(msiOffset + PCI_MSI_CTL), 2, &msiCtl);
vxbPciIntrExclusiveEnable(pDev, VXB_INT_PCI_MSI);
return OK;
}
/*******************************************************************************
*
* vxbPciMSIMultiErase - erase multiple MSI vectors
*
* This routine erases the multiple MSI vectors at the same time. MSI vectors
* can't be erased individually, so this routine just disables all the MSI vectors.
*
* RETURNS: ERROR when MSI is unavailable, else OK
*
* ERRNO: N/A
*
* \NOMANUAL
*/
LOCAL STATUS vxbPciMSIMultiErase
(
VXB_DEVICE_ID pDev,
int numVectors,
struct vxbIntDynaVecInfo * pDynaVec
)
{
VXB_DEVICE_ID busCtrlID = vxbDevParent(pDev);
struct vxbPciDevice * pPciDevice = pDev->pBusSpecificDevInfo;
UINT8 msiOffset = 0;
UINT16 msiCtl;
UINT32 vecAddr;
UINT16 vecVal;
PCILIB_DEBUG_MSG(20,"vxbPciMSIMultiErase : dev 0x%x bus Ctrl 0x%x"
"vecAddr 0x%x Val 0x%x\n",
(_Vx_usr_arg_t)pDev,
(_Vx_usr_arg_t)busCtrlID,
(_Vx_usr_arg_t)pDynaVec->vecAddr ,
(_Vx_usr_arg_t)pDynaVec->vecVal,5,6);
if (busCtrlID == NULL)
return(ERROR);
vxbPciConfigExtCapFind(busCtrlID, PCI_EXT_CAP_MSI,
pPciDevice->pciBus,
pPciDevice->pciDev,
pPciDevice->pciFunc,
&msiOffset);
if (msiOffset == 0)
return(ERROR);
/* This offset is for MSI_CTL which is 2bytes on so subtract to use defs */
msiOffset -= PCI_MSI_CTL;
vecVal = 0;
vecAddr = 0;
vxbPciBusCfgRead (pDev, (UINT32)(msiOffset + PCI_MSI_CTL), 2, &msiCtl);
vxbPciBusCfgWrite (pDev, (UINT32)(msiOffset + PCI_MSI_ADDR_LO), 4,
&(vecAddr));
if (msiCtl & PCI_MSI_CTL_64BIT)
{
vxbPciBusCfgWrite (pDev, (UINT32)(msiOffset + PCI_MSI_ADDR_HI), 4,
&vecAddr);
vxbPciBusCfgWrite (pDev, (UINT32)(msiOffset + PCI_MSI_DATA_64), 2,
&vecVal);
}
else
{
vxbPciBusCfgWrite (pDev, (UINT32)(msiOffset + PCI_MSI_DATA_32), 2,
&vecVal);
}
msiCtl &= ~(PCI_MSI_CTL_MSG_ALLOC | PCI_MSI_CTL_ENABLE);
vxbPciBusCfgWrite (pDev, (UINT32)(msiOffset + PCI_MSI_CTL), 2, &msiCtl);
return OK;
}
/*****************************************************************************
*
* vxbPciMSICtl - enable or disable the MSI and return the MSI address and data
*
* This routine enables or disables MSI, and stores the message address and
* data to dynaVec
*
* RETURNS: ERROR when MSI is unavailable, else OK
*
* ERRNO: N/A
*/
STATUS vxbPciMSICtl
(
VXB_DEVICE_ID pDev,
struct vxbIntDynaVecInfo * dynaVec,
BOOL enable
)
{
VXB_DEVICE_ID busCtrlID = vxbDevParent(pDev);
struct vxbPciDevice * pPciDevice = pDev->pBusSpecificDevInfo;
UINT32 tempa32 = 0;
UINT32 tempb32 = 0;
UINT16 temp16 = 0;
UINT16 msiCtl = 0;
UINT8 msiOffset = 0;
/* Capabilities List Implemented: Get first capability ID */
vxbPciConfigExtCapFind(busCtrlID, PCI_EXT_CAP_MSI,
pPciDevice->pciBus,
pPciDevice->pciDev,
pPciDevice->pciFunc,
&msiOffset);
if (msiOffset == 0)
return (ERROR);
/*
* This offset is for PCI_MSI_CTL which is 2 bytes in advance vs value
* expected by defs so subtract.
*/
msiOffset -= PCI_MSI_CTL;
vxbPciBusCfgRead (pDev, (UINT32)(msiOffset + PCI_MSI_CTL), 2, &msiCtl);
vxbPciBusCfgRead (pDev, (UINT32)(msiOffset + PCI_MSI_ADDR_LO), 4,
&tempa32);
if (msiCtl & PCI_MSI_CTL_64BIT)
{
vxbPciBusCfgRead (pDev, (UINT32)(msiOffset + PCI_MSI_ADDR_HI), 4,
&tempb32);
vxbPciBusCfgRead (pDev, (UINT32)(msiOffset + PCI_MSI_DATA_64), 2,
&temp16);
}
else
{
vxbPciBusCfgRead (pDev, (UINT32)(msiOffset + PCI_MSI_DATA_32), 2,
&temp16);
}
dynaVec->vecAddr = (UINT64)tempa32 | ((UINT64)tempb32 << 32);
dynaVec->vecVal = temp16;
msiCtl |= PCI_MSI_CTL_ENABLE;
if ( (msiCtl & PCI_MSI_CTL_MSG_MAX) == 0) /* only one vector */
{
if (enable == FALSE)
msiCtl &= ~PCI_MSI_CTL_ENABLE;
}
vxbPciBusCfgWrite (pDev, (UINT32)(msiOffset + PCI_MSI_CTL), 2, &msiCtl);
/* If the device does not support Per-vector masking, we are done! */
if (!(msiCtl & PCI_MSI_CTL_MASK))
{
return OK;
}
/* Handle per vector-masking */
if (msiCtl & PCI_MSI_CTL_64BIT)
{
vxbPciBusCfgRead (pDev, (UINT32)(msiOffset + PCI_MSI_MASK_64), 4,
&tempb32);
tempb32 &= ~(1 << dynaVec->index);
if (enable == FALSE)
tempb32 |= (1 << dynaVec->index);
vxbPciBusCfgWrite (pDev, (UINT32)(msiOffset + PCI_MSI_MASK_64), 4,
&tempb32);
}
else
{
vxbPciBusCfgRead (pDev, (UINT32)(msiOffset + PCI_MSI_MASK_32), 4,
&tempb32);
tempb32 &= ~(1 << dynaVec->index);
if (enable == FALSE)
tempb32 |= (1 << dynaVec->index);
vxbPciBusCfgWrite (pDev, (UINT32)(msiOffset + PCI_MSI_MASK_32), 4,
&tempb32);
}
return OK;
}
/*******************************************************************************
*
* vxbPciMSIEnable - enable a single MSI vector
*
* This routine enables a single MSI vector.
*
* RETURNS: ERROR when MSI/MSI-X is unavailable, else OK
*
* ERRNO: N/A
*/
STATUS vxbPciMSIEnable
(
VXB_DEVICE_ID pDev,
struct vxbIntDynaVecInfo * dynaVec
)
{
if (dynaVec->vecType == VXB_INT_PCI_MSIX)
return vxbPciMSIXCtl (pDev,dynaVec,TRUE);
else
return vxbPciMSICtl (pDev,dynaVec,TRUE);
}
/*******************************************************************************
*
* vxbPciMSIDisable - disable a single MSI vector
*
* This routine disables a single MSI vector.
*
* RETURNS: ERROR when MSI/MSI-X is unavailable, else OK
*
* ERRNO: N/A
*/
STATUS vxbPciMSIDisable
(
VXB_DEVICE_ID pDev,
struct vxbIntDynaVecInfo * dynaVec
)
{
if (dynaVec->vecType == VXB_INT_PCI_MSIX)
return vxbPciMSIXCtl (pDev,dynaVec,FALSE);
else
return vxbPciMSICtl (pDev,dynaVec, FALSE);
}
/*****************************************************************************
*
* vxbPciMSIGet - get the message address and data
*
* This routine get the message address and data to dynaVec
*
* RETURNS: OK or ERROR when PCI device doesn't support MSI
*
* ERRNO: N/A
*/
STATUS vxbPciMSIGet
(
VXB_DEVICE_ID pDev,
struct vxbIntDynaVecInfo * dynaVec
)
{
VXB_DEVICE_ID busCtrlID = vxbDevParent(pDev);
struct vxbPciDevice * pPciDevice = pDev->pBusSpecificDevInfo;
UINT32 tempa32;
UINT32 tempb32 = 0;
UINT16 temp16;
UINT8 msiOffset = 0, msiCtl;
/* Capabilities List Implemented: Get first capability ID */
vxbPciConfigExtCapFind(busCtrlID, PCI_EXT_CAP_MSI,
pPciDevice->pciBus,
pPciDevice->pciDev,
pPciDevice->pciFunc,
&msiOffset);
if (msiOffset == 0)
return (ERROR);
/*
* This offset is for PCI_MSI_CTL which is 2bytes in advance vs value
* expected by defs so subtract.
*/
msiOffset -= PCI_MSI_CTL;
vxbPciBusCfgRead (pDev, (UINT32)(msiOffset + PCI_MSI_CTL), 1, &msiCtl);
vxbPciBusCfgRead (pDev, (UINT32)(msiOffset + PCI_MSI_ADDR_LO), 4,
&tempa32);
if(msiCtl & PCI_MSI_CTL_64BIT)
{
vxbPciBusCfgRead (pDev, (UINT32)(msiOffset + PCI_MSI_ADDR_HI), 4,
&tempb32);
vxbPciBusCfgRead (pDev, (UINT32)(msiOffset + PCI_MSI_DATA_64), 2,
&temp16);
}
else
{
vxbPciBusCfgRead (pDev, (UINT32)(msiOffset + PCI_MSI_DATA_32), 2,
&temp16);
}
dynaVec->vecAddr = (UINT64)tempa32 | ((UINT64)tempb32 << 32);
dynaVec->vecVal = temp16;
PCILIB_DEBUG_MSG(20,"Got Here MSI Ctl dev 0x%x bus Ctrl 0x%x"
"vecAddr 0x%x Val 0x%x\n",
(_Vx_usr_arg_t)pDev,
(_Vx_usr_arg_t)busCtrlID,
(_Vx_usr_arg_t)dynaVec->vecAddr ,
(_Vx_usr_arg_t)dynaVec->vecVal,5,6);
return OK;
}
/*******************************************************************************
*
* vxbPciMSIXMultiProgram - programm multiple MSI-X vectors
*
* This routine programms multiple MSI-X vectors.
*
* RETURNS: ERROR when MSI-X is unavailable, else OK.
*
* ERRNO: N/A
*
* \NOMANUAL
*/
LOCAL STATUS vxbPciMSIXMultiProgram
(
VXB_DEVICE_ID pDev,
int numVectors,
struct vxbIntDynaVecInfo * pDynaVec
)
{
VXB_DEVICE_ID busCtrlID = vxbDevParent(pDev);
struct vxbPciDevice * pPciDevice = pDev->pBusSpecificDevInfo;
UINT8 msiOffset=0;
UINT16 msiCtl;
UINT32 tableSize;
UINT32 table, pba, tableOffset, *pTableEntry;
ULONG tableBase, pbaBase, temp;
int i;
void * msixBarHandle;
if (busCtrlID == NULL)
return ERROR;
vxbPciConfigExtCapFind(busCtrlID, PCI_EXT_CAP_MSIX,
pPciDevice->pciBus,
pPciDevice->pciDev,
pPciDevice->pciFunc,
&msiOffset);
if (msiOffset == 0)
return ERROR;
/*
* This offset is for MSIX_CTL which is 2 bytes, so must be adjusted
* to get the base offset
*/
msiOffset -=PCI_MSIX_CTL;
/*
* Software reads the Table Size field from the Message Control
* register to determine the MSI-X Table size. The field encodes
* the number of table entries as N-1, so software must add 1 to
* the content read from the field to calculate the number of table
* entries N. MSI-X supports a maximum table size of 2048 entries.
*/
vxbPciBusCfgRead (pDev, msiOffset + PCI_MSIX_CTL, 2, &msiCtl);
tableSize = (msiCtl & PCI_MSIX_CTL_TABLE_SIZE) + 1;
/*
* Software calculates the base address of the MSI-X Table by reading
* the 32-bit value from the Table Offset / BIR register, masking
* off the lower 3 BIR bits, and adding the remaining 8-byte aligned
* 32-bit offset to the address taken from the Base Address register
* indicated by the BIR.
*/
vxbPciBusCfgRead (pDev, msiOffset + PCI_MSIX_TABLE, 4, &table);
tableOffset = (table & PCI_MSIX_TABLE_OFFSET_MASK);
vxbPciBusCfgRead (pDev, msiOffset + PCI_MSIX_PBA, 4, &pba);
tableBase = (ULONG)pDev->pRegBase[(table & PCI_MSIX_TABLE_BIR_MASK)];
if (vxMemProbe(tableBase, VX_READ, 4, &temp) != OK)
{
vxbRegMap (pDev, table & PCI_MSIX_TABLE_BIR_MASK, &msixBarHandle);
tableBase = (ULONG)pDev->pRegBase[(table & PCI_MSIX_TABLE_BIR_MASK)];
}
pbaBase = (ULONG)pDev->pRegBase[pba & PCI_MSIX_PBA_BIR_MASK];
if (vxMemProbe(pbaBase, VX_READ, 4, &temp) != OK)
{
vxbRegMap (pDev, pba & PCI_MSIX_PBA_BIR_MASK, &msixBarHandle);
pbaBase = (ULONG)pDev->pRegBase[pba & PCI_MSIX_PBA_BIR_MASK];
}
tableBase = (ULONG)pDev->pRegBase[(table & PCI_MSIX_TABLE_BIR_MASK)];
PCILIB_DEBUG_MSG(20,
"bus %d dev %d func %d MSI-X @%p tableBase %p tableOffset %p\n",
pPciDevice->pciBus,
pPciDevice->pciDev,
pPciDevice->pciFunc,
msiOffset, tableBase, tableOffset);
msiCtl |= PCI_MSIX_CTL_FUNCTION_MASK | PCI_MSIX_CTL_ENABLE;
vxbPciBusCfgWrite (pDev, msiOffset + PCI_MSIX_CTL, 2, &msiCtl);
pTableEntry = (UINT32 *)(tableBase + tableOffset);
for (i = 0; i < numVectors; i++)
{
struct vxbIntDynaVecInfo *pCurDynaVec;
pCurDynaVec = &pDynaVec[i];
/* Message Vector Control */
pTableEntry[(pCurDynaVec->index << 2) + PCI_MSIX_MSG_CTRL] =
htole32(pTableEntry[(pCurDynaVec->index << 2) + PCI_MSIX_MSG_CTRL] |
PCI_MSIX_MSG_CTRL_MASK);
/* Message Lower Address */
pTableEntry[(pCurDynaVec->index << 2) + 0] = htole32(pCurDynaVec->vecAddr);
/* Message Higher Address */
pTableEntry[(pCurDynaVec->index << 2) + 1] = htole32(pCurDynaVec->vecAddr >> 32);
/* Message Data */
pTableEntry[(pCurDynaVec->index << 2) + 2] = htole32(pCurDynaVec->vecVal);
PCILIB_DEBUG_MSG(20,
"vxbPciMSIXMultiProgram(): "
"vecAddr 0x%016x val 0x%x for %s%d index %d Vector Control 0x%x\n",
pCurDynaVec->vecAddr,
pCurDynaVec->vecVal,
(int)pDev->pName,
pDev->unitNumber,
pCurDynaVec->index,
pTableEntry[(pCurDynaVec->index << 2) + 3]);
}
vxbPciIntrExclusiveEnable(pDev, VXB_INT_PCI_MSIX);
return OK;
}
/*******************************************************************************
*
* vxbPciMSIXMultiErase - erase the programmed MSI-X vectors
*
* This routine erases the multiple MSI-X vectors, that can be erased individually
* by the register for each MSI-X vector. This routine erases the data and address
* register specified by "pDynaVec".
*
* RETURNS: ERROR when MSI-X is unavailable, else OK.
*
* ERRNO: N/A
*
* \NOMANUAL
*/
LOCAL STATUS vxbPciMSIXMultiErase
(
VXB_DEVICE_ID pDev,
int numVectors,
struct vxbIntDynaVecInfo * pDynaVec
)
{
VXB_DEVICE_ID busCtrlID = vxbDevParent(pDev);
struct vxbPciDevice * pPciDevice = pDev->pBusSpecificDevInfo;
UINT8 msiOffset = 0;
UINT16 msiCtl;
UINT32 tableSize;
UINT32 table, tableOffset, *pTableEntry;
ULONG tableBase;
int i;
if (busCtrlID == NULL)
return ERROR;
vxbPciConfigExtCapFind(busCtrlID, PCI_EXT_CAP_MSIX,
pPciDevice->pciBus,
pPciDevice->pciDev,
pPciDevice->pciFunc,
&msiOffset);
if (msiOffset == 0)
return ERROR;
msiOffset -= PCI_MSIX_CTL;
/*
* Software reads the Table Size field from the Message Control
* register to determine the MSI-X Table size. The field encodes
* the number of table entries as N-1, so software must add 1 to
* the value read from the field to calculate the number of table
* entries N. MSI-X supports a maximum table size of 2048 entries.
*/
vxbPciBusCfgRead (pDev, msiOffset + PCI_MSIX_CTL, 2, &msiCtl);
tableSize = (msiCtl & PCI_MSIX_CTL_TABLE_SIZE) + 1;
/*
* Software calculates the base address of the MSI-X Table by reading
* the 32-bit value from the Table Offset / BIR register, masking
* off the lower 3 BIR bits, and adding the remaining 8-byte aligned
* 32-bit offset to the address taken from the Base Address register
* indicated by the BIR.
*/
vxbPciBusCfgRead (pDev, msiOffset + PCI_MSIX_TABLE, 4, &table);
tableOffset = (table & PCI_MSIX_TABLE_OFFSET_MASK);
tableBase = (ULONG)pDev->pRegBase[(table & PCI_MSIX_TABLE_BIR_MASK)];
PCILIB_DEBUG_MSG(10,
"vxbPciMSIXMultiErase():"
"bus %d dev %d func %d MSI-X @%p tableBase %p tableOffset %p\n",
pPciDevice->pciBus,
pPciDevice->pciDev,
pPciDevice->pciFunc,
msiOffset, tableBase, tableOffset);
msiCtl |= PCI_MSIX_CTL_FUNCTION_MASK;
msiCtl &= ~(PCI_MSIX_CTL_ENABLE);
vxbPciBusCfgWrite (pDev, msiOffset + PCI_MSIX_CTL, 2, &msiCtl);
pTableEntry = (UINT32 *)(tableBase + tableOffset);
for (i = 0; i < numVectors; i++)
{
struct vxbIntDynaVecInfo *pCurDynaVec;
pCurDynaVec = &pDynaVec[i];
/* Message Lower Address */
pTableEntry[(pCurDynaVec->index << 2) + 0] = 0;
/* Message Higher Address */
pTableEntry[(pCurDynaVec->index << 2) + 1] = 0;
/* Message Data */
pTableEntry[(pCurDynaVec->index << 2) + 2] = 0;
/* Message Vector Control */
pTableEntry[(pCurDynaVec->index << 2) + 3] = 1;
}
return OK;
}
/*******************************************************************************
*
* vxbPciMSIXCtl - enable or disable a single MSI-X vector
*
* This routine enables or disables a single MSI-X vector.
*
* RETURNS: ERROR when MSI-X is unavailable, else OK.
*
* ERRNO: N/A
*
* \NOMANUAL
*/
LOCAL STATUS vxbPciMSIXCtl
(
VXB_DEVICE_ID pDev,
struct vxbIntDynaVecInfo * dynaVec,
BOOL enable
)
{
VXB_DEVICE_ID busCtrlID = vxbDevParent(pDev);
struct vxbPciDevice * pPciDevice = pDev->pBusSpecificDevInfo;
UINT8 msixOffset = 0;
UINT16 msixCtl;
UINT32 tableSize;
UINT32 table, tableOffset, * tableEntry;
UINT32 tempa32 = 0;
UINT32 tempb32 = 0;
ULONG tableAddr;
/* Capabilities List Implemented: Get first capability ID */
vxbPciConfigExtCapFind(busCtrlID, PCI_EXT_CAP_MSIX,
pPciDevice->pciBus,
pPciDevice->pciDev,
pPciDevice->pciFunc,
&msixOffset);
if (msixOffset == 0)
return (ERROR);
msixOffset -= PCI_MSIX_CTL;
vxbPciBusCfgRead (pDev, msixOffset + PCI_MSIX_CTL, 2, &msixCtl);
/*
* Make sure the function mask is cleared (so we are on a per vector
* control).
*/
msixCtl &= ~PCI_MSIX_CTL_FUNCTION_MASK;
/*
* Make sure the MSI-X is enabled!
*
* Even if we are disabling a MSI-X vector, we do not disable
* the MSI-X itself, which should be the job of disconnecting
* the dynamical vectors (as a vxbIntDynaDisconnect() which
* we do not currently have).
*/
msixCtl |= PCI_MSIX_CTL_ENABLE;
vxbPciBusCfgWrite (pDev, msixOffset + PCI_MSIX_CTL, 2, &msixCtl);
tableSize = (msixCtl & PCI_MSIX_CTL_TABLE_SIZE) + 1;
vxbPciBusCfgRead (pDev, msixOffset + PCI_MSIX_TABLE, 4, &table);
tableOffset = (table & PCI_MSIX_TABLE_OFFSET_MASK);
tableAddr = (ULONG)pDev->pRegBase[(table & PCI_MSIX_TABLE_BIR_MASK)];
tableEntry = (UINT32 *)((~PCI_BAR_ALL_MASK & tableAddr) + tableOffset);
tableEntry[(dynaVec->index << 2) + PCI_MSIX_MSG_CTRL] =
htole32 (tableEntry[(dynaVec->index << 2) + PCI_MSIX_MSG_CTRL] &
~(PCI_MSIX_MSG_CTRL_MASK));
if (enable == FALSE)
tableEntry[(dynaVec->index << 2) + PCI_MSIX_MSG_CTRL] =
htole32 (tableEntry[(dynaVec->index << 2) + PCI_MSIX_MSG_CTRL] |
(PCI_MSIX_MSG_CTRL_MASK));
tempa32= le32toh(tableEntry[(dynaVec->index << 2)]);
tempb32= le32toh(tableEntry[(dynaVec->index << 2) + 1]);
dynaVec->vecAddr = (UINT64)tempa32 | ((UINT64)tempb32 << 32);
dynaVec->vecVal = le32toh(tableEntry[(dynaVec->index << 2) + 2]);
return OK;
}
/*******************************************************************************
*
* vxbPciMSIXDisable - disable a single MSI-X vector
*
* This routine disables a single MSI-X vector.
*
* RETURNS: ERROR when MSI-X is unavailable, else OK.
*
* ERRNO: N/A
*
* \NOMANUAL
*/
LOCAL STATUS vxbPciMSIXDisable
(
VXB_DEVICE_ID pDev,
struct vxbIntDynaVecInfo *dynaVec
)
{
return vxbPciMSIXCtl (pDev,dynaVec,FALSE);
}
/*******************************************************************************
*
* vxbPciMSIErase - erase the programmed MSI-X/MSI vectors
*
* This routine erases the programmed MSI-X/MSI vectors.
*
* RETURNS: OK when desired operation successfully done, ERROR when fail
*
* ERRNO: N/A
*
* \NOMANUAL
*/
STATUS vxbPciMSIErase
(
VXB_DEVICE_ID pDev,
struct vxbIntDynaVecInfo * dynaVec
)
{
UINT32 msiCount;
UINT32 vecType;
if (dynaVec == NULL)
return ERROR;
vecType = dynaVec->vecType;
if ((dynaVec->count & VXB_MULTIPLE_MSI_CNT_FLAG)
== VXB_MULTIPLE_MSI_CNT_FLAG)
msiCount = dynaVec->count & VXB_MULTIPLE_MSI_CNT_MASK;
else
msiCount = 1;
if (dynaVec->vecType == VXB_INT_PCI_MSIX)
return vxbPciMSIXMultiErase (pDev, msiCount, dynaVec);
else
return vxbPciMSIMultiErase (pDev, msiCount, dynaVec);
}
/*******************************************************************************
*
* vxbPciIntLibInit - initialize the vxbPciIntLib module
*
* This routine initializes the linked lists used to chain together the PCI
* interrupt service routines.
*
* RETURNS: OK, or ERROR upon linked list failures.
*
* ERRNO
*
*/
STATUS vxbPciIntLibInit
(
struct vxbPciInt * pIntInfo
)
{
if (pIntInfo == NULL)
return(ERROR);
pIntInfo->pciIntLibInitStatus = ERROR;
pIntInfo->pciIntList = (DL_LIST *) hwMemAlloc(sizeof(DL_LIST));
if (pIntInfo->pciIntList == NULL)
{
PCILIB_ERR("vxbPciIntLibInit Failed: hwMemAlloc pciIntList failed!\n");
return ERROR;
}
/* Initialize shared interrupt handler chains */
dllInit (pIntInfo->pciIntList);
SPIN_LOCK_ISR_INIT (&pIntInfo->pciIntLibSpinlockIsr, 0);
return (pIntInfo->pciIntLibInitStatus = OK);
}
/*******************************************************************************
*
* vxbPciInt - interrupt handler for shared PCI interrupt.
*
* This routine executes multiple interrupt handlers for a PCI interrupt.
* Each interrupt handler must check the device dependent interrupt status bit
* to determine the source of the interrupt, since it simply execute all
* interrupt handlers in the linked list.
*
* This is not a user callable routine
*
* RETURNS: N/A
*
* ERRNO
*
*/
void vxbPciInt
(
struct vxbPciIntHandlerInfo *pIntHandlerInfo
)
{
PCI_INT_RTN *pRtn;
struct vxbPciInt *pIntInfo;
PCI_INT_RTN *dllHandlerNext,*dllHandlerFirst;
pIntInfo = pIntHandlerInfo->pIntInfo;
SPIN_LOCK_ISR_TAKE (&pIntHandlerInfo->pciIntHandlerSpinlockIsr);
dllHandlerFirst = (PCI_INT_RTN *)DLL_FIRST (&pIntHandlerInfo->pciIntHandlerList);
for (pRtn = dllHandlerFirst; pRtn != NULL; pRtn = dllHandlerNext)
{
dllHandlerNext = (PCI_INT_RTN *)DLL_NEXT (&pRtn->node);
/*
* If this handler is disabled, just go to the next now.
* Previously we would drop the spinlock, then test the
* enable flag, then re-acquire the spinlock after. If we
* check here instead, we can avoid the spinlock manipulation
* since we know we're not going to call the ISR anyway.
*/
if (pRtn->enable == FALSE)
continue;
pRtn->refCnt++;
SPIN_LOCK_ISR_GIVE (&pIntHandlerInfo->pciIntHandlerSpinlockIsr);
(* pRtn->routine) (pRtn->parameter);
SPIN_LOCK_ISR_TAKE (&pIntHandlerInfo->pciIntHandlerSpinlockIsr);
pRtn->refCnt--;
}
SPIN_LOCK_ISR_GIVE (&pIntHandlerInfo->pciIntHandlerSpinlockIsr);
}
/*******************************************************************************
*
* vxbPciIntConnect - connect the interrupt handler to the PCI interrupt.
*
* This routine connects an interrupt handler to a shared PCI interrupt vector.
* A linked list is created for each shared interrupt used in the system. A call
* to vxbPciIntConnect will add its routine to the linked list for that vector.
*
* Note: The only place where vxbPciIntConnect() is called is from PCI bus controller
* drivers, it's not okay to use them in PCI device drivers. For a PCI device
* driver, user should only use vxbIntConnect()/vxbIntEnable() to enable interrupt
* and bind ISR.
*
* RETURNS:
* OK, or ERROR if the interrupt handler cannot be built.
*
* ERRNO
*
*/
STATUS vxbPciIntConnect
(
VXB_DEVICE_ID pDev,
VOIDFUNCPTR *vector, /* interrupt vector to attach to */
VOIDFUNCPTR routine, /* routine to be called */
_Vx_usr_arg_t parameter /* parameter to be passed to routine */
)
{
int irq = pciIntVectorToIRQ (vector);
PCI_INT_RTN *pRtn, *pLast;
STATUS retStatus;
struct vxbPciIntHandlerInfo * pPciIntHandlerInfo;
struct vxbPciInt * pIntInfo;
FUNCPTR method;
VXB_DEVICE_ID busCtrlID;
DL_NODE * pNode;
BOOL isEmpty = FALSE;
if(pDev == NULL)
return(ERROR);
if (irq < 0 || irq >= PCI_INT_LINES)
return ERROR;
if(pDev->busID == VXB_BUSID_PCI)
busCtrlID = vxbDevParent(pDev);
else
busCtrlID = pDev;
if(busCtrlID == NULL)
return(ERROR);
method = vxbDevMethodGet(busCtrlID ,PCI_CONTROLLER_METHOD_INTERRUPT_INFO);
if(method == NULL)
{
PCILIB_ERR("vxbPciIntConnect Failed: No PCI method for IntInfo\n");
return(ERROR);
}
retStatus = (*method)(busCtrlID,&pIntInfo);
if(retStatus == ERROR)
{
PCILIB_ERR("vxbPciIntConnect Failed: Method for IntInfo doesn't work\n");
return(ERROR);
}
if(pIntInfo == NULL)
{
PCILIB_ERR("vxbPciIntConnect Failed: pIntInfo is NULL\n");
return(ERROR);
}
if (pIntInfo->pciIntLibInitStatus != OK)
{
PCILIB_DEBUG_MSG(1, "pciIntLibInitStatus\n",1,2,3,4,5,6);
return ERROR;
}
vxbLockTake (&pciIntListLock, VXB_LOCK_WRITER);
/* Find the vector list entry for this irq. */
pNode = DLL_FIRST(pIntInfo->pciIntList);
while (pNode != NULL)
{
pPciIntHandlerInfo = (struct vxbPciIntHandlerInfo *)pNode;
if (pPciIntHandlerInfo->irq == irq)
break;
pNode = DLL_NEXT(pNode);
}
/* If it doesn't exist yet, create it. */
if (pNode == NULL)
{
pPciIntHandlerInfo = (struct vxbPciIntHandlerInfo *)
hwMemAlloc(sizeof(struct vxbPciIntHandlerInfo));
if (pPciIntHandlerInfo == NULL)
{
PCILIB_ERR("vxbPciIntConnect Failed: pPciIntHandlerInfo is NULL\n");
vxbLockGive (&pciIntListLock, VXB_LOCK_WRITER);
return (ERROR);
}
/* Initialize node. */
pPciIntHandlerInfo->pIntInfo= pIntInfo;
pPciIntHandlerInfo->irq = irq;
SPIN_LOCK_ISR_INIT (&pPciIntHandlerInfo->pciIntHandlerSpinlockIsr, 0);
DLL_INIT(&pPciIntHandlerInfo->pciIntHandlerList);
/* Add to vector list. */
DLL_ADD(pIntInfo->pciIntList, &pPciIntHandlerInfo->node);
}
/*
* Allocate a new handler chain node and add it to the list.
*/
if ((pRtn = (PCI_INT_RTN *)hwMemAlloc (sizeof (PCI_INT_RTN))) == NULL)
{
/* Once the pPciIntHandlerInfo is alloced, it won't be freeed */
PCILIB_LOG("hwMemAlloc PCI_INT_RTN failed\n",1,2,3,4,5,6);
vxbLockGive (&pciIntListLock, VXB_LOCK_WRITER);
return (ERROR);
}
/* Initialize the node. */
pRtn->routine = routine;
pRtn->parameter = parameter;
pRtn->refCnt = 0;
/*
* If vxbPciIntEnale is used, should be FALSE;
* Otherwise, should be TRUE.
*/
if (pIntInfo->pciIntEnableUsed == TRUE)
pRtn->enable = FALSE;
else
pRtn->enable = TRUE;
/* Now add the node to the handler chain. */
retry:
SPIN_LOCK_ISR_TAKE (&pPciIntHandlerInfo->pciIntHandlerSpinlockIsr);
/*
* If the list is empty, there's nothing to check yet.
*/
if (DLL_EMPTY(&pPciIntHandlerInfo->pciIntHandlerList) == TRUE)
{
isEmpty = TRUE;
goto insert;
}
/*
* DLL_ADD() adds to the end of the list, so we should look
* at the last node if it is busy.
*/
pLast = (PCI_INT_RTN *)DLL_LAST(&pPciIntHandlerInfo->pciIntHandlerList);
/*
* If the last nodes where we're inserting the
* entry is busy, we have to bail.
*/
if (pLast != NULL && pLast->refCnt != 0)
{
SPIN_LOCK_ISR_GIVE (&pPciIntHandlerInfo->pciIntHandlerSpinlockIsr);
goto retry;
}
insert:
DLL_ADD (&pPciIntHandlerInfo->pciIntHandlerList, &pRtn->node);
SPIN_LOCK_ISR_GIVE (&pPciIntHandlerInfo->pciIntHandlerSpinlockIsr);
if (isEmpty == TRUE)
{
PCI_INT_HANDLER_BIND(vector, vxbPciInt,
(_Vx_usr_arg_t)pPciIntHandlerInfo, &retStatus);
if (retStatus == ERROR)
{
#ifndef _VXBUS_BASIC_HWMEMLIB
hwMemFree((char*)pPciIntHandlerInfo);
#endif /* !_VXBUS_BASIC_HWMEMLIB */
vxbLockGive (&pciIntListLock, VXB_LOCK_WRITER);
return (ERROR);
}
}
vxbLockGive (&pciIntListLock, VXB_LOCK_WRITER);
return (OK);
}
/*******************************************************************************
*
* vxbPciIntDisconnectHelper - help to disconnect an PCI interrupt handler
*
* This routine help to disconnect a single instance of an interrupt handler
* from the PCI interrupt line.
*
* RETURNS:
* OK, or ERROR if the interrupt handler cannot be removed.
*
* ERRNO
*
*/
LOCAL STATUS vxbPciIntDisconnectHelper
(
VXB_DEVICE_ID pDev,
VOIDFUNCPTR *vector, /* interrupt vector to attach to */
VOIDFUNCPTR routine, /* routine to be called */
_Vx_usr_arg_t parameter, /* routine parameter */
BOOL withParam /* parameter available */
)
{
int irq = pciIntVectorToIRQ(vector);
PCI_INT_RTN *pRtn;
PCI_INT_RTN *pNext, *pPrev, *pFirst;
BOOL dllEmpty;
BOOL foundRtn = FALSE;
STATUS retStatus = ERROR;
struct vxbPciIntHandlerInfo * pPciIntHandlerInfo;
struct vxbPciInt * pIntInfo;
FUNCPTR method;
VXB_DEVICE_ID busCtrlID;
DL_NODE * pNode;
if (pDev == NULL)
return(ERROR);
if (pDev->busID == VXB_BUSID_PCI)
busCtrlID = vxbDevParent(pDev);
else
busCtrlID = pDev;
if (busCtrlID == NULL)
return(ERROR);
method = vxbDevMethodGet(busCtrlID,PCI_CONTROLLER_METHOD_INTERRUPT_INFO);
if (method == NULL)
{
PCILIB_ERR("vxbPciIntDisconnect2 Failed: No PCI method for IntInfo\n");
return(ERROR);
}
retStatus = (*method)(busCtrlID,&pIntInfo);
if (retStatus == ERROR)
{
PCILIB_ERR("vxbPciIntDisconnect2 Failed: Method for IntInfo doesn't work\n");
return(ERROR);
}
if (pIntInfo==NULL)
{
PCILIB_ERR("vxbPciIntDisconnect2 Failed: pIntInfo is NULL\n");
return(ERROR);
}
if (pIntInfo->pciIntLibInitStatus != OK)
return(ERROR);
if (irq < 0 || irq >= PCI_INT_LINES)
return ERROR;
vxbLockTake (&pciIntListLock, VXB_LOCK_WRITER);
pNode = DLL_FIRST(pIntInfo->pciIntList);
while (pNode != NULL)
{
pPciIntHandlerInfo = (struct vxbPciIntHandlerInfo *)pNode;
if (pPciIntHandlerInfo->irq == irq)
break;
pNode = DLL_NEXT(pNode);
}
if (pNode == NULL)
{
PCILIB_ERR("vxbPciIntDisconnect Failed: No irq line found\n");
vxbLockGive (&pciIntListLock, VXB_LOCK_WRITER);
return ERROR;
}
retry:
SPIN_LOCK_ISR_TAKE (&pPciIntHandlerInfo->pciIntHandlerSpinlockIsr);
pFirst = (PCI_INT_RTN *)DLL_FIRST (&pPciIntHandlerInfo->pciIntHandlerList);
for (pRtn = pFirst; pRtn != NULL; pRtn = pNext)
{
pNext = (PCI_INT_RTN *)DLL_NEXT (&pRtn->node);
if (pRtn->routine == routine &&
((withParam == TRUE && pRtn->parameter == parameter) ||
withParam == FALSE))
{
pPrev = (PCI_INT_RTN *)DLL_PREVIOUS (&pRtn->node);
if (pRtn->refCnt != 0 ||
(pNext != NULL && pNext->refCnt !=0) ||
(pPrev != NULL && pPrev->refCnt !=0))
{
SPIN_LOCK_ISR_GIVE (&pPciIntHandlerInfo->pciIntHandlerSpinlockIsr);
goto retry;
}
foundRtn = TRUE;
DLL_REMOVE (&pPciIntHandlerInfo->pciIntHandlerList, &pRtn->node);
break;
}
}
SPIN_LOCK_ISR_GIVE (&pPciIntHandlerInfo->pciIntHandlerSpinlockIsr);
#ifndef _VXBUS_BASIC_HWMEMLIB
if (foundRtn)
hwMemFree ((char *)pRtn);
#endif /* !_VXBUS_BASIC_HWMEMLIB */
/* If the last ISR was just removed, then do intDisconnect */
dllEmpty = DLL_EMPTY(&pPciIntHandlerInfo->pciIntHandlerList);
if (dllEmpty == TRUE)
{
PCI_INT_HANDLER_UNBIND (vector, vxbPciInt, pPciIntHandlerInfo,
&retStatus);
if (retStatus == ERROR)
{
vxbLockGive (&pciIntListLock, VXB_LOCK_WRITER);
return ERROR;
}
}
vxbLockGive (&pciIntListLock, VXB_LOCK_WRITER);
return OK;
}
/*******************************************************************************
*
* vxbPciIntDisconnect - disconnect the interrupt handler (OBSOLETE)
*
* This routine disconnects the interrupt handler from the PCI interrupt line.
*
* In a system where one driver and one ISR services multiple devices, this
* routine removes all instances of the ISR because it completely ignores the
* parameter argument used to install the handler.
*
* NOTE: Use of this routine is discouraged and will be obsoleted in the future.
* New code should use the vxbPciIntDisconnect2() routine instead.
*
* The only place where vxbPciIntDisconnect() is called is from PCI bus controller
* drivers, it's not okay to use them in PCI device drivers. For a PCI
* device driver, user should only use vxIntDisconnect to disconnect ISR.
*
* RETURNS:
* OK, or ERROR if the interrupt handler cannot be removed.
*
* ERRNO
*
*/
STATUS vxbPciIntDisconnect
(
VXB_DEVICE_ID pDev,
VOIDFUNCPTR *vector, /* interrupt vector to attach to */
VOIDFUNCPTR routine /* routine to be called */
)
{
return vxbPciIntDisconnectHelper(pDev, vector, routine, 0, FALSE);
}
/*******************************************************************************
*
* vxbPciIntDisconnect2 - disconnect an interrupt handler from the PCI interrupt.
*
* This routine disconnects a single instance of an interrupt handler from the
* PCI interrupt line.
*
* NOTE: This routine should be used in preference to the original
* vxbPciIntDisconnect() routine. This routine is compatible with drivers that
* are managing multiple device instances, using the same basic ISR, but with
* different parameters.
*
* The only place where vxbPciIntDisconnect2() is called is from PCI bus controller
* drivers, it's not okay to use them in PCI device drivers. For a PCI
* device driver, user should only use vxIntDisconnect to disconnect ISR.
*
* RETURNS:
* OK, or ERROR if the interrupt handler cannot be removed.
*
* ERRNO
*
*/
STATUS vxbPciIntDisconnect2
(
VXB_DEVICE_ID pDev,
VOIDFUNCPTR *vector, /* interrupt vector to attach to */
VOIDFUNCPTR routine, /* routine to be called */
_Vx_usr_arg_t parameter /* routine parameter */
)
{
return vxbPciIntDisconnectHelper(pDev, vector, routine, parameter, TRUE);
}
/*******************************************************************************
*
* vxbPciIntCtl - enable or diable an interrupt handler from the PCI interrupt.
*
* This routine enables or diables a single instance of an interrupt handler
* from the PCI interrupt line.
*
* NOTE: This routine should be used in preference to the original
* pciIntConnect() routine. This routine is compatible with drivers that
* are managing multiple device instances, using the same basic ISR, but with
* different parameters.
*
* RETURNS:
* OK, or ERROR if the interrupt handler cannot be enabled or disabled.
*
* ERRNO
*
*/
LOCAL STATUS vxbPciIntCtl
(
VXB_DEVICE_ID pDev,
VOIDFUNCPTR *vector, /* interrupt vector to attach to */
VOIDFUNCPTR routine, /* routine to be called */
_Vx_usr_arg_t parameter, /* routine parameter */
int level, /* interrupt leve */
BOOL enable /* enable or diable */
)
{
int irq = pciIntVectorToIRQ(vector);
PCI_INT_RTN *pRtn, *pFound = NULL;
PCI_INT_RTN *pNext,*pFirst;
BOOL dllEmpty;
STATUS retStatus = ERROR;
struct vxbPciIntHandlerInfo * pPciIntHandlerInfo;
struct vxbPciInt * pIntInfo;
FUNCPTR method;
VXB_DEVICE_ID busCtrlID;
BOOL curState;
BOOL atLeastOneOtherEnabled;
DL_NODE * pNode;
if (pDev == NULL)
return(ERROR);
if (pDev->busID == VXB_BUSID_PCI)
busCtrlID = vxbDevParent(pDev);
else
busCtrlID = pDev;
if (busCtrlID == NULL)
return(ERROR);
method = vxbDevMethodGet(busCtrlID,PCI_CONTROLLER_METHOD_INTERRUPT_INFO);
if (method == NULL)
{
PCILIB_ERR("pciIntConnect Failed: No PCI method for IntInfo\n");
return(ERROR);
}
retStatus = (*method)(busCtrlID,&pIntInfo);
if (retStatus == ERROR)
{
PCILIB_ERR("pciIntConnect Failed: Method for IntInfo doesn't work\n");
return(ERROR);
}
if (pIntInfo == NULL)
{
PCILIB_ERR("pciIntConnect Failed: pIntInfo is NULL\n");
return(ERROR);
}
if (pIntInfo->pciIntLibInitStatus != OK)
return(ERROR);
if (irq < 0 || irq >= PCI_INT_LINES)
return ERROR;
vxbLockTake (&pciIntListLock, VXB_LOCK_WRITER);
pNode = DLL_FIRST(pIntInfo->pciIntList);
while (pNode != NULL)
{
pPciIntHandlerInfo = (struct vxbPciIntHandlerInfo *)pNode;
if (pPciIntHandlerInfo->irq == irq)
break;
pNode = DLL_NEXT(pNode);
}
if (pNode == NULL)
{
PCILIB_ERR("vxbPciIntCtl Failed: No irq line found\n");
vxbLockGive (&pciIntListLock, VXB_LOCK_WRITER);
return ERROR;
}
/*
* The logic here is a little tricky to see:
*
* Disabling interrupt deliver:
* - Can only be done in specific circumstances:
* o If there's no handlers attached (can't happen; we would
* never be called)
* o If there's just one chained interrupt bound to this vector.
* o If there are many chained handlers on this vector, but they
* are all turned off.
*
* Enabling interrupt delivery:
* - Should only be done in specific circumstances:
* o If there's one or more handlers bound to the vector,
* and delivery is currently off.
* o Doesn't need to be done again if already turned on once
* before.
*/
atLeastOneOtherEnabled = FALSE;
curState = -1;
SPIN_LOCK_ISR_TAKE (&pPciIntHandlerInfo->pciIntHandlerSpinlockIsr);
pNode = DLL_FIRST(&pPciIntHandlerInfo->pciIntHandlerList);
while (pNode != NULL)
{
pRtn = (PCI_INT_RTN *)pNode;
/*
* If this is the node we want, update its state and save
* its previous state.
*/
if (pRtn->routine == routine && pRtn->parameter == parameter)
{
curState = pRtn->enable;
pRtn->enable = enable;
}
else
{
/* Record whether or not any other nodes are still enabled. */
if (pRtn->enable == TRUE)
atLeastOneOtherEnabled = TRUE;
}
pNode = DLL_NEXT(pNode);
}
SPIN_LOCK_ISR_GIVE (&pPciIntHandlerInfo->pciIntHandlerSpinlockIsr);
/* Couldn't find the desired node -- abort. */
if (curState == -1)
{
vxbLockGive (&pciIntListLock, VXB_LOCK_WRITER);
return (ERROR);
}
retStatus = OK;
/*
* If we're being asked to enable interrupts,
* o AND the current handler was disabled.
* o AND no other handler is enabled either
* THEN we can enable delivery.
* (If atLeastOneOtherEnabled is TRUE, then
* delivery is already on, and enabling it again
* is redundant.)
*/
if (enable == TRUE && curState == FALSE &&
atLeastOneOtherEnabled == FALSE)
PCI_INT_HANDLER_ENABLE (level, &retStatus);
/*
* If we're being asked to disable interrupts,
* o AND the current handler was enabled
* o AND no other handler is enabled either
* THEN we can disable delivery.
* (If atLeastOneOtherEnabled is TRUE, then
* there's still at least one other handler
* that wants interrupts delivered, so we can't
* turn them off yet.)
*/
if (enable == FALSE && curState == TRUE &&
atLeastOneOtherEnabled == FALSE)
PCI_INT_HANDLER_DISABLE (level, &retStatus);
if (retStatus == ERROR)
{
vxbLockGive (&pciIntListLock, VXB_LOCK_WRITER);
return (ERROR);
}
vxbLockGive (&pciIntListLock, VXB_LOCK_WRITER);
return (OK);
}
/*******************************************************************************
*
* vxbPciIntEnable - enable an interrupt handler from the PCI interrupt.
*
* This routine enables a single instance of an interrupt handler
* from the PCI interrupt line.
*
* NOTE: This routine should be used in preference to the original
* pciIntConnect() routine. This routine is compatible with drivers that
* are managing multiple device instances, using the same basic ISR, but with
* different parameters.
*
* The only place where vxbPciIntEnable() is called is from PCI bus controller
* drivers, it's not okay to use them in PCI device drivers. When vxbPciIntEnable()
* is called in PCI bus controller, flag pciIntEnableUsed should be set to TRUE,
* but which could cause intEnable() doesn't work in PCI device driver. For a PCI
* device driver, user should only use vxbIntEnable() to enable interrupt.
*
* RETURNS:
* OK, or ERROR if the interrupt handler cannot be enabled.
*
* ERRNO
*
*/
STATUS vxbPciIntEnable
(
VXB_DEVICE_ID pDev,
VOIDFUNCPTR *vector, /* interrupt vector to attach to */
VOIDFUNCPTR routine, /* routine to be called */
_Vx_usr_arg_t parameter, /* routine parameter */
int level /* interrupt level */
)
{
return vxbPciIntCtl (pDev, vector, routine, parameter, level, TRUE);
}
/*******************************************************************************
*
* vxbPciIntDisable - diable an interrupt handler from the PCI interrupt.
*
* This routine diables a single instance of an interrupt handler
* from the PCI interrupt line.
*
* NOTE: This routine should be used in preference to the original
* pciIntConnect() routine. This routine is compatible with drivers that
* are managing multiple device instances, using the same basic ISR, but with
* different parameters.
*
* The only place where vxbPciIntDisable() is called is from PCI bus
* controller drivers, it's not okay to use them in PCI device drivers.
* For a PCI device driver, user should only use vxbIntDisable()
* to disable interrupt.
*
* RETURNS:
* OK, or ERROR if the interrupt handler cannot be diabled.
*
* ERRNO
*
*/
STATUS vxbPciIntDisable
(
VXB_DEVICE_ID pDev,
VOIDFUNCPTR *vector, /* interrupt vector to attach to */
VOIDFUNCPTR routine, /* routine to be called */
_Vx_usr_arg_t parameter, /* routine parameter */
int level /* interrupt level */
)
{
return vxbPciIntCtl (pDev, vector, routine, parameter, level, FALSE);
}
/*********************************************************************
*
* pciConfigEnable - Set the globalBusCtrlID for the other LEGACY pci
* routines.
*
* RETURNS: N/A
*
* ERRNO
*/
void pciConfigEnable
(
VXB_DEVICE_ID busCtrlID
)
{
globalBusCtrlID = busCtrlID;
}
/*****************************************************************************
*
* vxbPciMSIIsCap - Check MSI Capability
*
* It checks if the PCIxx is capable of MSI. Device can be a plain PCI, PCI-x,
* PCI-e and/or PCI-e with MSI-x feature option. A PCIx is capable of MSI
* scheme only if the MSI Capability is set or it is PCI-Express Capable.
*
* RETURNS: It returns TRUE, if it is MSI capable. Otherwise, FALSE.
*
* Note:
* There is no checking for MSI-X, because that category is a subclass
* of PCI-e devices.
*
* \NOMANUAL
*/
BOOL vxbPciMSIIsCap
(
VXB_DEVICE_ID pDev
)
{
VXB_DEVICE_ID busCtrlID = vxbDevParent(pDev);
struct vxbPciDevice *pPciDevice = pDev->pBusSpecificDevInfo;
UINT8 msiOffset = 0;
UINT8 expOffset = 0;
PCILIB_DEBUG_MSG(20,"Capability for Device 0x%x bus Ctrl 0x%x\n",
(int)pDev,
(int)busCtrlID,
3,4,5,6);
/* Assume it doesn't support MSI */
if (busCtrlID == NULL)
{
PCILIB_ERR("Cannot know the MSI Capability "
"because busCtrlID is NULL\n");
return (FALSE);
}
/* Check if MSI is supported for PCI device */
vxbPciConfigExtCapFind (busCtrlID, PCI_EXT_CAP_MSI,
pPciDevice->pciBus,
pPciDevice->pciDev,
pPciDevice->pciFunc,
&msiOffset);
/* If MSI not supported, Check if the device is capable of PCI-e */
if (msiOffset==0)
{
vxbPciConfigExtCapFind (busCtrlID, PCI_EXT_CAP_EXP,
pPciDevice->pciBus,
pPciDevice->pciDev,
pPciDevice->pciFunc,
&expOffset);
if (expOffset == 0)
{
PCILIB_DEBUG_MSG(20, "The device 0x%x is not MSI capaple\n",
(int)pDev,1,2,3,4,5);
return (FALSE);
}
PCILIB_DEBUG_MSG(20, "Device %p is PCI-e capable\n",
(int)pDev,1,2,3,4,5);
}
PCILIB_DEBUG_MSG(20, "The Device %p is MSI capable\n",
(int)pDev,1,2,3,4,5);
return (TRUE);
}
/*****************************************************************************
*
* vxbPciDevIntCapabCheck - Check MSI, MSI-X Capability
*
* It check if the device has specified number of MSI or MSI-X interrupts.
*
* RETURNS: TRUE, if it has specified MSI, MSI-X interrupt. Otherwise, FALSE.
*
* \NOMANUAL
*/
STATUS vxbPciDevIntCapabCheck
(
VXB_DEVICE_ID pDev,
UINT32 type,
UINT32 count
)
{
VXB_DEVICE_ID busCtrlID = vxbDevParent(pDev);
struct vxbPciDevice * pPciDevice = pDev->pBusSpecificDevInfo;
UINT8 msiOffset = 0;
UINT16 msiCtl = 0;
if (busCtrlID == NULL)
return ERROR;
if (type == VXB_INT_PCI_MSIX)
{
/* Check MSI-X */
vxbPciConfigExtCapFind (busCtrlID, PCI_EXT_CAP_MSIX,
pPciDevice->pciBus,
pPciDevice->pciDev,
pPciDevice->pciFunc,
&msiOffset);
if (msiOffset == 0)
return ERROR;
vxbPciBusCfgRead (pDev, (UINT32)(msiOffset), 2, &msiCtl);
PCILIB_DEBUG_MSG(20, "MSIX %d count %d \n",
((msiCtl & PCI_MSIX_CTL_TABLE_SIZE) + 1), count,3,4,5,6);
if (count > ((msiCtl & PCI_MSIX_CTL_TABLE_SIZE) + 1))
return ERROR;
return OK;
}
if ((type == VXB_INT_PCI_MSI) || (type == VXB_INT_PCI_LEGACY_MSI))
{
/* Check MSI */
vxbPciConfigExtCapFind (busCtrlID, PCI_EXT_CAP_MSI,
pPciDevice->pciBus,
pPciDevice->pciDev,
pPciDevice->pciFunc,
&msiOffset);
if (msiOffset == 0)
return ERROR;
vxbPciBusCfgRead (pDev, (UINT32)(msiOffset), 2, &msiCtl);
PCILIB_DEBUG_MSG(20, "MSI %d count %d \n",
(1 << ((msiCtl & PCI_MSI_CTL_MSG_MAX) >> 1)), count,3,4,5,6);
if (count > (1 << ((msiCtl & PCI_MSI_CTL_MSG_MAX) >> 1)))
return ERROR;
return OK;
}
return ERROR;
}
/*******************************************************************************
*
* vxbPciGetDevInstActFunc - check if device on bus is the specified device
*
* This routine check if the device on bus is the specified device, if match, save
* its instance id into pCookie;
*
* RETURNS: OK, or ERROR if the bus/dev/func didn't match.
*
*/
LOCAL STATUS vxbPciGetDevInstActFunc
(
struct vxbDev * pDevOnBus,
struct vxbDev * pBusCtlr,
char * pArg
)
{
struct vxbPciDevice * pPciArg = (struct vxbPciDevice *)pArg;
struct vxbPciDevice * pPciDev = pDevOnBus->pBusSpecificDevInfo;
if (pPciArg->pCookie != NULL)
return OK;
if ((pPciDev->pciBus == pPciArg->pciBus) &&
(pPciDev->pciDev == pPciArg->pciDev) &&
(pPciDev->pciFunc == pPciArg->pciFunc))
{
pPciArg->pCookie = (void *)pDevOnBus;
return OK;
}
return ERROR;
}
/*******************************************************************************
*
* vxbPciGetDevInstance - get the specified PCI device's VxBus device ID
*
* This routine return the specified PCI device's VxBus device ID
*
* RETURNS: NULL or VxBus Device ID.
*
*/
VXB_DEVICE_ID vxbPciGetDevInstance
(
VXB_DEVICE_ID busCtrlID,
UINT8 busNo, /* bus number */
UINT8 deviceNo, /* device number */
UINT8 funcNo /* function number */
)
{
struct vxbPciDevice pArg;
bzero ((char *)&pArg, sizeof(struct vxbPciDevice));
pArg.pciBus = busNo;
pArg.pciDev = deviceNo;
pArg.pciFunc = funcNo;
/* Don't care return value. */
(void) vxbSubDevAction (busCtrlID, vxbPciGetDevInstActFunc, (char *)&pArg,
VXB_ITERATE_INSTANCES);
return pArg.pCookie;
}
#ifdef VXB_LEGACY_ACCESS
/******************************************************************************
*
* vxbAccessFree - Free VxBus access structure
*
* This routine frees the access structure for VxBus PCI devices
*
* RETURNS: N/A
*
* ERRNO: N/A
*/
LOCAL void vxbAccessFree
(
VXB_DEVICE_ID pDev
)
{
hwMemFree((char *)(pDev->pAccess));
}
/******************************************************************************
*
* vxbPciAccessCookieSet - Initialize cookie used by legacy access
*
* This routine sets the cookie used for legacy access
*
* RETURNS: N/A
*
* ERRNO: N/A
*/
LOCAL void vxbPciAccessCookieSet
(
VXB_DEVICE_ID pDev,
UINT32 * pBaseAddr
)
{
/* store the pointer to the array in the access structure */
pDev->pAccess->pCookie = (void *)pBaseAddr;
}
#endif /* VXB_LEGACY_ACCESS */