/*++

Copyright (c) 1996, 1997 Microsoft Corporation

Module Name:

    init.c

Abstract:

    This module contains the initialization code for the Compaq driver.

Author:

    John Vert (jvert) 10/21/1997

Revision History:

    12/15/97    John Theisen    Modified to support Compaq Chipsets
    10/09/98    John Theisen    Modified to workaround an RCC silicon bug.  
                                If RCC Silicon Rev <= 4, then limit DATA_RATE to 1X.
    10/09/98    John Theisen    Modified to enable Shadowing in the SP700 prior to MMIO writes.
    01/15/98    John Theisen    Modified to set RQ depth to be 0x0F for all REV_IDs
    03/14/00    Peter Johnston  Add support for HE chipset.

--*/

#include "AGPCPQ.H"

ULONG AgpExtensionSize = sizeof(AGPCPQ_EXTENSION);
PAGP_FLUSH_PAGES AgpFlushPages = NULL;  // not implemented


NTSTATUS
AgpInitializeTarget(
    IN PVOID AgpExtension
    )
/*******************************************************************************
*                                                                          
* Routine Functional Description:                                            
*                                                                            
*   This function is the entrypoint for initialization of the AGP Target.  It 
*   is called first, and performs initialization of the chipset and extension.
*               
* Assumptions:  Regardless of whether we are running on a dual north bridge platform, 
*               this driver will only be installed and invoked once (for the AGP bridge at B0D0F1).
*
* Arguments:
*   
*   AgpExtension -- Supplies the AGP Extension
*
* Return Value:
*
*   STATUS_SUCCESS if successfull
*                                                                       
*******************************************************************************/

{
    ULONG               DeviceVendorID  = 0;
    ULONG               BAR1            = 0;
    PAGPCPQ_EXTENSION   Extension       = AgpExtension;
    PHYSICAL_ADDRESS    pa;
    ULONG               BytesReturned   = 0;

    AGPLOG(AGP_NOISE, ("AgpCpq: AgpInitializeTarget entered.\n"));
    //
    // Initialize our Extension
    //
    RtlZeroMemory(Extension, sizeof(AGPCPQ_EXTENSION));

    //
    // Verify that the chipset is a supported RCC Chipset.
    //
    ReadCPQConfig(&DeviceVendorID,OFFSET_DEVICE_VENDOR_ID,sizeof(DeviceVendorID));

    if ((DeviceVendorID != AGP_CNB20_LE_IDENTIFIER)   &&
        (DeviceVendorID != AGP_CNB20_HE_IDENTIFIER)   &&
        (DeviceVendorID != AGP_CNB20_HE4X_IDENTIFIER) &&
        (DeviceVendorID != AGP_CMIC_GC_IDENTIFIER)) {
        AGPLOG(AGP_CRITICAL,
            ("AGPCPQ - AgpInitializeTarget was called for platform %08x, which is not a known RCC AGP chipset!\n",
            DeviceVendorID));
        return(STATUS_UNSUCCESSFUL);
    }

    Extension->DeviceVendorID = DeviceVendorID;

    //
    // Read the chipset's BAR1 Register, and then map the chipset's 
    // Memory Mapped Control Registers into kernel mode address space.
    //
    ReadCPQConfig(&BAR1,OFFSET_BAR1,sizeof(BAR1));
    pa.HighPart = 0;
    pa.LowPart = BAR1;
    Extension->MMIO = (PMM_CONTROL_REGS)MmMapIoSpace(pa, sizeof(MM_CONTROL_REGS), FALSE);

    if (Extension->MMIO == NULL)
        {
        AGPLOG(AGP_CRITICAL,
            ("AgpInitializeTarget - Couldn't allocate %08x bytes for MMIO\n",
            sizeof(MM_CONTROL_REGS)));
        return(STATUS_UNSUCCESSFUL);
        }

    // 
    // Verify that the chipset's Revision ID is correct, but only complain, if it isn't.
    //
    if (Extension->MMIO->RevisionID < LOWEST_REVISION_ID_SUPPORTED)
        {
        AGPLOG(AGP_CRITICAL,
            ("AgpInitializeTarget - Revision ID = %08x, it should = 1.\n",
            Extension->MMIO->RevisionID));
        }

    //
    // Determine if there are two RCC North Bridges in this system.
    //
    DeviceVendorID = 0;
    BytesReturned = HalGetBusDataByOffset(PCIConfiguration, SECONDARY_LE_BUS_ID, SECONDARY_LE_HOSTPCI_SLOT_ID, 
        &DeviceVendorID, OFFSET_DEVICE_VENDOR_ID, sizeof(DeviceVendorID));

    if((DeviceVendorID != Extension->DeviceVendorID) || (BytesReturned != sizeof(DeviceVendorID)) ) {
        Extension->IsHPSA = FALSE;        
    } else {
        Extension->IsHPSA = TRUE;
    }  

    //
    // Enable the GART cache
    //
    if (Extension->IsHPSA) DnbSetShadowBit(0);

    Extension->MMIO->FeatureControl.GARTCacheEnable = 1;

    Extension->GartPointer = 0;
    Extension->SpecialTarget = 0;
    
    //
    // If the chipset supports linking then enable linking.
    //
    if (Extension->MMIO->Capabilities.LinkingSupported==1) {
        Extension->MMIO->FeatureControl.LinkingEnable=1;
    }

    if (Extension->IsHPSA) DnbSetShadowBit(1);
    
    return(STATUS_SUCCESS);
}


