/***************************************************************************************\

Module Name:    MtxPci.c

Description:    Interface for PCI devices.

References:     MtxPci.htm

    Copyright (c) 2000, Matrox Graphics Inc.
    All Rights Reserved.

\***************************************************************************************/


// --------------------------------------------------------------------------------------
//                      H E A D E R   F I L E S   R E Q U I R E D
// --------------------------------------------------------------------------------------

#include "precomp.h"

// --------------------------------------------------------------------------------------
//                        C O N S T A N T S   A N D   T Y P E S
// --------------------------------------------------------------------------------------

// --------------------------------------------------------------------------------------
//                           G L O B A L   V A R I A B L E S
// --------------------------------------------------------------------------------------

// --------------------------------------------------------------------------------------
//                    L O C A L   F U N C T I O N   P R O T O T Y P E S
// --------------------------------------------------------------------------------------

EXTERN_C DWORD pciIsDevicePresent(
    PCIHANDLE  hPci
);

EXTERN_C DWORD pciIsMultiFunctionDevice(
    PCIHANDLE  hPci
);

EXTERN_C DWORD pciIsPciToPciBridge(
    PCIHANDLE  hPci
);

EXTERN_C DWORD pciGetSecBusNumber(
    PCIHANDLE  hBridge, 
    DWORD*     pdwBusNumber
);

// --------------------------------------------------------------------------------------
//                           I N L I N E S   A N D   M A C R O S
// --------------------------------------------------------------------------------------


/***************************************************************************************\

Function:       pciScanBus

Description:    Scan all devices on the PCI bus, calling the PCISEARCH functions at
                each node.

Parameters:     fnSearch        Compare function execute for each node
                pvRefData       Reference data passed to the compare function

Return Value:   DWORD 
                TRUE if the compare function returned TRUE, FALSE if the compare
                function returned FALSE for all devices on the PCI bus.

Algorithm:      |
                | For all bus
                | | For all devices
                | | | For all functions
                | | | | If pciIsDevicePresent
                | | | | | Detect multi-function
                | | | | | Detect pci-pci bridge
                | | | | | Call fnSearch function
                | | | | | If fnSearch return TRUE
                | | | | | | Exit all loops
                |

Comments:       None.

\***************************************************************************************/
DWORD pciScanBus(
    PCISEARCH   fnSearch, 
    void*       pvRefData)
{
    PCIHANDLE   hPci;
    DWORD       ulMaxBus;
    DWORD       ulMaxDevice;
    DWORD       ulBus;
    DWORD       ulDevice;
    DWORD       ulFunction;
    DWORD       ulMaxFunction;
    DWORD       ulSecBus;

    hPci = 0;

    ulMaxBus = 0;
    for (ulBus=0; (ulBus<=ulMaxBus); ulBus++)
    {
        ulMaxDevice = 31;
        for (ulDevice=0; (ulDevice<=ulMaxDevice); ulDevice++)
        {
            ulMaxFunction = 0;
            for (ulFunction=0; (ulFunction<=ulMaxFunction); ulFunction++)
            {
                hPci = ((ulBus << 8) | (ulDevice << 3) | (ulFunction << 0));

                if (pciIsDevicePresent(hPci))
                {
                    if (ulFunction == 0)
                    {
                        if (pciIsMultiFunctionDevice(hPci))
                        {
                            ulMaxFunction = 7;
                        }
                    }

                    if (pciGetSecBusNumber(hPci, &ulSecBus))
                    {
                        if (ulSecBus > ulMaxBus)
                        {
                            ulMaxBus = ulSecBus;
                        }
                    }

                    if (fnSearch(hPci, pvRefData))
                    {
                        return (TRUE);
                    }
                }
            }
        }
    }

    return (FALSE);
}


/***************************************************************************************\

Function:       pciScan

Description:    See pciScanBus for more details.

Parameters:     

Return Value:   DWORD 

Comments:       None.

\***************************************************************************************/
DWORD pciScan(
    PCISEARCH   fnSearch,
    void*       pvRefData)
{
    return pciScanBus(fnSearch, pvRefData);
}


/***************************************************************************************\

Function:       pciFindCapability

Description:    Search for a capability section in the capability list.

Parameters:     hPci            PCI address
                pdwOffset       Address of DWORD to store offset of found section
                dwCapId         ID of capability section to find

Return Value:   DWORD 
                TRUE if the section is found, FALSE otherwise.

Algorithm:      |
                | If caplist is availlable
                | | Read CAP_PTR register
                | |
                | | While cap_ptr is valid
                | | | Read CAP_HEADER register
                | | |
                | | | If cap_id == dwCapId
                | | | | Section is found
                | | |
                | | | Goto cap_next_ptr
                |

Comments:       None.

\***************************************************************************************/
DWORD pciFindCapability(
    PCIHANDLE   hPci, 
    DWORD*      pdwOffset, 
    DWORD       dwCapId)
{
    DWORD               bFoundCap;
    ST_PCI_DEVCTRL      DevCtrl;
    ST_PCI_CAP_PTR      CapPtr;
    ST_PCI_CAP_HEADER   CapHeader;

    bFoundCap = FALSE;

    csReadDword(hPci, PCI_DEVCTRL, &DevCtrl.all);
    if (DevCtrl.caplist)
    {
        csReadDword(hPci, PCI_CAP_PTR, &CapPtr.all);

        while (CapPtr.cap_ptr >= 0x40 && CapPtr.cap_ptr < 0xFF)
        {
            csReadDword(hPci, CapPtr.cap_ptr, &CapHeader.all);

            if (CapHeader.cap_id == dwCapId)
            {
                *pdwOffset = CapPtr.cap_ptr;
                bFoundCap = TRUE;
                break;
            }

            CapPtr.cap_ptr = CapHeader.cap_next_ptr;
        }
    }

    return (bFoundCap);
}


