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
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 */
|
|
|