/**
 * @file
 *
 * System Tuning Family 10h specific routines
 *
 * Support for Traffic Distribution and buffer tunings which
 * can not be done in a register table.
 *
 * @xrefitem bom "File Content Label" "Release Content"
 * @e project:      AGESA
 * @e sub-project:  HyperTransport
 * @e \$Revision: 44323 $   @e \$Date: 2010-12-22 01:24:58 -0700 (Wed, 22 Dec 2010) $
 *
 */
/*
*****************************************************************************
*
* 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.
*
* ***************************************************************************
*
*/

/*
 *----------------------------------------------------------------------------
 *                                MODULES USED
 *
 *----------------------------------------------------------------------------
 */



#include "AGESA.h"
#include "amdlib.h"
#include "Ids.h"
#include "Topology.h"
#include "htFeat.h"
#include "htInterface.h"
#include "htNb.h"
#include "htNbHardwareFam10.h"
#include "htNbSystemFam10.h"
#include "Filecode.h"
#define FILECODE PROC_HT_FAM10_HTNBSYSTEMFAM10_FILECODE
/*----------------------------------------------------------------------------
 *                          DEFINITIONS AND MACROS
 *
 *----------------------------------------------------------------------------
 */

/*----------------------------------------------------------------------------
 *                           TYPEDEFS AND STRUCTURES
 *
 *----------------------------------------------------------------------------
 */
/**
 * Register Fields for an individual link pair.
 */
typedef struct {
  UINT32 Enable:1;            ///< Enable distribution on this pair.
  UINT32 Asymmetric:1;        ///< Links are different widths.
  UINT32 MasterSelect:3;      ///< The master link.
  UINT32 AlternateSelect:3;   ///< The alternate link.
} PAIR_SELECT_FIELDS;

/**
 * Register access union for ::PAIR_SELECT_FIELDS.
 */
typedef union {
  UINT32 Value;               ///< access as a 32 bit value or register.
  PAIR_SELECT_FIELDS Fields;  ///< access individual fields.
} PAIR_SELECT;

/*----------------------------------------------------------------------------
 *                        PROTOTYPES OF LOCAL FUNCTIONS
 *
 *----------------------------------------------------------------------------
 */

/***************************************************************************
 ***               FAMILY/NORTHBRIDGE SPECIFIC FUNCTIONS                 ***
 ***************************************************************************/

/*----------------------------------------------------------------------------------------*/
/**
 * Set the traffic distribution register for the Links provided.
 *
 * @HtNbMethod{::F_WRITE_TRAFFIC_DISTRIBUTION}
 *
 * @param[in]     Links01   coherent Links from Node 0 to 1
 * @param[in]     Links10   coherent Links from Node 1 to 0
 * @param[in]     Nb        this northbridge
 */
VOID
Fam10WriteTrafficDistribution (
  IN       UINT32       Links01,
  IN       UINT32       Links10,
  IN       NORTHBRIDGE  *Nb
  )
{
  UINT32 Temp;
  PCI_ADDR TrafficDistReg;

  TrafficDistReg.AddressValue = MAKE_SBDFO (MakePciSegmentFromNode (0),
                                            MakePciBusFromNode (0),
                                            MakePciDeviceFromNode (0),
                                            CPU_HTNB_FUNC_00,
                                            REG_HT_TRAFFIC_DIST_0X164);

  // Node 0
  // DstLnk
  LibAmdPciWriteBits (TrafficDistReg, 23, 16, &Links01, Nb->ConfigHandle);
  // DstNode = 1, cHTPrbDistEn = 1, cHTRspDistEn = 1, cHTReqDistEn = 1
  Temp = 0x0107;
  LibAmdPciWriteBits (TrafficDistReg, 15, 0, &Temp, Nb->ConfigHandle);

  TrafficDistReg.AddressValue = MAKE_SBDFO (MakePciSegmentFromNode (1),
                                            MakePciBusFromNode (1),
                                            MakePciDeviceFromNode (1),
                                            CPU_HTNB_FUNC_00,
                                            REG_HT_TRAFFIC_DIST_0X164);

  // Node 1
  // DstLnk
  LibAmdPciWriteBits (TrafficDistReg, 23, 16, &Links10, Nb->ConfigHandle);
  // DstNode = 0, cHTPrbDistEn = 1, cHTRspDistEn = 1, cHTReqDistEn = 1
  Temp = 0x0007;
  LibAmdPciWriteBits (TrafficDistReg, 15, 0, &Temp, Nb->ConfigHandle);
}

