www.pudn.com > PlxSdk.rar > PciSupport.c


/*******************************************************************************
 * Copyright (c) 2007 PLX Technology, Inc.
 *
 * PLX Technology Inc. licenses this software under specific terms and
 * conditions.  Use of any of the software or derviatives thereof in any
 * product without a PLX Technology chip is strictly prohibited.
 *
 * PLX Technology, Inc. provides this software AS IS, WITHOUT ANY WARRANTY,
 * EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, ANY WARRANTY OF
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  PLX makes no guarantee
 * or representations regarding the use of, or the results of the use of,
 * the software and documentation in terms of correctness, accuracy,
 * reliability, currentness, or otherwise; and you rely on the software,
 * documentation and results solely at your own risk.
 *
 * IN NO EVENT SHALL PLX BE LIABLE FOR ANY LOSS OF USE, LOSS OF BUSINESS,
 * LOSS OF PROFITS, INDIRECT, INCIDENTAL, SPECIAL OR CONSEQUENTIAL DAMAGES
 * OF ANY KIND.  IN NO EVENT SHALL PLX'S TOTAL LIABILITY EXCEED THE SUM
 * PAID TO PLX FOR THE PRODUCT LICENSED HEREUNDER.
 *
 ******************************************************************************/

/******************************************************************************
 *
 * File Name:
 *
 *      PciSupport.c
 *
 * Description:
 *
 *      This file contains the PCI support functions
 *
 * Revision History:
 *
 *      02-01-07 : PLX SDK v5.00
 *
 ******************************************************************************/


#include "PciSupport.h"




/***********************************************
 *               Globals
 **********************************************/
// Global ECAM 64-bit address from ACPI table (0=Not Probed, -1=Not Available)
static U32 Gbl_Acpi_Addr_ECAM[2] = {ACPI_PCIE_NOT_PROBED, ACPI_PCIE_NOT_PROBED};




/******************************************************************************
 *
 * Function   :  PlxPciRegisterRead
 *
 * Description:  Reads a PCI register at the specified offset
 *
 ******************************************************************************/
RETURN_CODE
PlxPciRegisterRead(
    U8   bus,
    U8   slot,
    U8   function,
    U16  offset,
    U32 *pValue
    )
{
    int             rc;
    U32             RegValue;
    struct pci_dev *pPciDevice;


    // For PCIe extended register, use Enhanced PCIe Mechanism
    if (offset >= 0x100)
    {
        return PlxPciExpressRegRead(
                   bus,
                   slot,
                   function,
                   offset,
                   pValue
                   );
    }

    // Offset must on a 4-byte boundary
    if (offset & 0x3)
    {
        *pValue = (U32)-1;
        return ApiInvalidOffset;
    }

    // Locate PCI device
    pPciDevice =
        pci_find_slot(
            bus,
            PCI_DEVFN(slot, function)
            );

    if (pPciDevice == NULL)
    {
        DebugPrintf((
            "ERROR - Device at bus %02d, slot %02d does not exist\n",
            bus, slot
            ));

        *pValue = (U32)-1;
        return ApiInvalidDeviceInfo;
    }

    rc =
        pci_read_config_dword(
            pPciDevice,
            offset,
            &RegValue
            );

    if (rc != 0)
    {
        DebugPrintf((
            "ERROR - Unable to read PCI register 0x%02x\n",
            offset
            ));

        *pValue = (U32)-1;
        return ApiConfigAccessFailed;
    }

    *pValue = RegValue;

    return ApiSuccess;
}




/******************************************************************************
 *
 * Function   :  PlxPciRegisterWrite
 *
 * Description:  Writes a value to a PCI register at the specified offset
 *
 ******************************************************************************/
RETURN_CODE
PlxPciRegisterWrite(
    U8  bus,
    U8  slot,
    U8  function,
    U16 offset,
    U32 value
    )
{
    int             rc;
    struct pci_dev *pPciDevice;


    // For PCIe extended register, use Enhanced PCIe Mechanism
    if (offset >= 0x100)
    {
        return PlxPciExpressRegWrite(
                   bus,
                   slot,
                   function,
                   offset,
                   value
                   );
    }

    // Offset must on a 4-byte boundary
    if (offset & 0x3)
    {
        return ApiInvalidOffset;
    }

    // Locate PCI device
    pPciDevice =
        pci_find_slot(
            bus,
            PCI_DEVFN(slot, function)
            );

    if (pPciDevice == NULL)
    {
        DebugPrintf((
            "ERROR - Device at bus %02d, slot %02d does not exist\n",
            bus, slot
            ));

        return ApiInvalidDeviceInfo;
    }

    rc =
        pci_write_config_dword(
            pPciDevice,
            offset,
            value
            );

    if (rc != 0)
    {
        DebugPrintf((
            "ERROR - Unable to write to PCI register 0x%02x\n",
            offset
            ));

        return ApiConfigAccessFailed;
    }

    return ApiSuccess;
}




