text: /* $NoKeywords:$ */
  /**
   * @file
   *
   * Coherent Feature Northbridge routines.
   *
   * Provide access to hardware for routing, coherent discovery.
   *
   * @xrefitem bom "File Content Label" "Release Content"
   * @e project:      AGESA
   * @e sub-project:  HyperTransport
   * @e \$Revision: 35136 $   @e \$Date: 2010-07-16 11:29:48 +0800 (Fri, 16 Jul 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 "htNb.h"
  #include "htNbHardwareFam10.h"
  #include "htNbCoherent.h"
  #include "Filecode.h"
  CODE_GROUP (G1_PEICC)
  RDATA_GROUP (G1_PEICC)
  
  #define FILECODE PROC_HT_NBCOMMON_HTNBCOHERENT_FILECODE
  /*----------------------------------------------------------------------------
   *                          DEFINITIONS AND MACROS
   *
   *----------------------------------------------------------------------------
   */
  
  /*----------------------------------------------------------------------------
   *                           TYPEDEFS AND STRUCTURES
   *
   *----------------------------------------------------------------------------
   */
  /*----------------------------------------------------------------------------
   *                        PROTOTYPES OF LOCAL FUNCTIONS
   *
   *----------------------------------------------------------------------------
   */
  
  /***************************************************************************
   ***               FAMILY/NORTHBRIDGE SPECIFIC FUNCTIONS                 ***
   ***************************************************************************/
  
  /*----------------------------------------------------------------------------------------*/
  /**
   * Establish a Temporary route from one Node to another.
   *
   * @HtNbMethod{::F_WRITE_ROUTING_TABLE}
   *
   * This routine will modify the routing tables on the
   * SourceNode to cause it to route both request and response traffic to the
   * targetNode through the specified Link.
   *
   * @note: This routine is to be used for early discovery and initialization.  The
   * final routing tables must be loaded some other way because this
   * routine does not address the issue of probes, or independent request
   * response paths.
   *
   * @param[in]     Node     the Node that will have it's routing tables modified.
   * @param[in]     Target   For routing to Node target
   * @param[in]     Link     Link from Node to target
   * @param[in]     Nb       this northbridge
   */
  VOID
  WriteRoutingTable (
    IN       UINT8       Node,
    IN       UINT8       Target,
    IN       UINT8       Link,
    IN       NORTHBRIDGE *Nb
    )
  {
    PCI_ADDR Reg;
    UINT32 Temp;
  
    ASSERT ((Node < MAX_NODES) && (Target < MAX_NODES) && (Link < Nb->MaxLinks));
    Temp = (Nb->SelfRouteResponseMask | Nb->SelfRouteRequestMask) << (Link + 1);
    Reg.AddressValue = MAKE_SBDFO (MakePciSegmentFromNode (Node),
                                   MakePciBusFromNode (Node),
                                   MakePciDeviceFromNode (Node),
                                   CPU_HTNB_FUNC_00,
                                   REG_ROUTE0_0X40 + (Target * 4));
    LibAmdPciWrite (AccessWidth32, Reg, &Temp, Nb->ConfigHandle);
  }
  
  /*----------------------------------------------------------------------------------------*/
  /**
   * Modifies the NodeID register on the target Node
   *
   * @HtNbMethod{::F_WRITE_NODEID}
   *
   * @param[in]     Node   the Node that will have its NodeID altered.
   * @param[in]     NodeID the new value for NodeID
   * @param[in]     Nb     this northbridge
   */
  VOID
  WriteNodeID (
    IN       UINT8       Node,
    IN       UINT8       NodeID,
    IN       NORTHBRIDGE *Nb
    )
  {
    PCI_ADDR Reg;
    UINT32 Temp;
    Temp = NodeID;
    ASSERT ((Node < MAX_NODES) && (NodeID < MAX_NODES));
    Reg.AddressValue = MAKE_SBDFO (MakePciSegmentFromNode (Node),
                                   MakePciBusFromNode (Node),
                                   MakePciDeviceFromNode (Node),
                                   CPU_HTNB_FUNC_00,
                                   REG_NODE_ID_0X60);
    LibAmdPciWriteBits (Reg, 2, 0, &Temp, Nb->ConfigHandle);
  }
  
  /*----------------------------------------------------------------------------------------*/
  /**
   * Read the Default Link
   *
   * @HtNbMethod{::F_READ_DEFAULT_LINK}
   *
   * Read the DefLnk (the source Link of the current packet) from Node. Since this code
   * is running on the BSP, this should be the Link pointing back towards the BSP.
   *
   * @param[in]     Node   the Node that will have its NodeID altered.
   * @param[in]     Nb     this northbridge
   *
   * @return The HyperTransport Link where the request to
   *         read the default Link came from.
   */
  UINT8
  ReadDefaultLink (
    IN       UINT8       Node,
    IN       NORTHBRIDGE *Nb
    )
  {
    UINT32 DefaultLink;
    PCI_ADDR Reg;
    UINT32 Temp;
  
    DefaultLink = 0;
    Reg.AddressValue = MAKE_SBDFO (MakePciSegmentFromNode (Node),
                                   MakePciBusFromNode (Node),
                                   MakePciDeviceFromNode (Node),
                                   CPU_HTNB_FUNC_00,
                                   REG_LINK_INIT_CONTROL_0X6C);
  
    ASSERT ((Node < MAX_NODES));
    LibAmdPciReadBits (Reg, 3, 2, &DefaultLink, Nb->ConfigHandle);
    LibAmdPciReadBits (Reg, 8, 8, &Temp, Nb->ConfigHandle);
    DefaultLink |= (Temp << 2);
    return (UINT8)DefaultLink;
  }
  
  /*----------------------------------------------------------------------------------------*/
  /**
   * Turns routing tables on for a given Node
   *
   * @HtNbMethod{::F_ENABLE_ROUTING_TABLES}
   *
   * @param[in] Node the Node that will have it's routing tables enabled
   * @param[in] Nb   this northbridge
   */
  VOID
  EnableRoutingTables (
    IN       UINT8       Node,
    IN       NORTHBRIDGE *Nb
    )
  {
    PCI_ADDR Reg;
    UINT32 Temp;
    Temp = 0;
    ASSERT ((Node < MAX_NODES));
    Reg.AddressValue = MAKE_SBDFO (MakePciSegmentFromNode (Node),
                                   MakePciBusFromNode (Node),
                                   MakePciDeviceFromNode (Node),
                                   CPU_HTNB_FUNC_00,
                                   REG_LINK_INIT_CONTROL_0X6C);
    LibAmdPciWriteBits (Reg, 0, 0, &Temp, Nb->ConfigHandle);
  }
  
  /*----------------------------------------------------------------------------------------*/
  /**
   * Turns routing tables off for a given Node
   *
   * @HtNbMethod{::F_DISABLE_ROUTING_TABLES}
   *
   * @param[in] Node the Node that will have it's routing tables disabled
   * @param[in] Nb   this northbridge
   */
  VOID
  DisableRoutingTables (
    IN       UINT8       Node,
    IN       NORTHBRIDGE *Nb
    )
  {
    PCI_ADDR Reg;
    UINT32 Temp;
    Temp = 1;
    ASSERT ((Node < MAX_NODES));
    Reg.AddressValue = MAKE_SBDFO (MakePciSegmentFromNode (Node),
                                   MakePciBusFromNode (Node),
                                   MakePciDeviceFromNode (Node),
                                   CPU_HTNB_FUNC_00,
                                   REG_LINK_INIT_CONTROL_0X6C);
    LibAmdPciWriteBits (Reg, 0, 0, &Temp, Nb->ConfigHandle);
  }
  
  /*----------------------------------------------------------------------------------------*/
  /**
   * Verify that the Link is coherent, connected, and ready
   *
   * @HtNbMethod{::F_VERIFY_LINK_IS_COHERENT}
   *
   * @param[in]     Node    the Node that will be examined
   * @param[in]     Link    the Link on that Node to examine
   * @param[in]     Nb      this northbridge
   *
   * @retval        TRUE    The Link has the following status
   *                        -  LinkCon=1,           Link is connected
   *                        -  InitComplete=1,      Link initialization is complete
   *                        -  NC=0,                Link is coherent
   *                        -  UniP-cLDT=0,         Link is not Uniprocessor cLDT
   *                        -  LinkConPend=0        Link connection is not pending
   *  @retval       FALSE   The Link has some other status
  */
  BOOLEAN
  VerifyLinkIsCoherent (
    IN       UINT8       Node,
    IN       UINT8       Link,
    IN       NORTHBRIDGE *Nb
    )
  {
    UINT32 LinkType;
    PCI_ADDR LinkBase;
  
    ASSERT ((Node < MAX_NODES) && (Link < Nb->MaxLinks));
  
    LinkBase = Nb->MakeLinkBase (Node, Link, Nb);
  
    //  FN0_98/A4/C4 = LDT Type Register
    LinkBase.Address.Register += HTHOST_LINK_TYPE_REG;
    LibAmdPciRead (AccessWidth32, LinkBase, &LinkType, Nb->ConfigHandle);
  
    //  Verify LinkCon = 1, InitComplete = 1, NC = 0, UniP-cLDT = 0, LinkConPend = 0
    return (BOOLEAN) ((LinkType & HTHOST_TYPE_MASK) ==  HTHOST_TYPE_COHERENT);
  }
  
  /*----------------------------------------------------------------------------------------*/
  /**
   * Read the token stored in the scratchpad register field.
   *
   * @HtNbMethod{::F_READ_TOKEN}
   *
   * Use the CPU core count as a scratch pad.
   *
   * @note The location used to store the token is arbitrary.  The only requirement is
   * that the location warm resets to zero, and that using it will have no ill-effects
   * during HyperTransport initialization.
   *
   * @param[in]     Node    the Node that will be examined
   * @param[in]     Nb      this northbridge
   *
   * @return        the Token read from the Node
   */
  UINT8
  ReadToken (
    IN       UINT8       Node,
    IN       NORTHBRIDGE *Nb
    )
  {
    UINT32 Temp;
    PCI_ADDR Reg;
  
    ASSERT ((Node < MAX_NODES));
    // Use CpuCnt as a scratch register
    Reg.AddressValue = MAKE_SBDFO (MakePciSegmentFromNode (Node),
                                   MakePciBusFromNode (Node),
                                   MakePciDeviceFromNode (Node),
                                   CPU_HTNB_FUNC_00,
                                   REG_NODE_ID_0X60);
    LibAmdPciReadBits (Reg, 19, 16, &Temp, Nb->ConfigHandle);
  
    return (UINT8)Temp;
  }
  
  
  /*----------------------------------------------------------------------------------------*/
  /**
   * Write the token stored in the scratchpad register
   *
   * @HtNbMethod{::F_WRITE_TOKEN}
   *
   * Use the CPU core count as a scratch pad.
   *
   * @note The location used to store the token is arbitrary.  The only requirement is
   * that the location warm resets to zero, and that using it will have no ill-effects
   * during HyperTransport initialization.
   *
   * @param[in]     Node  the Node that marked with token
   * @param[in]     Value the token Value
   * @param[in]     Nb    this northbridge
   */
  VOID
  WriteToken (
    IN       UINT8       Node,
    IN       UINT8       Value,
    IN       NORTHBRIDGE *Nb
    )
  {
    PCI_ADDR Reg;
    UINT32 Temp;
    Temp = Value;
    ASSERT ((Node < MAX_NODES));
    // Use CpuCnt as a scratch register
    Reg.AddressValue = MAKE_SBDFO (MakePciSegmentFromNode (Node),
                                   MakePciBusFromNode (Node),
                                   MakePciDeviceFromNode (Node),
                                   CPU_HTNB_FUNC_00,
                                   REG_NODE_ID_0X60);
    LibAmdPciWriteBits (Reg, 19, 16, &Temp, Nb->ConfigHandle);
  }
  
  /*----------------------------------------------------------------------------------------*/
  /**
   * Full Routing Table Register initialization
   *
   * @HtNbMethod{::F_WRITE_FULL_ROUTING_TABLE}
   *
   * Write the routing table entry for Node to target, using the request Link, response
   * Link, and broadcast Links provided.
   *
   * @param[in]     Node            the Node that will be examined
   * @param[in]     Target          the Target Node for these routes
   * @param[in]     ReqLink         the Link for requests to Target
   * @param[in]     RspLink         the Link for responses to Target
   * @param[in]     BroadcastLinks  the broadcast Links
   * @param[in]     Nb              this northbridge
   */
  VOID
  WriteFullRoutingTable (
    IN       UINT8       Node,
    IN       UINT8       Target,
    IN       UINT8       ReqLink,
    IN       UINT8       RspLink,
    IN       UINT32      BroadcastLinks,
    IN       NORTHBRIDGE *Nb
    )
  {
    PCI_ADDR Reg;
    UINT32 Value;
  
    Value = 0;
    ASSERT ((Node < MAX_NODES) && (Target < MAX_NODES));
    if (ReqLink == ROUTE_TO_SELF) {
      Value |= Nb->SelfRouteRequestMask;
    } else {
      Value |= Nb->SelfRouteRequestMask << (ReqLink + 1);
    }
  
    if (RspLink == ROUTE_TO_SELF) {
      Value |= Nb->SelfRouteResponseMask;
    } else {
      Value |= Nb->SelfRouteResponseMask << (RspLink + 1);
    }
  
    // Allow us to accept a Broadcast ourselves, then set broadcasts for routes
    Value |= (UINT32)1 << Nb->BroadcastSelfBit;
    Value |= (UINT32)BroadcastLinks << (Nb->BroadcastSelfBit + 1);
  
    Reg.AddressValue = MAKE_SBDFO (MakePciSegmentFromNode (Node),
                                   MakePciBusFromNode (Node),
                                   MakePciDeviceFromNode (Node),
                                   CPU_HTNB_FUNC_00,
                                   REG_ROUTE0_0X40 + (Target * 4));
    LibAmdPciWrite (AccessWidth32, Reg, &Value, Nb->ConfigHandle);
  }
  
  /*----------------------------------------------------------------------------------------*/
  /**
   * Determine whether a Node is compatible with the discovered configuration so far.
   *
   * @HtNbMethod{::F_IS_ILLEGAL_TYPE_MIX}.
   *
   * Currently, that means the family, extended family of the new Node are the
   * same as the BSP's.
   *
   * @param[in] Node the Node
   * @param[in] Nb   this northbridge
   *
   * @retval   TRUE    the new node is not compatible
   * @retval   FALSE   the new node is compatible
   */
  BOOLEAN
  IsIllegalTypeMix (
    IN       UINT8       Node,
    IN       NORTHBRIDGE *Nb
    )
  {
    return ((BOOLEAN) ((Nb->MakeKey (Node, Nb) & Nb->CompatibleKey) == 0));
  }
  
  /*----------------------------------------------------------------------------------------*/
  /**
   * Fix (hopefully) exceptional conditions.
   *
   * @HtNbMethod{::F_HANDLE_SPECIAL_NODE_CASE}.
   *
   * Currently, this routine is implemented for all coherent HT families to check
   * vendor ID of coherent Node. If the vendor ID is 0x1022 then return FALSE,
   * or return TRUE.
   *
   * @param[in] Node  The Node which need to be checked.
   * @param[in] Link  The link to check for special conditions.
   * @param[in] State our global state.
   * @param[in] Nb    this northbridge.
   *
   * @retval    TRUE  This node received special handling.
   * @retval    FALSE This node was not handled specially, handle it normally.
   *
   */
  BOOLEAN
  HandleSpecialNodeCase (
    IN       UINT8       Node,
    IN       UINT8       Link,
    IN       STATE_DATA  *State,
    IN       NORTHBRIDGE *Nb
    )
  {
    BOOLEAN Result;
    PCI_ADDR Reg;
    UINT32 VendorID;
  
    Result = TRUE;
  
    Reg.AddressValue = MAKE_SBDFO (MakePciSegmentFromNode (Node),
                                   MakePciBusFromNode (Node),
                                   MakePciDeviceFromNode (Node),
                                   0,
                                   0);
  
    LibAmdPciReadBits (Reg, 15, 0, &VendorID, Nb->ConfigHandle);
    if (VendorID == 0x1022) {
      Result = FALSE;
    }
  
    return Result;
  }
  