/*
* Copyright (c) 2006-2008 Advanced Micro Devices,Inc. ("AMD").
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of the
* License, or (at your option) any later version.
*
* This code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.

* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307 USA 
*/

//*****************************************************************************
//*     Implements reads of virtualized PCI configuration headers
//*****************************************************************************


#include "VSA2.H"
#include "PCI.H"
#include "VPCI.H"
#include "PROTOS.H"
#include "SYSMGR.H"
#include "CHIPSET.H"


// External function declarations:
ULONG pascal Get_Device_Status(PCI_HEADER_ENTRY *);
ULONG pascal Handle_PCI_PM_Rd(PCI_HEADER_ENTRY *);
ULONG pascal Handle_EHCI_Rd(PCI_HEADER_ENTRY *);
ULONG pascal Read_MSR_LO(ULONG);
UCHAR pascal Get_Latency(PCI_HEADER_ENTRY *);
PCI_HEADER_ENTRY * pascal Get_Structure(USHORT);
extern Hardware HardwareInfo;



// External variable declarations:
extern DESCRIPTOR MSRs[];
extern UCHAR Shift, AlignedReg, Function;
extern PCI_HEADER_ENTRY * CommandPtr;
extern ULONG ATA_Error;



//***********************************************************************
// Reads an embedded PCI register
//***********************************************************************
ULONG pascal Read_EPCI(UCHAR AlignedReg)
{ ULONG MsrAddr;

  MsrAddr = MSRs[CommandPtr->Link].MsrAddr;
  if (MsrAddr) {  
    (UCHAR)MsrAddr = AlignedReg/4;
    return Read_MSR_LO(MsrAddr);
  } else {
    return 0;  
  }  
}


//***********************************************************************
//
// This routine implements reads to virtualized configuration space.
//
// NOTES:
// 1) Misaligned accesses are handled.  If an access crosses a DWORD 
//    boundary, only the bytes within the addressed DWORD are read.
//    The remaining bytes return FFs.
// 2) The variable Pci points to a PCI_HEADER_ENTRY entry that defines
//    the state and characteristics of the register being read.
//***********************************************************************
ULONG pascal Virtual_PCI_Read_Handler(USHORT PCI_Address)
{ ULONG Data;
  register PCI_HEADER_ENTRY * Pci;


  // Get ptr to virtualized table entry
  Pci = Get_Structure(PCI_Address);


  switch ((USHORT)Pci) {

    case UNIMPLEMENTED_FUNCTION:
      Data = 0xFFFFFFFF;
      break;

    case UNIMPLEMENTED_REGISTER:
      Data = 0x00000000;
      break;

    default:
      // Handle special cases
      if (Pci->Flag & EPCI_R && AlignedReg < 0x40) {

        // Embedded PCI device: read its h/w PCI configuration space
        Data = Read_EPCI(AlignedReg);

        if (AlignedReg == COMMAND) {
          // Mark device '66 MHz capable'
          Data |= PCI_66MHZ_CAPABLE;
        }
        break;    
      }

      Data = Pci->Value;

      switch (AlignedReg) {

        case COMMAND:
          // Get device's Status
          Data |= Get_Device_Status(Pci);
          break;

        case BAR0:
        case BAR1:
        case BAR2:
        case BAR3:
        case BAR4:
        case BAR5:
          // If I/O BAR, set bit 0
          if (Pci->Flag & IO_BAR) {
            Data |= 1;
          }
          break;

        case CACHE_LINE:
          Pci->LatencyTimer = Get_Latency(Pci);
          Data = Pci->Value;
          break;

#if SUPPORT_CAPABILITIES
        case (PCI_PM_REG+4):  // 0x44
          if (Pci->Flag & PCI_PM) {
            Data = Handle_PCI_PM_Rd(Pci);
            break;
          }

        case USBLEGCTLSTS:
          if (Pci->Flag & PCI_EHCI) {
            Data = Handle_EHCI_Rd(Pci);
            break;
          }
#endif

        // Thor ATA vendor-specific registers:
        case IDE_CFG:   // 0x40
        case IDE_DTC:	// 0x48
        case IDE_CAST:  // 0x4C
        case IDE_ETC:   // 0x50
          if (Pci->LBar) {
            ULONG MsrAddr = ATA_Error;

            (USHORT)MsrAddr = (USHORT)Pci->LBar;
            Data = Read_MSR_LO(MsrAddr);
          }
          break;

      }
  }


  // Handle non-dword aligned accesses
  Data >>= Shift;
  Data  |= 0xFFFFFFFFL << (32-Shift);

  return Data;
}   



