/* $NoKeywords:$ */
/**
 * @file
 *
 * Fch SATA controller Library
 *
 * SATA Library
 *
 * @xrefitem bom "File Content Label" "Release Content"
 * @e project:     AGESA
 * @e sub-project: FCH
 * @e \$Revision: 46088 $   @e \$Date: 2011-01-28 11:24:26 +0800 (Fri, 28 Jan 2011) $
 *
 */
/*
*****************************************************************************
*
* Copyright (c) 2011, Advanced Micro Devices, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of Advanced Micro Devices, Inc. nor the names of
 *       its contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
****************************************************************************
*/
#include "FchPlatform.h"
#include "Filecode.h"
#define FILECODE PROC_FCH_SATA_SATALIB_FILECODE

extern  VOID  FchSataSetDeviceNumMsi (IN VOID  *FchDataPtr);

/**
 * sataBar5setting - Config SATA BAR5
 *
 *
 * @param[in] FchDataPtr - Fch configuration structure pointer.
 * @param[in] *Bar5Ptr   - SATA BAR5 buffer.
 *
 */
VOID
SataBar5setting (
  IN  VOID       *FchDataPtr,
  IN  UINT32     *Bar5Ptr
  )
{
  FCH_DATA_BLOCK         *LocalCfgPtr;
  AMD_CONFIG_PARAMS      *StdHeader;

  LocalCfgPtr = (FCH_DATA_BLOCK *) FchDataPtr;
  StdHeader = LocalCfgPtr->StdHeader;

  //
  //Get BAR5 value
  //
  ReadPci (((SATA_BUS_DEV_FUN << 16) + FCH_SATA_REG24), AccessWidth32, Bar5Ptr, StdHeader);

  //
  //Assign temporary BAR if is not already assigned
  //
  if ( (*Bar5Ptr == 0) || (*Bar5Ptr == - 1) ) {
    //
    //assign temporary BAR5
    //
    if ( (LocalCfgPtr->Sata.TempMmio == 0) || (LocalCfgPtr->Sata.TempMmio == - 1) ) {
      *Bar5Ptr = 0xFEC01000;
    } else {
      *Bar5Ptr = LocalCfgPtr->Sata.TempMmio;
    }
    WritePci (((SATA_BUS_DEV_FUN << 16) + FCH_SATA_REG24), AccessWidth32, Bar5Ptr, StdHeader);
  }

  //
  //Clear Bits 9:0
  //
  *Bar5Ptr = *Bar5Ptr & 0xFFFFFC00;
}

/**
 * sataEnableWriteAccess - Enable Sata PCI configuration space
 *
 * @param[in]   StdHeader
 *
 */
VOID
SataEnableWriteAccess (
  IN  AMD_CONFIG_PARAMS   *StdHeader
  )
{
  //
  // BIT0 Enable write access to PCI header
  //
  RwPci (((SATA_BUS_DEV_FUN << 16) + FCH_SATA_REG40), AccessWidth8, 0xff, BIT0, StdHeader);
}

/**
 * sataDisableWriteAccess - Disable Sata PCI configuration space
 *
 * @param[in]   StdHeader
 *
 */
VOID
SataDisableWriteAccess (
  IN  AMD_CONFIG_PARAMS   *StdHeader
  )
{
  //
  // Disable write access to PCI header
  //
  RwPci (((SATA_BUS_DEV_FUN << 16) + FCH_SATA_REG40), AccessWidth8, ~BIT0, 0, StdHeader);
}



#ifdef SATA_BUS_DEV_FUN_FPGA

/**
 * FchSataBar5settingFpga
 *
 * @param[in]   LocalCfgPtr
 * @param[in]   Bar5
 *
 */