/*----------------------------------------------------------------------------------------*/
/**
 * Write a link pair to the link pair distribution and fixups.
 *
 * @HtNbMethod{::F_WRITE_LINK_PAIR_DISTRIBUTION}
 *
 * Set the links as a pair using the link pair index provided.  Set asymmetric attribute as
 * provided. If the Master Link is not currently used as the route, fixup the routes for all
 * nodes which specify the alternate link.
 *
 * @param[in]    Node           Set the pair on this node
 * @param[in]    ConnectedNode  The Node to which this link pair directly connects.
 * @param[in]    Pair           Using this pair set in the register
 * @param[in]    Asymmetric     True if different widths
 * @param[in]    MasterLink     Set this as the master link and in the route
 * @param[in]    AlternateLink  Set this as the alternate link
 * @param[in]    Nb             this northbridge
 *
 */
VOID
Fam10WriteLinkPairDistribution (
  IN       UINT8        Node,
  IN       UINT8        ConnectedNode,
  IN       UINT8        Pair,
  IN       BOOLEAN      Asymmetric,
  IN       UINT8        MasterLink,
  IN       UINT8        AlternateLink,
  IN       NORTHBRIDGE  *Nb
  )
{
  PCI_ADDR Reg;
  UINT32 CurrentRoute;
  UINT32 MasterRoute;
  UINT32 AlternateRoute;
  PAIR_SELECT Selection;
  UINT32 RouteIndex;

  ASSERT ((Node < MAX_NODES) && (ConnectedNode < MAX_NODES));
  ASSERT (Pair < MAX_LINK_PAIRS);
  ASSERT (MasterLink < Nb->MaxLinks);
  ASSERT (AlternateLink < Nb->MaxLinks);

  // Make the master link the route for all routes to or through NodeB, by replacing all occurrences of
  // Alternate link with Master link.  If routing used the master link, no update is necessary.
  MasterRoute = (((1 << Nb->BroadcastSelfBit) | Nb->SelfRouteResponseMask | Nb->SelfRouteRequestMask) << (MasterLink + 1));
  AlternateRoute = (((1 << Nb->BroadcastSelfBit) | Nb->SelfRouteResponseMask | Nb->SelfRouteRequestMask) << (AlternateLink + 1));
  Reg.AddressValue = MAKE_SBDFO (MakePciSegmentFromNode (Node),
                                 MakePciBusFromNode (Node),
                                 MakePciDeviceFromNode (Node),
                                 CPU_HTNB_FUNC_00,
                                 REG_ROUTE0_0X40);
  for (RouteIndex = 0; RouteIndex < MAX_NODES; RouteIndex++) {
    Reg.Address.Register = REG_ROUTE0_0X40 + (RouteIndex * 4);
    LibAmdPciReadBits (Reg, 31, 0, &CurrentRoute, Nb->ConfigHandle);
    if ((CurrentRoute & AlternateRoute) != 0) {
      // Since Master and Alternate are redundant, the route must use one or the other but not both.
      ASSERT ((CurrentRoute & MasterRoute) == 0);
      // Set the master route for Request, Response or Broadcast only if the alternate was used for that case.
      // Example, use of a link as a broadcast link is typically not the same route register as its use for Request, Response.
      CurrentRoute = ((CurrentRoute & ~AlternateRoute) |
                      ((((CurrentRoute & AlternateRoute) >> (AlternateLink + 1)) << (MasterLink + 1)) & MasterRoute));
      LibAmdPciWriteBits (Reg, 31, 0, &CurrentRoute, Nb->ConfigHandle);
    }
  }

  // Set the Link Pair and Enable it
  Selection.Fields.Enable = 1;
  Selection.Fields.Asymmetric = Asymmetric;
  Selection.Fields.MasterSelect = MasterLink;
  Selection.Fields.AlternateSelect = AlternateLink;
  Reg.AddressValue = MAKE_SBDFO (MakePciSegmentFromNode (Node),
                                 MakePciBusFromNode (Node),
                                 MakePciDeviceFromNode (Node),
                                 CPU_HTNB_FUNC_00,
                                 REG_HT_LINK_PAIR_DIST_0X1E0);
  LibAmdPciWriteBits (
    Reg,
    ((PAIR_SELECT_OFFSET * (Pair + 1)) - 1),
    (PAIR_SELECT_OFFSET * Pair),
    &Selection.Value,
    Nb->ConfigHandle
    );
}