NTSTATUS
AgpInitializeMaster(
    IN  PVOID AgpExtension,
    OUT ULONG *AgpCapabilities
    )
/*******************************************************************************
*                                                                          
* Routine Functional Description:                                            
*                                                                            
*   This function is the entrypoint for initialization of the AGP Master. It
*   is called after Target initialization, and is intended to be used to 
*   initialize the AGP capabilities of both master and target.
*
* Arguments:
*   
*   AgpExtension -- Supplies the AGP Extension
*   
*   AgpCapabilities -- Returns the "software-visible" capabilities of the device
*
* Return Value:
*
*   NTSTATUS       
*                                                                
*******************************************************************************/

{
    NTSTATUS Status;
    PCI_AGP_CAPABILITY MasterCap;
    PCI_AGP_CAPABILITY TargetCap;
    PAGPCPQ_EXTENSION Extension = AgpExtension;
    ULONG SBAEnable;
    ULONG FastWrite;
    ULONG DataRate;
    UCHAR RevID = 0;
    BOOLEAN ReverseInit;

    AGPLOG(AGP_NOISE, ("AgpCpq: AgpInitializeMaster entered.\n"));

    //
    // Get the master and target AGP capabilities
    //
    Status = AgpLibGetMasterCapability(AgpExtension, &MasterCap);
    if (!NT_SUCCESS(Status)) 
        {
        AGPLOG(AGP_CRITICAL,
            ("AGPCPQInitializeDevice - AgpLibGetMasterCapability failed %08lx\n",
            Status));
        return(Status);
        }

    Status = AgpLibGetPciDeviceCapability(AGP_CPQ_BUS_ID, 
                                          AGP_CPQ_PCIPCI_SLOT_ID,
                                          &TargetCap);
    if (!NT_SUCCESS(Status)) 
        {
        AGPLOG(AGP_CRITICAL,
               ("AGPCPQInitializeDevice - AgpLibGetPciDeviceCapability failed %08lx\n",
               Status));
        return(Status);
        }

    //
    // Determine the greatest common denominator for data rate.
    //
    DataRate = TargetCap.AGPStatus.Rate & MasterCap.AGPStatus.Rate;
    ASSERT(DataRate != 0);

    //
    // Select the highest common rate.
    //
    if (DataRate & PCI_AGP_RATE_4X) {
        DataRate = PCI_AGP_RATE_4X;
    } else if (DataRate & PCI_AGP_RATE_2X) {
        DataRate = PCI_AGP_RATE_2X;
    } else if (DataRate & PCI_AGP_RATE_1X) {
        DataRate = PCI_AGP_RATE_1X;
    }

    //
    // Previously a call was made to change the rate (successfully),
    // use this rate again now
    //
    if (Extension->SpecialTarget & AGP_FLAG_SPECIAL_RESERVE) {
        DataRate = (ULONG)((Extension->SpecialTarget & 
                            AGP_FLAG_SPECIAL_RESERVE) >>
                           AGP_FLAG_SET_RATE_SHIFT);
    }

    //
    // FIX RCC silicon bugs:  
    // If RevID <= 4, then the reported Data Rate is 2X but the chip only supports 1X.
    // Regardless of RevID the reported RQDepth should be 0x0F.
    //
    if (Extension->DeviceVendorID == AGP_CNB20_LE_IDENTIFIER) {
        ReadCPQConfig(&RevID, OFFSET_REV_ID, sizeof(RevID));

        ASSERT(TargetCap.AGPStatus.RequestQueueDepthMaximum == 0x10); 
        TargetCap.AGPStatus.RequestQueueDepthMaximum = 0x0F;

        if (RevID <= MAX_REV_ID_TO_LIMIT_1X) {
            DataRate = PCI_AGP_RATE_1X;
        }
    }

    //
    // Enable SBA if both master and target support it.
    //
    SBAEnable = (TargetCap.AGPStatus.SideBandAddressing & 
                 MasterCap.AGPStatus.SideBandAddressing);

    //
    // Enable FastWrite if both master and target support it.
    //
    
    FastWrite = (TargetCap.AGPStatus.FastWrite &
                 MasterCap.AGPStatus.FastWrite);

    //
    // Enable the Master first.
    //
    ReverseInit = 
        (Extension->SpecialTarget & AGP_FLAG_REVERSE_INITIALIZATION) ==
        AGP_FLAG_REVERSE_INITIALIZATION;
    if (ReverseInit) {
        MasterCap.AGPCommand.Rate = DataRate;
        MasterCap.AGPCommand.AGPEnable = 1;
        MasterCap.AGPCommand.SBAEnable = SBAEnable;
        MasterCap.AGPCommand.FastWriteEnable = FastWrite;
        MasterCap.AGPCommand.RequestQueueDepth = TargetCap.AGPStatus.RequestQueueDepthMaximum;
        Status = AgpLibSetMasterCapability(AgpExtension, &MasterCap);
        if (!NT_SUCCESS(Status)) 
        {
            AGPLOG(AGP_CRITICAL,
                   ("AGPCPQInitializeDevice - AgpLibSetMasterCapability %08lx failed %08lx\n",
                    &MasterCap,
                    Status));
        }
    }

    //
    // Now enable the Target.
    //
    TargetCap.AGPCommand.Rate = DataRate;
    TargetCap.AGPCommand.AGPEnable = 1;
    TargetCap.AGPCommand.SBAEnable = SBAEnable;
    TargetCap.AGPCommand.FastWriteEnable = FastWrite;
    Status = AgpLibSetPciDeviceCapability(AGP_CPQ_BUS_ID, 
                                          AGP_CPQ_PCIPCI_SLOT_ID, 
                                          &TargetCap);
    if (!NT_SUCCESS(Status)) 
        {
        AGPLOG(AGP_CRITICAL,
               ("AGPCPQInitializeDevice - AgpLibSetPciDeviceCapability %08lx for target failed %08lx\n",
                &TargetCap,
                Status));
        return(Status);
        }

    if (!ReverseInit) {
        MasterCap.AGPCommand.Rate = DataRate;
        MasterCap.AGPCommand.AGPEnable = 1;
        MasterCap.AGPCommand.SBAEnable = SBAEnable;
        MasterCap.AGPCommand.FastWriteEnable = FastWrite;
        MasterCap.AGPCommand.RequestQueueDepth = TargetCap.AGPStatus.RequestQueueDepthMaximum;
        Status = AgpLibSetMasterCapability(AgpExtension, &MasterCap);
        if (!NT_SUCCESS(Status)) 
        {
            AGPLOG(AGP_CRITICAL,
                   ("AGPCPQInitializeDevice - AgpLibSetMasterCapability %08lx failed %08lx\n",
                    &MasterCap,
                    Status));
        }
    }

#if DBG
    {
        PCI_AGP_CAPABILITY CurrentCap;

        //
        // Read them back, see if it worked
        //
        Status = AgpLibGetMasterCapability(AgpExtension, &CurrentCap);
        ASSERT(NT_SUCCESS(Status));
        //
        // If the target request queue depth is greater than the master will
        // allow, it will be trimmed.   Loosen the assert to not require an
        // exact match.
        //
        ASSERT(CurrentCap.AGPCommand.RequestQueueDepth <= MasterCap.AGPCommand.RequestQueueDepth);
        CurrentCap.AGPCommand.RequestQueueDepth = MasterCap.AGPCommand.RequestQueueDepth;
        ASSERT(RtlEqualMemory(&CurrentCap.AGPCommand, &MasterCap.AGPCommand, sizeof(CurrentCap.AGPCommand)));


        Status = AgpLibGetPciDeviceCapability(AGP_CPQ_BUS_ID, 
                                              AGP_CPQ_PCIPCI_SLOT_ID,
                                              &CurrentCap);
        ASSERT(NT_SUCCESS(Status));
        ASSERT(RtlEqualMemory(&CurrentCap.AGPCommand, &TargetCap.AGPCommand, 
            sizeof(CurrentCap.AGPCommand)));
    }
#endif

    //
    // Indicate that we can map memory through the GART aperture
    //
    *AgpCapabilities = AGP_CAPABILITIES_MAP_PHYSICAL;

    return(Status);
}