VOID
FchSataBar5settingFpga (
  IN       FCH_DATA_BLOCK     *LocalCfgPtr,
  IN       UINT32             *Bar5
  )
{
  UINT8  Value;

  //Get BAR5 value
  ReadPci (((SATA_BUS_DEV_FUN_FPGA << 16) + FCH_SATA_REG24), AccWidthUint32, Bar5);

  //Assign temporary BAR if is not already assigned
  if ( (*Bar5 == 0) || (*Bar5 == - 1) ) {
    //assign temporary BAR5
    if ( (LocalCfgPtr->Sata.TempMMIO == 0) || (LocalCfgPtr->Sata.TempMMIO == - 1) ) {
      *Bar5 = 0xFEC01000;
    } else {
      *Bar5 = LocalCfgPtr->Sata.TempMMIO;
    }
    WritePci (((SATA_BUS_DEV_FUN_FPGA << 16) + FCH_SATA_REG24), AccWidthUint32, Bar5);
  }

  //Clear Bits 9:0
  *Bar5 = *Bar5 & 0xFFFFFC00;
  Value = 0x07;
  WritePci (((SATA_BUS_DEV_FUN_FPGA << 16) + 0x04), AccWidthUint8, &Value);
  WritePci (((PCIB_BUS_DEV_FUN << 16) + 0x04), AccWidthUint8, &Value);
}

/**
 * FchSataDriveDetectionFpga
 *
 * @param[in]   LocalCfgPtr
 * @param[in]   Bar5
 *
 */
VOID
FchSataDriveDetectionFpga (
  IN       FCH_DATA_BLOCK     *LocalCfgPtr,
  IN       UINT32             *Bar5
  )
{
  UINT32                 SataBarFpgaInfo;
  UINT8                  PortNum;
  UINT8                  SataFpaPortType;
  UINT16                 IoBase;
  UINT16                 SataFpgaLoopVarWord;
  AMD_CONFIG_PARAMS      *StdHeader;

  StdHeader = LocalCfgPtr->StdHeader;

  TRACE ((DMSG_FCH_TRACE, "FCH - Entering sata drive detection procedure\n\n"));
  TRACE ((DMSG_FCH_TRACE, "SATA BAR5 is %X \n", *pBar5));

  for ( PortNum = 0; PortNum < 4; PortNum++ ) {
    ReadMem (*Bar5 + FCH_SATA_BAR5_REG128 + PortNum * 0x80, AccWidthUint32, &SataBarFpgaInfo);
    if ( ( SataBarFpgaInfo & 0x0F ) == 0x03 ) {
      if ( PortNum & BIT0 ) {
        //this port belongs to secondary channel
        ReadPci (((UINT32) (SATA_BUS_DEV_FUN_FPGA << 16) + FCH_SATA_REG18), AccWidthUint16, &IoBase);
      } else {
        //this port belongs to primary channel
        ReadPci (((UINT32) (SATA_BUS_DEV_FUN_FPGA << 16) + FCH_SATA_REG10), AccWidthUint16, &IoBase);
      }

      //if legacy ide mode, then the bar registers don't contain the correct values. So we need to hardcode them
      if ( LocalCfgPtr->Sata.SataClass == SataLegacyIde ) {
        IoBase = ( (0x170) | ((UINT16) ( (~((UINT8) (PortNum & BIT0) << 7)) & 0x80 )) );
      }

      if ( PortNum & BIT1 ) {
        //this port is slave
        SataFpaPortType = 0xB0;
      } else {
        //this port is master
        SataFpaPortType = 0xA0;
      }

      IoBase &= 0xFFF8;
      LibAmdIoWrite (AccessWidth8, IoBase + 6, &SataFpaPortType, StdHeader);

      //Wait in loop for 30s for the drive to become ready
      for ( SataFpgaLoopVarWord = 0; SataFpgaLoopVarWord < 300000; SataFpgaLoopVarWord++ ) {
        LibAmdIoRead (AccessWidth8, IoBase + 7, &SataFpaPortType, StdHeader);
        if ( (SataFpaPortType & 0x88) == 0 ) {
          break;
        }
        FchStall (100, StdHeader);
      }
    }
  }
}

/**
 * FchSataDriveFpga -
 *
 *
 *
 * @param[in] FchDataPtr Fch configuration structure pointer.
 *
 */
VOID
FchSataDriveFpga (
  IN  VOID     *FchDataPtr
  )
{
  UINT32                 Bar5;
  FCH_DATA_BLOCK         *LocalCfgPtr;

  LocalCfgPtr = (FCH_DATA_BLOCK *) FchDataPtr;

  Bar5 = 0;
  SataBar5setting (LocalCfgPtr, &Bar5);

  FchSataBar5settingFpga (LocalCfgPtr, &Bar5);
  FchSataDriveDetectionFpga (LocalCfgPtr, &Bar5);
}

#endif