/*----------------------------------------------------------------------------------------*/
/**
 * Family 10h specific tunings.
 *
 * @HtNbMethod{::F_BUFFER_OPTIMIZATIONS}
 *
 * Buffer tunings are inherently northbridge specific. Check for specific configs
 * which require adjustments and apply any standard workarounds to this Node.
 *
 * @param[in]     Node   the Node to tune
 * @param[in]     State  global state
 * @param[in]     Nb     this northbridge
 */
VOID
Fam10BufferOptimizations (
  IN       UINT8       Node,
  IN       STATE_DATA  *State,
  IN       NORTHBRIDGE *Nb
  )
{
  UINT32 Temp;
  PCI_ADDR currentPtr;
  PCI_ADDR GangedReg;
  UINT8 i;

  ASSERT (Node < MAX_NODES);

  //
  // Link to XCS Token Count Tuning
  //
  // For each active Link that we reganged (so this unfortunately can't go into the PCI reg
  // table), we have to switch the Link to XCS Token Counts to the ganged state.
  // We do this here for the non - uma case, which is to write the values that would have
  // been power on defaults if the Link was ganged at cold reset.
  //
  for (i = 0; i < (State->TotalLinks * 2); i++) {
    if (((*State->PortList)[i].NodeID == Node) && ((*State->PortList)[i].Type == PORTLIST_TYPE_CPU)) {
      // If the Link is greater than 4, this is a subLink 1, so it is not reganged.
      if ((*State->PortList)[i].Link < 4) {
        currentPtr.AddressValue = MAKE_SBDFO (MakePciSegmentFromNode (Node),
                                              MakePciBusFromNode (Node),
                                              MakePciDeviceFromNode (Node),
                                              CPU_NB_FUNC_03,
                                              REG_NB_LINK_XCS_TOKEN0_3X148 + (4 * (*State->PortList)[i].Link)
          );
        if ((*State->PortList)[i].SelRegang) {
          // Handle all the regang Token count adjustments

          // SubLink 0: [Probe0tok] = 2 [Rsp0tok] = 2 [PReq0tok] = 2 [Req0tok] = 2
          Temp = 0xAA;
          LibAmdPciWriteBits (currentPtr, 7, 0, &Temp, Nb->ConfigHandle);
          // SubLink 1: [Probe1tok] = 0 [Rsp1tok] = 0 [PReq1tok] = 0 [Req1tok] = 0
          Temp = 0;
          LibAmdPciWriteBits (currentPtr, 23, 16, &Temp, Nb->ConfigHandle);
          // [FreeTok] = 3
          Temp = 3;
          LibAmdPciWriteBits (currentPtr, 15, 14, &Temp, Nb->ConfigHandle);

        } else {
          // Read the regang bit in hardware
          GangedReg.AddressValue = MAKE_SBDFO (MakePciSegmentFromNode ((*State->PortList)[i].NodeID),
                                               MakePciBusFromNode ((*State->PortList)[i].NodeID),
                                               MakePciDeviceFromNode ((*State->PortList)[i].NodeID),
                                               CPU_HTNB_FUNC_00,
                                               REG_HT_LINK_EXT_CONTROL0_0X170 + (4 * (*State->PortList)[i].Link));
          LibAmdPciReadBits (GangedReg, 0, 0, &Temp, Nb->ConfigHandle);
          if (Temp == 1) {
            // handle a minor adjustment for strapped ganged Links.  If SelRegang is false we
            // didn't do the regang, so if the bit is on then it's hardware strapped.
            //

            // [FreeTok] = 3
            Temp = 3;
            LibAmdPciWriteBits (currentPtr, 15, 14, &Temp, Nb->ConfigHandle);
          }
        }
      }
    }
  }
}