/******************************************************************************
 *
 * Function   :  PlxPciExpressRegRead
 *
 * Description:  Reads a PCI Express register using the enhanced configuration mechanism
 *
 *****************************************************************************/
RETURN_CODE
PlxPciExpressRegRead(
    U8   bus,
    U8   slot,
    U8   function,
    U16  offset,
    U32 *pValue
    )
{
    U32 address;


    // Offset must on a 4-byte boundary
    if (offset & 0x3)
    {
        return ApiInvalidOffset;
    }

    // Check if PCIe ECAM was probed for
    if (Gbl_Acpi_Addr_ECAM[0] == ACPI_PCIE_NOT_PROBED)
        PlxProbeForEcamBase();

    // Check if Enhanced mechanism available through ACPI
    if (Gbl_Acpi_Addr_ECAM[0] == ACPI_PCIE_NOT_AVAILABLE)
    {
        *pValue = (U32)-1;
        return ApiUnsupportedFunction;
    }

    // Setup base address for register access
    address =
        Gbl_Acpi_Addr_ECAM[0] |
        (bus      << 20)      |
        (slot     << 15)      |
        (function << 12)      |
        (offset   <<  0);

    // Read the register
    *pValue = PHYS_MEM_READ_32(address);

    return ApiSuccess;
}




/******************************************************************************
 *
 * Function   :  PlxPciExpressRegWrite
 *
 * Description:  Writes a PCI Express register using the enhanced configuration mechanism
 *
 *****************************************************************************/
RETURN_CODE
PlxPciExpressRegWrite(
    U8  bus,
    U8  slot,
    U8  function,
    U16 offset,
    U32 value
    )
{
    U32 address;


    // Offset must on a 4-byte boundary
    if (offset & 0x3)
    {
        return ApiInvalidOffset;
    }

    // Check if PCIe ECAM was probed for
    if (Gbl_Acpi_Addr_ECAM[0] == ACPI_PCIE_NOT_PROBED)
        PlxProbeForEcamBase();

    // Check if Enhanced mechanism available through ACPI
    if (Gbl_Acpi_Addr_ECAM[0] == ACPI_PCIE_NOT_AVAILABLE)
    {
        return ApiUnsupportedFunction;
    }

    // Setup base address for register access
    address =
        Gbl_Acpi_Addr_ECAM[0] |
        (bus      << 20)      |
        (slot     << 15)      |
        (function << 12)      |
        (offset   <<  0);

    // Write the register
    PHYS_MEM_WRITE_32(address, value);

    return ApiSuccess;
}




/******************************************************************************
 *
 * Function   :  PlxProbeForEcamBase
 *
 * Description:  Probes for Enhanced Configuration Access Mechanism base
 *               address through ACPI
 *
 *****************************************************************************/