/***************************************************************************************\

Function:       pciFindPMCapability

Description:    None.

Parameters:     hPci
                pdwOffset

Return Value:   DWORD 
                Not Specified.

Comments:       None.

\***************************************************************************************/
DWORD pciFindPMCapability(
    PCIHANDLE   hPci, 
    DWORD*      pdwOffset)
{
    return pciFindCapability(hPci, pdwOffset, CAP_ID_PM);
}


/***************************************************************************************\

Function:       pciFindAGPCapability

Description:    None.

Parameters:     hPci
                pdwOffset

Return Value:   DWORD 
                Not Specified.

Comments:       None.

\***************************************************************************************/
DWORD pciFindAGPCapability(
    PCIHANDLE   hPci, 
    DWORD*      pdwOffset)
{
    return pciFindCapability(hPci, pdwOffset, CAP_ID_AGP);
}


/***************************************************************************************\

Function:       pciGetTermbase

Description:    None.

Parameters:     hPci
                dwOffset
                pdwBase
                pdwSize

Return Value:   DWORD 
                Not Specified.

Comments:       None.

\***************************************************************************************/
DWORD pciGetTermbase(
    PCIHANDLE   hPci, 
    DWORD       dwOffset, 
    DWORD*      pdwBase, 
    DWORD*      pdwSize)
{
    DWORD dwTmp;
    DWORD dwMask;
    
    csReadDword (hPci, dwOffset, &dwTmp);
    csWriteDword(hPci, dwOffset, 0xFFFFFFFF);
    csReadDword (hPci, dwOffset, &dwMask);
    csWriteDword(hPci, dwOffset, dwTmp);

    *pdwBase = (dwTmp & 0xFFFFFFF0);
    *pdwSize = (dwMask & 0xFFFFFFF0);

    return (TRUE);
}


/***************************************************************************************\

Function:       pciGetLocation

Description:    Get the location information for the PCI device.

Parameters:     hPci
                poInfo

Return Value:   DWORD 
                Not Specified.

Comments:       None.

\***************************************************************************************/
DWORD pciGetLocation(
    PCIHANDLE       hPci,
    PCILOCATION*    poInfo)
{
    poInfo->bus = ((hPci >> 8) & 0xFF);
    poInfo->device = ((hPci >> 3) & 0x1F);
    poInfo->function = ((hPci >> 0) & 0x07);
    poInfo->reserved = 0;

    return TRUE;
}


/***************************************************************************************\

Function:       pciIsDevicePresent

Description:    Detect if a device is present at the given address.

Parameters:     hPci            PCI address

Return Value:   DWORD
                TRUE if the device is present.

Comments:       None.

\***************************************************************************************/
DWORD pciIsDevicePresent(
    PCIHANDLE   hPci)
{
    WORD    wDevID;
    DWORD   bIsPresent;

    bIsPresent = FALSE;

    csReadWord(hPci, PCI_DEVID, &wDevID);

    if ((wDevID != 0) && (wDevID != (WORD)(-1)))
    {
        bIsPresent = TRUE;
    }

    return (bIsPresent);
}


/***************************************************************************************\

Function:       pciIsMultiFunctionDevice

Description:    Detect if the device has other devices present in its sub-functions.

Parameters:     hPci            PCI handle for main device

Return Value:   DWORD 
                TRUE if the device has sub-functions.

Comments:       None.

\***************************************************************************************/
DWORD pciIsMultiFunctionDevice(
    PCIHANDLE   hPci)
{
    DWORD bIsMulti;
    ST_PCI_HEADER Header;

    bIsMulti = FALSE;

    csReadDword(hPci, PCI_HEADER, &Header.all);

    if (Header.multifunction)
    {
        bIsMulti = TRUE;
    }

    return (bIsMulti);
}


/***************************************************************************************\

Function:       pciIsPciToPciBridge

Description:    Detect if the given device is a PCI-to-PCI bridge.

Parameters:     hPci            PCI handle of device

Return Value:   DWORD 
                TRUE if the device is a PCI-to-PCI bridge, FALSE otherwise.

Comments:       None.

\***************************************************************************************/
DWORD pciIsPciToPciBridge(
    PCIHANDLE   hPci)
{
    DWORD bIsBridge;
    ST_PCI_HEADER Header;

    bIsBridge = FALSE;

    csReadDword(hPci, PCI_HEADER, &Header.all);

    if (Header.header == HEADER_TYPE_BRIDGE)
    {
        bIsBridge = TRUE;
    }

    return (bIsBridge);
}


/***************************************************************************************\

Function:       pciGetSecBusNumber

Description:    Get the 'Secondary bus number' value from if the given device is a 
                PCI-to-PCI bridge.

Parameters:     hBridge         PCI handle of device
                pdwBusNumber    Address of DWORD to store found 'Secondary bus number'

Return Value:   DWORD 
                Not Specified.

Comments:       None.

\***************************************************************************************/
DWORD pciGetSecBusNumber(
    PCIHANDLE   hBridge, 
    DWORD*      pdwBusNumber)
{
    DWORD bGetBus;
    ST_PCI_BRIDGE_BUS_CFG busInfo;

    bGetBus = FALSE;

    if (pciIsPciToPciBridge(hBridge))
    {
        csReadDword(hBridge, PCI_BRIDGE_BUS_CFG, &busInfo.all);

        if ((busInfo.secBusNb != 0xFF) && (busInfo.secBusNb != 0x00))
        {
            *pdwBusNumber = busInfo.secBusNb;
            bGetBus = TRUE;
        }
    }

    return (bGetBus);
}