/*----------------------------------------------------------------------------------------*/
/**
 * Family 10h specific tunings.
 *
 * @HtNbMethod{::F_BUFFER_OPTIMIZATIONS}
 *
 * Buffer tunings are inherently northbridge specific. Check for specific configs
 * which require adjustments and apply any standard workarounds to this Node.
 *
 * @param[in]     Node   the Node to tune
 * @param[in]     State  global state
 * @param[in]     Nb     this northbridge
 */
VOID
Fam10RevDBufferOptimizations (
  IN       UINT8       Node,
  IN       STATE_DATA  *State,
  IN       NORTHBRIDGE *Nb
  )
{
  UINT32 Temp;
  PCI_ADDR Reg;
  UINT8 i;
  FINAL_LINK_STATE FinalLinkState;
  UINT32 Widthin;
  UINT32 Widthout;

  ASSERT (Node < MAX_NODES);

  //
  // Internal link fixup.
  // When powering off internal link 2, a performance optimization may be possible where its buffers
  // can be made available to the external paired sublink.  If the conditions are met, do the fix up here.
  //
  for (i = 0; i < (State->TotalLinks * 2); i++) {
    if (((*State->PortList)[i].NodeID == Node) && ((*State->PortList)[i].Type == PORTLIST_TYPE_CPU)) {
      // Is this a sublink 0 paired with internal link 2?
      if (((*State->PortList)[i].Link < 4) &&
          (Nb->GetPackageLink (Node, ((*State->PortList)[i].Link + 4), Nb) == HT_LIST_MATCH_INTERNAL_LINK_2)) {
        FinalLinkState = State->HtInterface->GetIgnoreLink (Node, ((*State->PortList)[i].Link + 4), Nb->DefaultIgnoreLinkList, State);
        // Are we ignoring the internal link 2 with Power Off?
        if (FinalLinkState == POWERED_OFF) {
          // Read the regang bit in hardware.
          Reg.AddressValue = MAKE_SBDFO (MakePciSegmentFromNode (Node),
                                         MakePciBusFromNode (Node),
                                         MakePciDeviceFromNode (Node),
                                         CPU_HTNB_FUNC_00,
                                         REG_HT_LINK_EXT_CONTROL0_0X170 + (4 * (*State->PortList)[i].Link));
          LibAmdPciReadBits (Reg, 0, 0, &Temp, Nb->ConfigHandle);
          // If it's already ganged, skip to the width fix up.
          if (Temp == 0) {
            // Clear EndOfChain / XmitOff on internal sublink
            Reg = Nb->MakeLinkBase (Node, ((*State->PortList)[i].Link + 4), Nb);
            Reg.Address.Register += HTHOST_LINK_CONTROL_REG;
            Temp = 0;
            State->HtFeatures->SetHtControlRegisterBits (Reg, 7, 6, &Temp, State);

            // Gang the link
            Nb->SetLinkRegang (Node, (*State->PortList)[i].Link, Nb);
          }

          // Set InLnSt = PHY_OFF in register table.
          // Set sublink 0 widths to 8 bits
          if ((*State->PortList)[i].SelWidthOut > 8) {
            (*State->PortList)[i].SelWidthOut = 8;
          }
          if ((*State->PortList)[i].SelWidthIn > 8) {
            (*State->PortList)[i].SelWidthIn = 8;
          }
          Widthout = State->HtFeatures->ConvertWidthToBits ((*State->PortList)[i].SelWidthOut);
          Widthin = State->HtFeatures->ConvertWidthToBits ((*State->PortList)[i].SelWidthIn);
          Temp = (Widthin & 7) | ((Widthout & 7) << 4);
          Reg = Nb->MakeLinkBase (Node, (*State->PortList)[i].Link, Nb);
          Reg.Address.Register += HTHOST_LINK_CONTROL_REG;
          State->HtFeatures->SetHtControlRegisterBits (Reg, 31, 24, &Temp, State);
        }
      }
    }
  }
}