VOID
PlxProbeForEcamBase(
    VOID
    )
{
    U8             Str_ID[9];
    U32            Entry;
    U32            address;
    U32            value;
    U32            NumEntries;
    U32            Acpi_Addr_RSDP;
    U32            Acpi_Addr_RSDT;
    BOOLEAN        bFound;
    ACPI_RSDT_v1_0 Acpi_Rsdt;


    // Added for compiler warning
    Acpi_Addr_RSDP = 0;

    // Default to ACPI and/or ECAM not detected
    Gbl_Acpi_Addr_ECAM[0] = ACPI_PCIE_NOT_AVAILABLE;

    // Mark end of string
    Str_ID[8] = '\0';

    // Setup starting address on 16-byte boundary
    address = BIOS_MEM_START;
    address = (address + 0xF) & ~0xF;

    bFound = FALSE;

    // Scan system ROM for ACPI RSDP pointer
    do
    {
        // Read 8-bytes
        *(U32*)Str_ID       = PHYS_MEM_READ_32(address);
        *(U32*)(Str_ID + 4) = PHYS_MEM_READ_32(address + 4);

        // Check for header signature
        if (memcmp(
                "RSD PTR ",
                Str_ID,
                8       // 8 bytes
                ) == 0)
        {
            // Store ACPI RSDP table address
            Acpi_Addr_RSDP = address;

            bFound = TRUE;
        }
        else
        {
            // Increment to next 16-byte boundary
            address += 16;
        }
    }
    while (!bFound && (address <= BIOS_MEM_END));

    if (!bFound)
    {
        DebugPrintf(("ACPI Probe - ACPI not detected\n"));
        return;
    }

    DebugPrintf((
        "ACPI Probe - 'RSD PTR ' found at 0x%08X\n",
        Acpi_Addr_RSDP
        ));

    // Store RSDT address
    Acpi_Addr_RSDT = PHYS_MEM_READ_32(Acpi_Addr_RSDP + 16);

    // Get RSDT size
    Acpi_Rsdt.Length = PHYS_MEM_READ_32(Acpi_Addr_RSDT + 4);

    if (Acpi_Rsdt.Length == 0)
    {
        DebugPrintf(("ACPI Probe - Unable to read RSDT table length\n"));
        return;
    }

    // Calculate number of entries
    NumEntries = (Acpi_Rsdt.Length - sizeof(ACPI_RSDT_v1_0)) / sizeof(U32);

    if (NumEntries > 20)
    {
        DebugPrintf(("ACPI Probe - Unable to determine number of entries in RSDT table\n"));
        return;
    }

    DebugPrintf((
        "ACPI Probe - RSDT table at 0x%08X has %d entries\n",
        Acpi_Addr_RSDT,
        NumEntries
        ));

    // Get address of first entry
    Entry = Acpi_Addr_RSDT + sizeof(ACPI_RSDT_v1_0);

    bFound = FALSE;

    // Parse entry pointers for MCFG table
    while ((bFound == FALSE) && (NumEntries != 0))
    {
        // Get address of entry
        address = PHYS_MEM_READ_32(Entry);

        // Get table signature
        value = PHYS_MEM_READ_32(address);

        DebugPrintf((
            "ACPI Probe - Located '%c%c%c%c' table at 0x%08X\n",
            (char)(value >>  0),
            (char)(value >>  8),
            (char)(value >> 16),
            (char)(value >> 24),
            address
            ));

        // Check if MCFG table
        if (memcmp(
                "MCFG",
                &value,
                sizeof(U32)
                ) == 0)
        {
            // Get 64-bit base address of Enhanced Config Access Mechanism
            Gbl_Acpi_Addr_ECAM[0] = PHYS_MEM_READ_32(address + 44);
            Gbl_Acpi_Addr_ECAM[1] = PHYS_MEM_READ_32(address + 48);

            bFound = TRUE;
        }
        else
        {
            // Get address of next entry
            Entry += sizeof(U32);

            // Decrement count
            NumEntries--;
        }
    }

    if (bFound)
    {
        DebugPrintf((
            "ACPI Probe - PCIe ECAM is located at 0x%08X_%08X\n",
            Gbl_Acpi_Addr_ECAM[1], Gbl_Acpi_Addr_ECAM[0]
            ));
    }
    else
    {
        DebugPrintf((
            "ACPI Probe - MCFG entry not found (PCIe ECAM not supported)\n"
            ));
    }
}




/******************************************************************************
 *
 * Function   :  PlxPhysicalMemRead
 *
 * Description:  Maps a memory location and performs a read from it
 *
 *****************************************************************************/
U32
PlxPhysicalMemRead(
    U32 address,
    U8  size
    )
{
    U32   value;
    VOID *KernelVa;


    // Map address into kernel space
    KernelVa =
        ioremap(
            address,
            sizeof(U32)
            );

    if (KernelVa == NULL)
    {
        DebugPrintf(("ERROR: Unable to map address into kernel space\n"));
        return -1;
    }

    // Read memory
    switch (size)
    {
        case sizeof(U8):
            value = *(U8*)KernelVa;
            break;

        case sizeof(U16):
            value = *(U16*)KernelVa;
            break;

        case sizeof(U32):
            value = *(U32*)KernelVa;
            break;

        default:
            value = 0;
    }

    // Release the mapping
    iounmap(
        KernelVa
        );

    return value;
}





/******************************************************************************
 *
 * Function   :  PlxPhysicalMemWrite
 *
 * Description:  Maps a memory location and performs a write to it
 *
 *****************************************************************************/
U32
PlxPhysicalMemWrite(
    U32 address,
    U32 value,
    U8  size
    )
{
    VOID *KernelVa;


    // Map address into kernel space
    KernelVa =
        ioremap(
            address,
            sizeof(U32)
            );

    if (KernelVa == NULL)
    {
        DebugPrintf(("ERROR: Unable to map address into kernel space\n"));
        return -1;
    }

    // Write memory
    switch (size)
    {
        case sizeof(U8):
            *(U8*)KernelVa = (U8)value;
            break;

        case sizeof(U16):
            *(U16*)KernelVa = (U16)value;
            break;

        case sizeof(U32):
            *(U32*)KernelVa = (U32)value;
            break;
    }

    // Release the mapping
    iounmap(
        KernelVa
        );

    return 0;
}