NTSTATUS 
DnbSetShadowBit(
    ULONG SetToOne
    )
//
// This routine is required, (because of a new requirement in the RCC chipset.).
// When there are two NorthBridge's, the shadow bit must be set to 0 prior
// to any MMIO writes, and then set back to 1 when done.
//
{
    NTSTATUS    Status = STATUS_SUCCESS;    // Assume Success
    UCHAR       ShadowByte = 0;
    ULONG       BytesReturned = 0;
    ULONG       length = 1;

    if (SetToOne == 1) {

        //
        // Set the shadow bit to a one. (This disables shadowing.)
        //
        BytesReturned = HalGetBusDataByOffset(PCIConfiguration, SECONDARY_LE_BUS_ID,
            SECONDARY_LE_HOSTPCI_SLOT_ID, &ShadowByte, OFFSET_SHADOW_BYTE, length);

        if(BytesReturned != length) {
            AGPLOG(AGP_CRITICAL,("ERROR: Failed to read shadow register!\n"));
            Status = STATUS_UNSUCCESSFUL;
            goto exit_routine;
        }

        ShadowByte |= FLAG_DISABLE_SHADOW;

        HalSetBusDataByOffset(PCIConfiguration, SECONDARY_LE_BUS_ID,
            SECONDARY_LE_HOSTPCI_SLOT_ID, &ShadowByte, OFFSET_SHADOW_BYTE, length);

        if(BytesReturned != length) {
            AGPLOG(AGP_CRITICAL,("ERROR: Failed to write shadow register!\n"));
            Status = STATUS_UNSUCCESSFUL;
            goto exit_routine;
        }

    } else {

        //
        // Set the shadow bit to a zero. (This enables shadowing.)
        //
        BytesReturned = HalGetBusDataByOffset(PCIConfiguration, SECONDARY_LE_BUS_ID,
            SECONDARY_LE_HOSTPCI_SLOT_ID, &ShadowByte, OFFSET_SHADOW_BYTE, length);

        if(BytesReturned != length) {
            AGPLOG(AGP_CRITICAL,("ERROR: Failed to read shadow register!"));
            Status = STATUS_UNSUCCESSFUL;
            goto exit_routine;
        }

        ShadowByte &= MASK_ENABLE_SHADOW;

        HalSetBusDataByOffset(PCIConfiguration, SECONDARY_LE_BUS_ID,
            SECONDARY_LE_HOSTPCI_SLOT_ID, &ShadowByte, OFFSET_SHADOW_BYTE, length);

        if(BytesReturned != length) {
            AGPLOG(AGP_CRITICAL,("ERROR: Failed to write shadow register!"));
            Status = STATUS_UNSUCCESSFUL;
            goto exit_routine;
        }
    }

exit_routine:

    return(Status);
}