/*
 * This file is part of the coreboot project.
 *
 * Copyright (C) 2007 Advanced Micro Devices, Inc.
 * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 *
 * This program 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 General Public License for more details.
 */


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

#undef FILECODE
#define FILECODE 0xF002
#include "h3ncmn.h"
#include "h3finit.h"
#include "h3ffeat.h"
#include "AsPsNb.h"

#include <device/pci.h>
#include <console/console.h>
#include <cpu/amd/msr.h>
#include <device/pci_def.h>
#include <device/pci_ids.h>
#include <northbridge/amd/amdfam10/raminit.h>
#include <northbridge/amd/amdfam10/amdfam10.h>


/*----------------------------------------------------------------------------
 *			DEFINITIONS AND MACROS
 *
 *----------------------------------------------------------------------------
 */

/* CPU Northbridge Functions */
#define CPU_HTNB_FUNC_00		0
#define CPU_HTNB_FUNC_04		4
#define CPU_ADDR_FUNC_01		1
#define CPU_NB_FUNC_03			3
#define CPU_NB_FUNC_05			5

/* Function 0 registers */
#define REG_ROUTE0_0X40		0x40
#define REG_ROUTE1_0X44		0x44
#define REG_NODE_ID_0X60		0x60
#define REG_UNIT_ID_0X64		0x64
#define REG_LINK_TRANS_CONTROL_0X68	0x68
#define REG_LINK_INIT_CONTROL_0X6C	0x6c
#define REG_HT_CAP_BASE_0X80		0x80
#define REG_NORTHBRIDGE_CFG_3X8C	0x8c
#define REG_HT_LINK_RETRY0_0X130	0x130
#define REG_HT_TRAFFIC_DIST_0X164	0x164
#define REG_HT_LINK_EXT_CONTROL0_0X170	0x170

#define HT_CONTROL_CLEAR_CRC		(~(3 << 8))

/* Function 1 registers */
#define REG_ADDR_CONFIG_MAP0_1XE0	0xE0
#define CPU_ADDR_NUM_CONFIG_MAPS	4

/* Function 3 registers */
#define REG_NB_SRI_XBAR_BUF_3X70	0x70
#define REG_NB_MCT_XBAR_BUF_3X78	0x78
#define REG_NB_FIFOPTR_3XDC		0xDC
#define REG_NB_CAPABILITY_3XE8		0xE8
#define REG_NB_CPUID_3XFC		0xFC
#define REG_NB_LINK_XCS_TOKEN0_3X148	0x148
#define REG_NB_DOWNCORE_3X190		0x190
#define REG_NB_CAPABILITY_5X84		0x84

/* Function 4 registers */


/*----------------------------------------------------------------------------
 *			TYPEDEFS AND STRUCTURES
 *
 *----------------------------------------------------------------------------
 */
/*----------------------------------------------------------------------------
 *			PROTOTYPES OF LOCAL FUNCTIONS
 *
 *----------------------------------------------------------------------------
 */

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

inline uint8_t is_gt_rev_d(void)
{
	uint8_t fam15h = 0;
	uint8_t rev_gte_d = 0;
	uint32_t family;
	uint32_t model;

	family = model = cpuid_eax(0x80000001);
	model = ((model & 0xf0000) >> 12) | ((model & 0xf0) >> 4);
	family = ((family & 0xf00000) >> 16) | ((family & 0xf00) >> 8);

	if (family >= 0x6f)
		/* Family 15h or later */
		fam15h = 1;

	if ((model >= 0x8) || fam15h)
		/* Revision D or later */
		rev_gte_d = 1;

	return rev_gte_d;
}

/***************************************************************************//**
 *
 * SBDFO
 * makeLinkBase(u8 currentNode, u8 currentLink)
 *
 *  Description:
 *	Private to northbridge implementation. Return the HT Host capability base
 *	PCI config address for a link.
 *
 *  Parameters:
 *	@param[in]  node    = the node this link is on
 *	@param[in]  link    = the link
 *
 *****************************************************************************/
static SBDFO makeLinkBase(u8 node, u8 link)
{
	SBDFO linkBase;

	/* With rev F can not be called with a 4th link or with the sublinks */
	if (link < 4)
		linkBase = MAKE_SBDFO(makePCISegmentFromNode(node),
				makePCIBusFromNode(node),
				makePCIDeviceFromNode(node),
				CPU_HTNB_FUNC_00,
				REG_HT_CAP_BASE_0X80 + link*HT_HOST_CAP_SIZE);
	else
		linkBase = MAKE_SBDFO(makePCISegmentFromNode(node),
				makePCIBusFromNode(node),
				makePCIDeviceFromNode(node),
				CPU_HTNB_FUNC_04,
				REG_HT_CAP_BASE_0X80 + (link-4)*HT_HOST_CAP_SIZE);
	return linkBase;
}

/***************************************************************************//**
 *
 * void
 * setHtControlRegisterBits(SBDFO reg, u8 hiBit, u8 loBit, u32 *pValue)
 *
 *  Description:
 *	Private to northbridge implementation. Provide a common routine for accessing the
 *	HT Link Control registers (84, a4, c4, e4), to enforce not clearing the
 *	HT CRC error bits.  Replaces direct use of AmdPCIWriteBits().
 *	NOTE: This routine is called for IO Devices as well as CPUs!
 *
 *  Parameters:
 *	@param[in]  reg    = the PCI config address the control register
 *	@param[in]  hiBit  = the high bit number
 *	@param[in]  loBit  = the low bit number
 *	@param[in]  pValue = the value to write to that bit range. Bit 0 => loBit.
 *
 *****************************************************************************/
static void setHtControlRegisterBits(SBDFO reg, u8 hiBit, u8 loBit, u32 *pValue)
{
	u32 temp, mask;

	ASSERT((hiBit < 32) && (loBit < 32) && (hiBit >= loBit) && ((reg & 0x3) == 0));
	ASSERT((hiBit < 8) || (loBit > 9));

	/* A 1<<32 == 1<<0 due to x86 SHL instruction, so skip if that is the case */
	if ((hiBit-loBit) != 31)
		mask = (((u32)1 << (hiBit-loBit+1))-1);
	else
		mask = (u32)0xFFFFFFFF;

	AmdPCIRead(reg, &temp);
	temp &= ~(mask << loBit);
	temp |= (*pValue & mask) << loBit;
	temp &= (u32)HT_CONTROL_CLEAR_CRC;
	AmdPCIWrite(reg, &temp);
}

/***************************************************************************//**
 *
 * static void
 * writeRoutingTable(u8 node, u8 target, u8 Link, cNorthBridge *nb)
 *
 *  Description:
 *	 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.
 *
 *  Parameters:
 *	@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
 *
 *****************************************************************************/

static void writeRoutingTable(u8 node, u8 target, u8 link, cNorthBridge *nb)
{
#ifndef HT_BUILD_NC_ONLY
	u32 temp = (nb->selfRouteResponseMask | nb->selfRouteRequestMask) << (link + 1);
	ASSERT((node < nb->maxNodes) && (target < nb->maxNodes) && (link < nb->maxLinks));
	AmdPCIWrite(MAKE_SBDFO(makePCISegmentFromNode(node),
			makePCIBusFromNode(node),
			makePCIDeviceFromNode(node),
			CPU_HTNB_FUNC_00,
			REG_ROUTE0_0X40 + target*4),
			&temp);
#else
	STOP_HERE;
#endif
}

/***************************************************************************//**
 *
 * static void
 * writeNodeID(u8 node, u8 nodeID, cNorthBridge *nb)
 *
 *  Description:
 *	Modifies the NodeID register on the target node
 *
 *  Parameters:
 *	@param[in] node    = the node that will have its NodeID altered.
 *	@param[in] nodeID  = the new value for NodeID
 *	@param[in] *nb     = this northbridge
 *
 *****************************************************************************/

static void writeNodeID(u8 node, u8 nodeID, cNorthBridge *nb)
{
	u32 temp;
	ASSERT((node < nb->maxNodes) && (nodeID < nb->maxNodes));
	if (is_fam15h()) {
		temp = 1;
		AmdPCIWriteBits(MAKE_SBDFO(makePCISegmentFromNode(node),
					makePCIBusFromNode(node),
					makePCIDeviceFromNode(node),
					CPU_NB_FUNC_03,
					REG_NORTHBRIDGE_CFG_3X8C),
					22, 22, &temp);
	}
	temp = nodeID;
	AmdPCIWriteBits(MAKE_SBDFO(makePCISegmentFromNode(node),
				makePCIBusFromNode(node),
				makePCIDeviceFromNode(node),
				CPU_HTNB_FUNC_00,
				REG_NODE_ID_0X60),
				2, 0, &temp);
}

/***************************************************************************//**
 *
 * static void
 * readDefLnk(u8 node, cNorthBridge *nb)
 *
 *  Description:
 *	 Read the DefLnk (the source link of the current packet)
 *	 from node
 *
 *  Parameters:
 *	@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.  Since this
 *				code is running on the BSP, this should be the link
 *				pointing back towards the BSP.
 *
 *****************************************************************************/

static u8 readDefLnk(u8 node, cNorthBridge *nb)
{
	u32 deflink = 0;
	SBDFO licr;
	u32 temp;

	licr = MAKE_SBDFO(makePCISegmentFromNode(node),
			makePCIBusFromNode(node),
			makePCIDeviceFromNode(node),
			CPU_HTNB_FUNC_00,
			REG_LINK_INIT_CONTROL_0X6C);

	ASSERT((node < nb->maxNodes));
	AmdPCIReadBits(licr, 3, 2, &deflink);
	AmdPCIReadBits(licr, 8, 8, &temp);	/* on rev F, this bit is reserved == 0 */
	deflink |= temp << 2;
	return (u8)deflink;
}

/***************************************************************************//**
 *
 * static void
 * enableRoutingTables(u8 node, cNorthBridge *nb)
 *
 *  Description:
 *	Turns routing tables on for a given node
 *
 *  Parameters:
 *	@param[in]  node = the node that will have it's routing tables enabled
 *	@param[in]  *nb  = this northbridge
 *
 *****************************************************************************/

static void enableRoutingTables(u8 node, cNorthBridge *nb)
{
	u32 temp = 0;
	ASSERT((node < nb->maxNodes));
	AmdPCIWriteBits(MAKE_SBDFO(makePCISegmentFromNode(node),
				makePCIBusFromNode(node),
				makePCIDeviceFromNode(node),
				CPU_HTNB_FUNC_00,
				REG_LINK_INIT_CONTROL_0X6C),
				0, 0, &temp);
}


/***************************************************************************//**
 *
 * static BOOL
 * verifyLinkIsCoherent(u8 node, u8 Link, cNorthBridge *nbk)
 *
 *  Description:
 *	Verify that the link is coherent, connected, and ready
 *
 *  Parameters:
 *	@param[in]   node      = the node that will be examined
 *	@param[in]   link      = the link on that Node to examine
 *	@param[in]   *nb       = this northbridge
 *	@return            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
 *				  false- The link has some other status
 *
 *****************************************************************************/

static BOOL verifyLinkIsCoherent(u8 node, u8 link, cNorthBridge *nb)
{
#ifndef HT_BUILD_NC_ONLY

	u32 linkType;
	SBDFO linkBase;

	ASSERT((node < nb->maxNodes) && (link < nb->maxLinks));

	linkBase = makeLinkBase(node, link);

	/*  FN0_98/A4/C4 = LDT Type Register */
	AmdPCIRead(linkBase + HTHOST_LINK_TYPE_REG, &linkType);

	/*  Verify LinkCon = 1, InitComplete = 1, NC = 0, UniP-cLDT = 0, LinkConPend = 0 */
	return (linkType & HTHOST_TYPE_MASK) ==  HTHOST_TYPE_COHERENT;
#else
	return 0;
#endif /* HT_BUILD_NC_ONLY */
}

/***************************************************************************//**
 *
 * static bool
 * readTrueLinkFailStatus(u8 node, u8 link, sMainData *pDat, cNorthBridge *nb)
 *
 *  Description:
 *	Return the LinkFailed status AFTER an attempt is made to clear the bit.
 *	Also, call event notify if a Hardware Fault caused a synch flood on a previous boot.
 *
 *	The table below summarizes correct responses of this routine.
 *	Family	  before    after    unconnected    Notify?    return
 *	  0F         0       0          0             No         0
 *	  0F         1       0          0             Yes        0
 *	  0F         1       1          X             No         1
 *	  10         0       0          0             No         0
 *	  10         1       0          0             Yes        0
 *	  10         1       0          3             No         1
 *
 *  Parameters:
 *	@param[in]    node      = the node that will be examined
 *	@param[in]    link      = the link on that node to examine
 *	@param[in]    *pDat = access to call back routine
 *	@param[in]    *nb       = this northbridge
 *	@return                   true - the link is not connected or has hard error
 *	                          false- if the link is connected
 *
 *****************************************************************************/

static BOOL readTrueLinkFailStatus(u8 node, u8 link, sMainData *pDat, cNorthBridge *nb)
{
	u32 before, after, unconnected, crc;
	SBDFO linkBase;

	ASSERT((node < nb->maxNodes) && (link < nb->maxLinks));

	linkBase = makeLinkBase(node, link);

	/* Save the CRC status before doing anything else.
	 * Read, Clear, the Re-read the error bits in the Link Control Register
	 * FN0_84/A4/C4[4] = LinkFail bit
	 * and the connection status, TransOff and EndOfChain
	 */
	AmdPCIReadBits(linkBase + HTHOST_LINK_CONTROL_REG, 9, 8, &crc);
	AmdPCIReadBits(linkBase + HTHOST_LINK_CONTROL_REG, 4, 4, &before);
	setHtControlRegisterBits(linkBase + HTHOST_LINK_CONTROL_REG, 4, 4, &before);
	AmdPCIReadBits(linkBase + HTHOST_LINK_CONTROL_REG, 4, 4, &after);
	AmdPCIReadBits(linkBase + HTHOST_LINK_CONTROL_REG, 7, 6, &unconnected);

	if (before != after)
	{
		if (!unconnected)
		{
			if (crc != 0)
			{
				/* A synch flood occurred due to HT CRC */
				if (pDat->HtBlock->AMD_CB_EventNotify)
				{
					/* Pass the node and link on which the generic synch flood event occurred. */
					sHtEventHWHtCrc evt;
					evt.eSize = sizeof(sHtEventHWHtCrc);
					evt.node = node;
					evt.link = link;
					evt.laneMask = (uint8)crc;

					pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_HW_FAULT,
									HT_EVENT_HW_HTCRC,
									(u8 *)&evt);
				}
			}
			else
			{
				/* Some synch flood occurred */
				if (pDat->HtBlock->AMD_CB_EventNotify)
				{
					/* Pass the node and link on which the generic synch flood event occurred. */
					sHtEventHWSynchFlood evt;
					evt.eSize = sizeof(sHtEventHWSynchFlood);
					evt.node = node;
					evt.link = link;

					pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_HW_FAULT,
									HT_EVENT_HW_SYNCHFLOOD,
									(u8 *)&evt);
				}
			}
		}
	}
	return ((after != 0) || unconnected);
}


/***************************************************************************//**
 *
 * static u8
 * readToken(u8 node, cNorthBridge *nb)
 *
 *  Description:
 *	Read the token stored in the scratchpad register
 *	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.
 *
 *  Parameters:
 *	@param[in]  node      = the node that will be examined
 *	@param[in]  *nb       = this northbridge
 *	@return                the Token read from the node
 *
 *****************************************************************************/
static u8 readToken(u8 node, cNorthBridge *nb)
{
	u32 temp;

	ASSERT((node < nb->maxNodes));
	/* Use CpuCnt as a scratch register */
	/* Limiting use to 4 bits makes code GH to rev F compatible. */
	AmdPCIReadBits(MAKE_SBDFO(makePCISegmentFromNode(node),
				makePCIBusFromNode(node),
				makePCIDeviceFromNode(node),
				CPU_HTNB_FUNC_00,
				REG_NODE_ID_0X60),
				19, 16, &temp);

	return (u8)temp;
}


/***************************************************************************//**
 *
 * static void
 * writeToken(u8 node, u8 Value, cNorthBridge *nb)
 *
 *  Description:
 *	Write the token stored in the scratchpad register
 *	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.
 *	Limiting use to 4 bits makes code GH to rev F compatible.
 *
 *  Parameters:
 *	@param[in]  node  = the node that will be examined
 *	@param      value
 *	@param[in] *nb  = this northbridge
 *
 *****************************************************************************/
static void writeToken(u8 node, u8 value, cNorthBridge *nb)
{
	u32 temp = value;
	ASSERT((node < nb->maxNodes));
	/* Use CpuCnt as a scratch register */
	/* Limiting use to 4 bits makes code GH to rev F compatible. */
	AmdPCIWriteBits(MAKE_SBDFO(makePCISegmentFromNode(node),
					makePCIBusFromNode(node),
					makePCIDeviceFromNode(node),
					CPU_HTNB_FUNC_00,
					REG_NODE_ID_0X60),
					19, 16, &temp);
}

/***************************************************************************//**
 *
 * static u8
 * fam0FGetNumCoresOnNode(u8 node, cNorthBridge *nb)
 *
 *  Description:
 *	Return the number of cores (1 based count) on node.
 *
 *  Parameters:
 *	@param[in]  node      = the node that will be examined
 *	@param[in] *nb = this northbridge
 *	@return    = the number of cores
 *
 * ---------------------------------------------------------------------------------------
 */
static u8 fam0FGetNumCoresOnNode(u8 node, cNorthBridge *nb)
{
	u32 temp;

	ASSERT((node < nb->maxNodes));
	/* Read CmpCap */
	AmdPCIReadBits(MAKE_SBDFO(makePCISegmentFromNode(node),
			makePCIBusFromNode(node),
			makePCIDeviceFromNode(node),
			CPU_NB_FUNC_03,
			REG_NB_CAPABILITY_3XE8),
			13, 12, &temp);

	/* and add one */
	return (u8)(temp+1);
}

/***************************************************************************//**
 *
 * static u8
 * fam10GetNumCoresOnNode(u8 node, cNorthBridge *nb)
 *
 *  Description:
 *	Return the number of cores (1 based count) on node.
 *
 *  Parameters:
 *	@param[in]  node      = the node that will be examined
 *	@param[in] *nb = this northbridge
 *	@return    = the number of cores
 *
 *
 */
static u8 fam10GetNumCoresOnNode(u8 node, cNorthBridge *nb)
{
	u32 temp, leveling, cores;
	u8 i;

	ASSERT((node < nb->maxNodes));
	/* Read CmpCap [2][1:0] */
	AmdPCIReadBits(MAKE_SBDFO(makePCISegmentFromNode(node),
				makePCIBusFromNode(node),
				makePCIDeviceFromNode(node),
				CPU_NB_FUNC_03,
				REG_NB_CAPABILITY_3XE8),
				15, 12, &temp);

	/* bits[15,13,12] specify the cores */
	temp = ((temp & 8) >> 1) + (temp & 3);
	cores = temp + 1;

	/* Support Downcoring */
	AmdPCIReadBits (MAKE_SBDFO(makePCISegmentFromNode(node),
					makePCIBusFromNode(node),
					makePCIDeviceFromNode(node),
					CPU_NB_FUNC_03,
					REG_NB_DOWNCORE_3X190),
					3, 0, &leveling);
	for (i = 0; i < cores; i++)
	{
		if (leveling & ((u32) 1 << i))
		{
			temp--;
		}
	}
	return (u8)(temp+1);
}

/***************************************************************************//**
 *
 * static u8
 * fam15GetNumCoresOnNode(u8 node, cNorthBridge *nb)
 *
 *  Description:
 *	Return the number of cores (1 based count) on node.
 *
 *  Parameters:
 *	@param[in]  node      = the node that will be examined
 *	@param[in] *nb = this northbridge
 *	@return    = the number of cores
 *
 *
 */
static u8 fam15GetNumCoresOnNode(u8 node, cNorthBridge *nb)
{
	u32 temp, leveling, cores;
	u8 i;

	ASSERT((node < nb->maxNodes));
	/* Read CmpCap [7:0] */
	AmdPCIReadBits(MAKE_SBDFO(makePCISegmentFromNode(node),
				makePCIBusFromNode(node),
				makePCIDeviceFromNode(node),
				CPU_NB_FUNC_05,
				REG_NB_CAPABILITY_5X84),
				7, 0, &temp);

	/* bits[7:0] specify the cores */
	temp = temp & 0xff;
	cores = temp + 1;

	/* Support Downcoring */
	AmdPCIReadBits (MAKE_SBDFO(makePCISegmentFromNode(node),
					makePCIBusFromNode(node),
					makePCIDeviceFromNode(node),
					CPU_NB_FUNC_03,
					REG_NB_DOWNCORE_3X190),
					31, 0, &leveling);
	for (i = 0; i < cores; i++)
	{
		if (leveling & ((u32) 1 << i))
		{
			temp--;
		}
	}
	return (u8)(temp+1);
}

/***************************************************************************//**
 *
 * static void
 * setTotalNodesAndCores(u8 node, u8 totalNodes, u8 totalCores, cNorthBridge *nb)
 *
 *  Description:
 *	Write the total number of cores and nodes to the node
 *
 *  Parameters:
 *	@param[in]  node   = the node that will be examined
 *	@param[in]  totalNodes  = the total number of nodes
 *	@param[in]  totalCores  = the total number of cores
 *	@param[in] *nb   = this northbridge
 *
 * ---------------------------------------------------------------------------------------
 */
static void setTotalNodesAndCores(u8 node, u8 totalNodes, u8 totalCores, cNorthBridge *nb)
{
	SBDFO nodeIDReg;
	u32 temp;

	ASSERT((node < nb->maxNodes) && (totalNodes <= nb->maxNodes));
	nodeIDReg = MAKE_SBDFO(makePCISegmentFromNode(node),
				makePCIBusFromNode(node),
				makePCIDeviceFromNode(node),
				CPU_HTNB_FUNC_00,
				REG_NODE_ID_0X60);

	temp = totalCores-1;
	/* Rely on max number of nodes:cores for rev F and GH to make
	 * this code work, even though we write reserved bit 20 on rev F it will be
	 * zero in that case.
	 */
	AmdPCIWriteBits(nodeIDReg, 20, 16, &temp);
	temp = totalNodes-1;
	AmdPCIWriteBits(nodeIDReg, 6,  4, &temp);
}

/***************************************************************************//**
 *
 * static void
 * limitNodes(u8 node, cNorthBridge *nb)
 *
 *  Description:
 *	Limit coherent config accesses to cpus as indicated by nodecnt.
 *
 *  Parameters:
 *	@param[in]  node  = the node that will be examined
 *	@param[in] *nb  = this northbridge
 *
 * ---------------------------------------------------------------------------------------
 */
static void limitNodes(u8 node, cNorthBridge *nb)
{
	u32 temp = 1;
	ASSERT((node < nb->maxNodes));
	AmdPCIWriteBits(MAKE_SBDFO(makePCISegmentFromNode(node),
				makePCIBusFromNode(node),
				makePCIDeviceFromNode(node),
				CPU_HTNB_FUNC_00,
				REG_LINK_TRANS_CONTROL_0X68),
				15, 15, &temp);
}

/***************************************************************************//**
 *
 * static void
 * writeFullRoutingTable(u8 node, u8 target, u8 reqLink, u8 rspLink, u32 BClinks, cNorthBridge *nb)
 *
 *  Description:
 *	Write the routing table entry for node to target, using the request link, response
 *	link, and broadcast links provided.
 *
 *  Parameters:
 *	@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]  bClinks  = the broadcast links
 *	@param[in] *nb  = this northbridge
 *
 * ---------------------------------------------------------------------------------------
 */
static void writeFullRoutingTable(u8 node, u8 target, u8 reqLink, u8 rspLink, u32 bClinks, cNorthBridge *nb)
{
#ifndef HT_BUILD_NC_ONLY
	u32 value = 0;

	ASSERT((node < nb->maxNodes) && (target < nb->maxNodes));
	if (reqLink == ROUTETOSELF)
		value |= nb->selfRouteRequestMask;
	else
		value |= nb->selfRouteRequestMask << (reqLink+1);

	if (rspLink == ROUTETOSELF)
		value |= nb->selfRouteResponseMask;
	else
		value |= nb->selfRouteResponseMask << (rspLink+1);

	/* Allow us to accept a Broadcast ourselves, then set broadcasts for routes */
	value |= (u32)1 << nb->broadcastSelfBit;
	value |= (u32)bClinks << (nb->broadcastSelfBit + 1);

	AmdPCIWrite(MAKE_SBDFO(makePCISegmentFromNode(node),
				makePCIBusFromNode(node),
				makePCIDeviceFromNode(node),
				CPU_HTNB_FUNC_00,
				REG_ROUTE0_0X40 + target*4), &value);
#else
	STOP_HERE;
#endif /* HT_BUILD_NC_ONLY */
}

/***************************************************************************//**
 *
 * static u32
 * makeKey(u8 currentNode)
 *
 *  Description:
 *	Private routine to northbridge code.
 *	Determine whether a node is compatible with the discovered configuration so
 *	far.  Currently, that means the family, extended family of the new node are the
 *	same as the BSP's.
 *
 *  Parameters:
 *	@param[in]   node   = the node
 *	@return = the key value
 *
 * ---------------------------------------------------------------------------------------
 */
static u32 makeKey(u8 node)
{
	u32 extFam, baseFam;
	AmdPCIReadBits(MAKE_SBDFO(makePCISegmentFromNode(node),
				makePCIBusFromNode(node),
				makePCIDeviceFromNode(node),
				CPU_NB_FUNC_03,
				REG_NB_CPUID_3XFC),
				27, 20, &extFam);
	AmdPCIReadBits(MAKE_SBDFO(makePCISegmentFromNode(node),
				makePCIBusFromNode(node),
				makePCIDeviceFromNode(node),
				CPU_NB_FUNC_03,
				REG_NB_CPUID_3XFC),
				11, 8, &baseFam);
	return ((u32)(baseFam << 8) | extFam);
}


/***************************************************************************//**
 *
 * static BOOL
 * isCompatible(u8 currentNode, cNorthBridge *nb)
 *
 *  Description:
 *	Determine whether a node is compatible with the discovered configuration so
 *	far.  Currently, that means the family, extended family of the new node are the
 *	same as the BSP's.
 *
 *  Parameters:
 *	@param[in]  node   = the node
 *	@param[in] *nb  = this northbridge
 *	@return = true: the new is compatible, false: it is not
 *
 * ---------------------------------------------------------------------------------------
 */
static BOOL isCompatible(u8 node, cNorthBridge *nb)
{
	return (makeKey(node) == nb->compatibleKey);
}

/***************************************************************************//**
 *
 * static BOOL
 * fam0fIsCapable(u8 node, sMainData *pDat, cNorthBridge *nb)
 *
 *  Description:
 *	Get node capability and update the minimum supported system capability.
 *	Return whether the current configuration exceeds the capability.
 *
 *  Parameters:
 *	@param[in]       node = the node
 *	@param[in,out]  *pDat = sysMpCap (updated) and NodesDiscovered
 *	@param[in]        *nb = this northbridge
 *	@return               true:  system is capable of current config.
 *			      false: system is not capable of current config.
 *
 * ---------------------------------------------------------------------------------------
 */
static BOOL fam0fIsCapable(u8 node, sMainData *pDat, cNorthBridge *nb)
{
#ifndef HT_BUILD_NC_ONLY
	u32 temp;
	u8 maxNodes;

	ASSERT(node < nb->maxNodes);

	AmdPCIReadBits(MAKE_SBDFO(makePCISegmentFromNode(node),
				makePCIBusFromNode(node),
				makePCIDeviceFromNode(node),
				CPU_NB_FUNC_03,
				REG_NB_CAPABILITY_3XE8),
				2, 1, &temp);
	if (temp > 1)
	{
		maxNodes = 8;
	} else {
		if (temp == 1)
		{
			maxNodes = 2;
		} else {
			maxNodes = 1;
		}
	}
	if (pDat->sysMpCap > maxNodes)
	{
		 pDat->sysMpCap = maxNodes;
	}
	/* Note since sysMpCap is one based and NodesDiscovered is zero based, equal is false */
	return (pDat->sysMpCap > pDat->NodesDiscovered);
#else
	return 1;
#endif
}

/***************************************************************************//**
 *
 * static BOOL
 * fam10IsCapable(u8 node, sMainData *pDat, cNorthBridge *nb)
 *
 *  Description:
 *	Get node capability and update the minimum supported system capability.
 *	Return whether the current configuration exceeds the capability.
 *
 *  Parameters:
 *	@param[in]  node   = the node
 *	@param[in,out] *pDat = sysMpCap (updated) and NodesDiscovered
 *	@param[in] *nb   = this northbridge
 *	@return             true: system is capable of current config.
 *			   false: system is not capable of current config.
 *
 * ---------------------------------------------------------------------------------------
 */
static BOOL fam10IsCapable(u8 node, sMainData *pDat, cNorthBridge *nb)
{
#ifndef HT_BUILD_NC_ONLY
	u32 temp;
	u8 maxNodes;

	ASSERT(node < nb->maxNodes);

	AmdPCIReadBits(MAKE_SBDFO(makePCISegmentFromNode(node),
				makePCIBusFromNode(node),
				makePCIDeviceFromNode(node),
				CPU_NB_FUNC_03,
				REG_NB_CAPABILITY_3XE8),
				18, 16, &temp);

	if (temp != 0)
	{
		maxNodes = (1 << (~temp & 0x3));  /* That is, 1, 2, 4, or 8 */
	}
	else
	{
		maxNodes = 8;
	}

	if (pDat->sysMpCap > maxNodes)
	{
		pDat->sysMpCap = maxNodes;
	}
	/* Note since sysMpCap is one based and NodesDiscovered is zero based, equal is false */
	return (pDat->sysMpCap > pDat->NodesDiscovered);
#else
	return 1;
#endif
}

/***************************************************************************//**
 *
 * static BOOL
 * fam15IsCapable(u8 node, sMainData *pDat, cNorthBridge *nb)
 *
 *  Description:
 *	Get node capability and update the minimum supported system capability.
 *	Return whether the current configuration exceeds the capability.
 *
 *  Parameters:
 *	@param[in]  node   = the node
 *	@param[in,out] *pDat = sysMpCap (updated) and NodesDiscovered
 *	@param[in] *nb   = this northbridge
 *	@return             true: system is capable of current config.
 *			   false: system is not capable of current config.
 *
 * ---------------------------------------------------------------------------------------
 */
static BOOL fam15IsCapable(u8 node, sMainData *pDat, cNorthBridge *nb)
{
#ifndef HT_BUILD_NC_ONLY
	u32 temp;
	u8 maxNodes;

	ASSERT(node < nb->maxNodes);

	AmdPCIReadBits(MAKE_SBDFO(makePCISegmentFromNode(node),
				makePCIBusFromNode(node),
				makePCIDeviceFromNode(node),
				CPU_NB_FUNC_03,
				REG_NB_CAPABILITY_3XE8),
				18, 16, &temp);

	if (temp != 0)
	{
		maxNodes = (1 << (~temp & 0x3));  /* That is, 1, 2, 4, or 8 */
	}
	else
	{
		/* Check if CPU package is dual node */
		AmdPCIReadBits(MAKE_SBDFO(makePCISegmentFromNode(node),
					makePCIBusFromNode(node),
					makePCIDeviceFromNode(node),
					CPU_NB_FUNC_03,
					REG_NB_CAPABILITY_3XE8),
					29, 29, &temp);
		if (temp)
			maxNodes = 4;
		else
			maxNodes = 8;
	}

	if (pDat->sysMpCap > maxNodes)
	{
		pDat->sysMpCap = maxNodes;
	}
	/* Note since sysMpCap is one based and NodesDiscovered is zero based, equal is false */
	return (pDat->sysMpCap > pDat->NodesDiscovered);
#else
	return 1;
#endif
}

/***************************************************************************//**
 *
 * static void
 * fam0fStopLink(u8 currentNode, u8 currentLink, cNorthBridge *nb)
 *
 *  Description:
 *	Disable a cHT link on node by setting F0x[E4, C4, A4, 84][TransOff, EndOfChain]=1
 *
 *  Parameters:
 *	@param[in]  node      = the node this link is on
 *	@param[in]  link      = the link to stop
 *	@param[in] *nb = this northbridge
 *
 * ---------------------------------------------------------------------------------------
 */
static void fam0fStopLink(u8 node, u8 link, cNorthBridge *nb)
{
#ifndef HT_BUILD_NC_ONLY
	u32 temp;
	SBDFO linkBase;

	ASSERT((node < nb->maxNodes) && (link < nb->maxLinks));

	linkBase = makeLinkBase(node, link);

	/* Set TransOff, EndOfChain */
	temp = 3;
	setHtControlRegisterBits(linkBase + HTHOST_LINK_CONTROL_REG, 7, 6, &temp);
#endif
}

/***************************************************************************//**
 *
 * static void
 * commonVoid()
 *
 *  Description:
 *	Nothing.
 *
 *  Parameters:
 *		None.
 *
 * ---------------------------------------------------------------------------------------
 */
static void commonVoid(void)
{
}

/***************************************************************************//**
 *
 * static BOOL
 * commonReturnFalse()
 *
 *  Description:
 *	Return False.
 *
 *  Parameters:
 *	     @return	   = false
 *
 */
static BOOL commonReturnFalse(void)
{
	return 0;
}

/***************************************************************************
 ***			Non-coherent init code				  ***
 ***			Northbridge access routines			  ***
 ***************************************************************************/

/***************************************************************************//**
 *
 * static u8
 * readSbLink(cNorthBridge *nb)
 *
 *  Description:
 *	 Return the link to the Southbridge
 *
 *  Parameters:
 *	@param[in] *nb = this northbridge
 *	@return          the link to the southbridge
 *
 * ---------------------------------------------------------------------------------------
 */
static u8 readSbLink(cNorthBridge *nb)
{
	u32 temp;
	AmdPCIReadBits(MAKE_SBDFO(makePCISegmentFromNode(0),
				makePCIBusFromNode(0),
				makePCIDeviceFromNode(0),
				CPU_HTNB_FUNC_00,
				REG_UNIT_ID_0X64),
				10, 8, &temp);
	return (u8)temp;
}

/***************************************************************************//**
 *
 * static BOOL
 * verifyLinkIsNonCoherent(u8 node, u8 link, cNorthBridge *nb)
 *
 *  Description:
 *	 Verify that the link is non-coherent, connected, and ready
 *
 *  Parameters:
 *	@param[in]  node   = the node that will be examined
 *	@param[in]  link   = the Link on that node to examine
 *	@param[in] *nb = this northbridge
 *	@return   = true - The link has the following status
 *					LinkCon = 1,     Link is connected
 *					InitComplete = 1,Link initilization is complete
 *					NC = 1,          Link is coherent
 *					UniP-cLDT = 0,   Link is not Uniprocessor cLDT
 *					LinkConPend = 0  Link connection is not pending
 *					false- The link has some other status
 *
 * ---------------------------------------------------------------------------------------
 */
static BOOL verifyLinkIsNonCoherent(u8 node, u8 link, cNorthBridge *nb)
{
	u32 linkType;
	SBDFO linkBase;

	ASSERT((node < nb->maxNodes) && (link < nb->maxLinks));

	linkBase = makeLinkBase(node, link);

	/* FN0_98/A4/C4 = LDT Type Register */
	AmdPCIRead(linkBase + HTHOST_LINK_TYPE_REG, &linkType);

	/* Verify linkCon = 1, InitComplete = 1, NC = 0, UniP-cLDT = 0, LinkConPend = 0 */
	return (linkType & HTHOST_TYPE_MASK) ==  HTHOST_TYPE_NONCOHERENT;
}

/***************************************************************************//**
 *
 * static void
 * ht3SetCFGAddrMap(u8 cfgMapIndex, u8 secBus, u8 subBus, u8 targetNode, u8 targetLink, sMainData *pDat, cNorthBridge *nb)
 *
 *  Description:
 *	 Configure and enable config access to a non-coherent chain for the given bus range.
 *
 *  Parameters:
 *	@param[in] cfgMapIndex = the map entry to set
 *	@param[in] secBus      = The secondary bus number to use
 *	@param[in] subBus      = The subordinate bus number to use
 *	@param[in] targetNode  = The node  that shall be the recipient of the traffic
 *	@param[in] targetLink  = The link that shall be the recipient of the traffic
 *	@param[in] pDat   = our global state
 *	@param[in] *nb  = this northbridge
 *
 * ---------------------------------------------------------------------------------------
 */
static void  ht3SetCFGAddrMap(u8 cfgMapIndex, u8 secBus, u8 subBus, u8 targetNode, u8 targetLink, sMainData *pDat, cNorthBridge *nb)
{
	u8 curNode;
	SBDFO linkBase;
	u32 temp;

	linkBase = makeLinkBase(targetNode, targetLink);

	ASSERT(secBus <= subBus);
	temp = secBus;
	AmdPCIWriteBits(linkBase + HTHOST_ISOC_REG, 15, 8, &temp);

	/* For target link, note that rev F uses bits 9:8 and only with GH is bit 10
	 * set to indicate a sublink.  For node, we are currently not supporting Extended
	 * routing tables.
	 */
	temp = ((u32)subBus << 24) + ((u32)secBus << 16) + ((u32)targetLink << 8)
		+ ((u32)targetNode << 4) + (u32)3;
	for (curNode = 0; curNode < pDat->NodesDiscovered+1; curNode++)
		AmdPCIWrite(MAKE_SBDFO(makePCISegmentFromNode(curNode),
					makePCIBusFromNode(curNode),
					makePCIDeviceFromNode(curNode),
					CPU_ADDR_FUNC_01,
					REG_ADDR_CONFIG_MAP0_1XE0 + 4*cfgMapIndex),
					&temp);
}

/***************************************************************************//**
 *
 * static void
 * ht1SetCFGAddrMap(u8 cfgMapIndex, u8 secBus, u8 subBus, u8 targetNode, u8 targetLink, sMainData *pDat, cNorthBridge *nb)
 *
 *  Description:
 *	 Configure and enable config access to a non-coherent chain for the given bus range.
 *
 *  Parameters:
 *	@param[in]  cfgMapIndex = the map entry to set
 *	@param[in]  secBus      = The secondary bus number to use
 *	@param[in]  subBus      = The subordinate bus number to use
 *	@param[in]  targetNode  = The node  that shall be the recipient of the traffic
 *	@param[in]  targetLink  = The link that shall be the recipient of the traffic
 *	@param[in] pDat   = our global state
 *	@param[in] *nb   = this northbridge
 *
 ******************************************************************************/
static void ht1SetCFGAddrMap(u8 cfgMapIndex, u8 secBus, u8 subBus, u8 targetNode, u8 targetLink, sMainData *pDat, cNorthBridge *nb)
{
	u8 curNode;
	SBDFO linkBase;
	u32 temp;

	linkBase = makeLinkBase(targetNode, targetLink);

	ASSERT(secBus <= subBus);
	temp = secBus;
	AmdPCIWriteBits(linkBase + HTHOST_ISOC_REG, 15, 8, &temp);

	temp = subBus;
	AmdPCIWriteBits(linkBase + HTHOST_ISOC_REG, 23, 16, &temp);

	/* For target link, note that rev F uses bits 9:8 and only with GH is bit 10
	 * set to indicate a sublink.  For node, we are currently not supporting Extended
	 * routing tables.
	 */
	temp = ((u32)subBus << 24) + ((u32)secBus << 16) + ((u32)targetLink << 8)
		+ ((u32)targetNode << 4) + (u32)3;
	for (curNode = 0; curNode < pDat->NodesDiscovered+1; curNode++)
		 AmdPCIWrite(MAKE_SBDFO(makePCISegmentFromNode(curNode),
					makePCIBusFromNode(curNode),
					makePCIDeviceFromNode(curNode),
					CPU_ADDR_FUNC_01,
					REG_ADDR_CONFIG_MAP0_1XE0 + 4*cfgMapIndex),
					&temp);
}

/***************************************************************************
 ***				 Link Optimization			  ***
 ***************************************************************************/

/**
 * static u8
 * convertBitsToWidth(u8 value, cNorthBridge *nb)
 *
 *  Description:
 *	 Given the bits set in the register field, return the width it represents
 *
 *  Parameters:
 *	@param[in]  value   = The bits for the register
 *	@param[in] *nb = this northbridge
 *	@return  The width
 *
 ******************************************************************************/
static u8 convertBitsToWidth(u8 value, cNorthBridge *nb)
{
	switch(value) {
	case 1: return 16;
	case 0: return 8;
	case 5: return 4;
	case 4: return 2;
	default: STOP_HERE; /*  This is an error internal condition */
	}
	return 0; // shut up GCC.
}

/***************************************************************************//**
 *
 * static u8
 * convertWidthToBits(u8 value, cNorthBridge *nb)
 *
 *  Description:
 *	Translate a desired width setting to the bits to set in the register field
 *
 *  Parameters:
 *	@param[in]  value     = The width
 *	@param[in] *nb = this northbridge
 *	@return The bits for the register
 *
 ******************************************************************************/
static u8 convertWidthToBits(u8 value, cNorthBridge *nb)
{
	switch (value) {
	case 16: return 1;
	case  8: return 0;
	case  4: return 5;
	case  2: return 4;
	default: STOP_HERE; /*  This is an internal error condition */
	}
	return 0; // shut up GCC
}

/***************************************************************************//**
 *
 * static u16
 * ht1NorthBridgeFreqMask(u8 NodeID, cNorthBridge *nb)
 *
 *  Description:
 *	Return a mask that eliminates HT frequencies that cannot be used due to a slow
 *	northbridge frequency.
 *
 *  Parameters:
 *	@param[in]  node      = Result could (later) be for a specific node
 *	@param[in] *nb = this northbridge
 *	@return  Frequency mask
 *
 ******************************************************************************/
static uint32_t ht1NorthBridgeFreqMask(u8 node, cNorthBridge *nb)
{
	/* only up to HT1 speeds */
	return (HT_FREQUENCY_LIMIT_HT1_ONLY);
}

/***************************************************************************//**
 *
 * static u16
 * fam10NorthBridgeFreqMask(u8 NodeID, cNorthBridge *nb)
 *
 *  Description:
 *	Return a mask that eliminates HT frequencies that cannot be used due to a slow
 *	northbridge frequency.
 *
 *  Parameters:
 *	@param[in]  node     = Result could (later) be for a specific node
 *	@param[in]  *nb      = this northbridge
 *	@return  = Frequency mask
 *
 ******************************************************************************/
static uint32_t fam10NorthBridgeFreqMask(u8 node, cNorthBridge *nb)
{
	u8 nbCOF;
	uint32_t supported;

	nbCOF = getMinNbCOF();
	/*
	 * nbCOF is minimum northbridge speed in hundreds of MHz.
	 * HT can not go faster than the minimum speed of the northbridge.
	 */
	if ((nbCOF >= 6) && (nbCOF < 10))
	{
		/* Generation 1 HT link frequency */
		/* Convert frequency to bit and all less significant bits,
		 * by setting next power of 2 and subtracting 1.
		 */
		supported = ((uint32_t)1 << ((nbCOF >> 1) + 2)) - 1;
	}
	else if ((nbCOF >= 10) && (nbCOF <= 32))
	{
		/* Generation 3 HT link frequency
		 * Assume error retry is enabled on all Gen 3 links
		 */
		if (is_gt_rev_d()) {
			nbCOF *= 2;
			if (nbCOF > 32)
				nbCOF = 32;
		}

		/* Convert frequency to bit and all less significant bits,
		 * by setting next power of 2 and subtracting 1.
		 */
		supported = ((uint32_t)1 << ((nbCOF >> 1) + 2)) - 1;
	}
	else if (nbCOF > 32)
	{
		supported = HT_FREQUENCY_LIMIT_3200M;
	}
	/* unlikely cases, but include as a defensive measure, also avoid trick above */
	else if (nbCOF == 4)
	{
		supported = HT_FREQUENCY_LIMIT_400M;
	}
	else if (nbCOF == 2)
	{
		supported = HT_FREQUENCY_LIMIT_200M;
	}
	else
	{
		STOP_HERE;
		supported = HT_FREQUENCY_LIMIT_200M;
	}

	return (fixEarlySampleFreqCapability(supported));
}

/***************************************************************************//**
 *
 * static u16
 * fam15NorthBridgeFreqMask(u8 NodeID, cNorthBridge *nb)
 *
 *  Description:
 *	Return a mask that eliminates HT frequencies that cannot be used due to a slow
 *	northbridge frequency.
 *
 *  Parameters:
 *	@param[in]  node     = Result could (later) be for a specific node
 *	@param[in]  *nb      = this northbridge
 *	@return  = Frequency mask
 *
 ******************************************************************************/
static uint32_t fam15NorthBridgeFreqMask(u8 node, cNorthBridge *nb)
{
	u8 nbCOF;
	uint32_t supported;

	nbCOF = getMinNbCOF();
	/*
	 * nbCOF is minimum northbridge speed in hundreds of MHz.
	 * HT can not go faster than the minimum speed of the northbridge.
	 */
	if ((nbCOF >= 6) && (nbCOF < 10))
	{
		/* Generation 1 HT link frequency */
		/* Convert frequency to bit and all less significant bits,
		 * by setting next power of 2 and subtracting 1.
		 */
		supported = ((uint32_t)1 << ((nbCOF >> 1) + 2)) - 1;
	}
	else if ((nbCOF >= 10) && (nbCOF <= 32))
	{
		/* Generation 3 HT link frequency
		 * Assume error retry is enabled on all Gen 3 links
		 */
		nbCOF *= 2;
		if (nbCOF > 32)
			nbCOF = 32;

		/* Convert frequency to bit and all less significant bits,
		 * by setting next power of 2 and subtracting 1.
		 */
		supported = ((uint32_t)1 << ((nbCOF >> 1) + 2)) - 1;
	}
	else if (nbCOF > 32)
	{
		supported = HT_FREQUENCY_LIMIT_3200M;
	}
	/* unlikely cases, but include as a defensive measure, also avoid trick above */
	else if (nbCOF == 4)
	{
		supported = HT_FREQUENCY_LIMIT_400M;
	}
	else if (nbCOF == 2)
	{
		supported = HT_FREQUENCY_LIMIT_200M;
	}
	else
	{
		STOP_HERE;
		supported = HT_FREQUENCY_LIMIT_200M;
	}

	return (fixEarlySampleFreqCapability(supported));
}

/***************************************************************************//**
 *
 * static void
 * gatherLinkData(sMainData *pDat, cNorthBridge *nb)
 *
 *  Description:
 *	 For all discovered links, populate the port list with the frequency and width
 *	 capabilities.
 *
 *  Parameters:
 *	@param[in,out] pDat = our global state, port list
 *	@param[in]     *nb = this northbridge
 *
 ******************************************************************************/
static void gatherLinkData(sMainData *pDat, cNorthBridge *nb)
{
	u8 i;
	SBDFO linkBase;
	u32 temp;

	for (i = 0; i < pDat->TotalLinks*2; i++)
	{
		if (pDat->PortList[i].Type == PORTLIST_TYPE_CPU)
		{
			linkBase = makeLinkBase(pDat->PortList[i].NodeID, pDat->PortList[i].Link);

			pDat->PortList[i].Pointer = linkBase;

			AmdPCIReadBits(linkBase + HTHOST_LINK_CONTROL_REG, 22, 20, &temp);
			pDat->PortList[i].PrvWidthOutCap = convertBitsToWidth((u8)temp, pDat->nb);

			AmdPCIReadBits(linkBase + HTHOST_LINK_CONTROL_REG, 18, 16, &temp);
			pDat->PortList[i].PrvWidthInCap = convertBitsToWidth((u8)temp, pDat->nb);

			AmdPCIReadBits(linkBase + HTHOST_FREQ_REV_REG, 31, 16, &temp);
			pDat->PortList[i].PrvFrequencyCap = temp & 0x7FFF	/*  Mask off bit 15, reserved value */
				& nb->northBridgeFreqMask(pDat->PortList[i].NodeID, pDat->nb);
			if (is_gt_rev_d()) {
				AmdPCIReadBits(linkBase + HTHOST_FREQ_REV_REG_2, 15, 1, &temp);
				temp &= 0x7;	/* Mask off reserved values */
				pDat->PortList[i].PrvFrequencyCap |= (temp << 17);
			}

			AmdPCIReadBits(linkBase + HTHOST_FEATURE_CAP_REG, 9, 0, &temp);
			pDat->PortList[i].PrvFeatureCap = (u16)temp;
		}
		else
		{
			linkBase = pDat->PortList[i].Pointer;
			if (pDat->PortList[i].Link == 1)
				linkBase += HTSLAVE_LINK01_OFFSET;

			AmdPCIReadBits(linkBase + HTSLAVE_LINK_CONTROL_0_REG, 22, 20, &temp);
			pDat->PortList[i].PrvWidthOutCap = convertBitsToWidth((u8)temp, pDat->nb);

			AmdPCIReadBits(linkBase + HTSLAVE_LINK_CONTROL_0_REG, 18, 16, &temp);
			pDat->PortList[i].PrvWidthInCap = convertBitsToWidth((u8)temp, pDat->nb);

			AmdPCIReadBits(linkBase + HTSLAVE_FREQ_REV_0_REG, 31, 16, &temp);
			pDat->PortList[i].PrvFrequencyCap = (u16)temp;

			AmdPCIReadBits(linkBase + HTSLAVE_FEATURE_CAP_REG, 7, 0, &temp);
			pDat->PortList[i].PrvFeatureCap = (u16)temp;

			if (pDat->HtBlock->AMD_CB_DeviceCapOverride)
			{
				linkBase &= 0xFFFFF000;
				AmdPCIRead(linkBase, &temp);

				pDat->HtBlock->AMD_CB_DeviceCapOverride(
					pDat->PortList[i].NodeID,
					pDat->PortList[i].HostLink,
					pDat->PortList[i].HostDepth,
					(u8)SBDFO_SEG(pDat->PortList[i].Pointer),
					(u8)SBDFO_BUS(pDat->PortList[i].Pointer),
					(u8)SBDFO_DEV(pDat->PortList[i].Pointer),
					temp,
					pDat->PortList[i].Link,
					&(pDat->PortList[i].PrvWidthInCap),
					&(pDat->PortList[i].PrvWidthOutCap),
					&(pDat->PortList[i].PrvFrequencyCap),
					&(pDat->PortList[i].PrvFeatureCap));
			}
		}
	}
}

/***************************************************************************//**
 *
 * static void
 * setLinkData(sMainData *pDat, cNorthBridge *nb)
 *
 *  Description:
 *	 Change the hardware state for all links according to the now optimized data in the
 *	 port list data structure.
 *
 *  Parameters:
 *	  @param[in]  pDat = our global state, port list
 *	  @param[in]  *nb   = this northbridge
 *
 ******************************************************************************/
static void setLinkData(sMainData *pDat, cNorthBridge *nb)
{
	u8 i;
	SBDFO linkBase;
	u32 temp, temp2, frequency_index, widthin, widthout, bits;

	for (i = 0; i < pDat->TotalLinks*2; i++)
	{

		ASSERT(pDat->PortList[i&0xFE].SelWidthOut == pDat->PortList[(i&0xFE)+1].SelWidthIn);
		ASSERT(pDat->PortList[i&0xFE].SelWidthIn == pDat->PortList[(i&0xFE)+1].SelWidthOut);
		ASSERT(pDat->PortList[i&0xFE].SelFrequency == pDat->PortList[(i&0xFE)+1].SelFrequency);

		if (pDat->PortList[i].SelRegang)
		{
			ASSERT(pDat->PortList[i].Type == PORTLIST_TYPE_CPU);
			ASSERT(pDat->PortList[i].Link < 4);
			temp = 1;
			AmdPCIWriteBits(MAKE_SBDFO(makePCISegmentFromNode(pDat->PortList[i].NodeID),
					makePCIBusFromNode(pDat->PortList[i].NodeID),
					makePCIDeviceFromNode(pDat->PortList[i].NodeID),
					CPU_HTNB_FUNC_00,
					REG_HT_LINK_EXT_CONTROL0_0X170 + 4*pDat->PortList[i].Link),
					0, 0, &temp);
		}

		if (pDat->PortList[i].Type == PORTLIST_TYPE_CPU)
		{
			if (pDat->HtBlock->AMD_CB_OverrideCpuPort)
				pDat->HtBlock->AMD_CB_OverrideCpuPort(pDat->PortList[i].NodeID,
						pDat->PortList[i].Link,
						&(pDat->PortList[i].SelWidthIn),
						&(pDat->PortList[i].SelWidthOut),
						&(pDat->PortList[i].SelFrequency));
		}
		else
		{
			if (pDat->HtBlock->AMD_CB_OverrideDevicePort)
				pDat->HtBlock->AMD_CB_OverrideDevicePort(pDat->PortList[i].NodeID,
							pDat->PortList[i].HostLink,
							pDat->PortList[i].HostDepth,
							pDat->PortList[i].Link,
							&(pDat->PortList[i].SelWidthIn),
							&(pDat->PortList[i].SelWidthOut),
							&(pDat->PortList[i].SelFrequency));
		}

		linkBase = pDat->PortList[i].Pointer;
		if ((pDat->PortList[i].Type == PORTLIST_TYPE_IO) && (pDat->PortList[i].Link == 1))
			linkBase += HTSLAVE_LINK01_OFFSET;

		/* Some IO devices don't work properly when setting widths, so write them in a single operation,
		 * rather than individually.
		 */
		widthout = convertWidthToBits(pDat->PortList[i].SelWidthOut, pDat->nb);
		ASSERT(widthout == 1 || widthout == 0 || widthout == 5 || widthout == 4);
		widthin = convertWidthToBits(pDat->PortList[i].SelWidthIn, pDat->nb);
		ASSERT(widthin == 1 || widthin == 0 || widthin == 5 || widthin == 4);

		temp = (widthin & 7) | ((widthout & 7) << 4);
		setHtControlRegisterBits(linkBase + HTHOST_LINK_CONTROL_REG, 31, 24, &temp);

		temp = pDat->PortList[i].SelFrequency;
		if (pDat->PortList[i].Type == PORTLIST_TYPE_CPU)
		{
			ASSERT((temp >= HT_FREQUENCY_600M && temp <= HT_FREQUENCY_3200M)
				|| (temp == HT_FREQUENCY_200M) || (temp == HT_FREQUENCY_400M));
			frequency_index = temp;
			if (temp > 0xf) {
				temp2 = (temp >> 4) & 0x1;
				temp &= 0xf;
			} else {
				temp2 = 0x0;
			}
			/* NOTE
			 * The Family 15h BKDG Rev. 3.14 is wrong
			 * Freq[4] must be set before Freq[3:0], otherwise the register writes will be ignored!
			 */
			if (is_gt_rev_d())
				AmdPCIWriteBits(linkBase + HTHOST_FREQ_REV_REG_2, 0, 0, &temp2);
			AmdPCIWriteBits(linkBase + HTHOST_FREQ_REV_REG, 11, 8, &temp);

			/* Enable isochronous flow control mode if supported by chipset */
			if (is_fam15h()) {
				if (pDat->PortList[i].enable_isochronous_mode)
					temp = 1;
				else
					temp = 0;
				setHtControlRegisterBits(linkBase + HTHOST_LINK_CONTROL_REG, 12, 12, &temp);
			}

			if (frequency_index > HT_FREQUENCY_1000M) /*  Gen1 = 200MHz -> 1000MHz, Gen3 = 1200MHz -> 3200MHz */
			{
				/* Enable  for Gen3 frequencies */
				temp = 1;
			}
			else
			{
				/* Disable  for Gen1 frequencies */
				temp = 0;
			}
			/* HT3 retry mode enable / disable */
			AmdPCIWriteBits(MAKE_SBDFO(makePCISegmentFromNode(pDat->PortList[i].NodeID),
						makePCIBusFromNode(pDat->PortList[i].NodeID),
						makePCIDeviceFromNode(pDat->PortList[i].NodeID),
						CPU_HTNB_FUNC_00,
						REG_HT_LINK_RETRY0_0X130 + 4*pDat->PortList[i].Link),
						0, 0, &temp);

			/* and Scrambling enable / disable */
			AmdPCIWriteBits(MAKE_SBDFO(makePCISegmentFromNode(pDat->PortList[i].NodeID),
					makePCIBusFromNode(pDat->PortList[i].NodeID),
					makePCIDeviceFromNode(pDat->PortList[i].NodeID),
					CPU_HTNB_FUNC_00,
					REG_HT_LINK_EXT_CONTROL0_0X170 + 4*pDat->PortList[i].Link),
					3, 3, &temp);
		}
		else
		{
			SBDFO currentPtr;
			BOOL isFound;

			ASSERT(temp <= HT_FREQUENCY_3200M);
			/* Write the frequency setting */
			AmdPCIWriteBits(linkBase + HTSLAVE_FREQ_REV_0_REG, 11, 8, &temp);

			/* Handle additional HT3 frequency requirements, if needed,
			 * or clear them if switching down to ht1 on a warm reset.
			 * Gen1 = 200MHz -> 1000MHz, Gen3 = 1200MHz -> 2600MHz
			 *
			 * Even though we assert if debugging, we need to check that the capability was found
			 * always, since this is an unknown hardware device, also we are taking
			 * unqualified frequency from the call backs
			 * (could be trying to do ht3 on an ht1 IO device).
			 */

			if (temp > HT_FREQUENCY_1000M)
			{
				/* Enabling features if gen 3 */
				bits = 1;
			}
			else
			{
				/* Disabling features if gen 1 */
				bits = 0;
			}

			/* Enable isochronous flow control mode if supported by chipset */
			if (is_fam15h()) {
				if (pDat->PortList[i].enable_isochronous_mode)
					temp = 1;
				else
					temp = 0;
			}

			/* Retry Enable */
			isFound = FALSE;
			currentPtr = linkBase & (u32)0xFFFFF000; /* Set PCI Offset to 0 */
			do
			{
				AmdPCIFindNextCap(&currentPtr);
				if (currentPtr != ILLEGAL_SBDFO)
				{
					AmdPCIRead(currentPtr, &temp);
					/* HyperTransport Retry Capability? */
					if (IS_HT_RETRY_CAPABILITY(temp))
					{
						ASSERT(pDat->PortList[i].Link < 2);
						AmdPCIWriteBits(currentPtr + HTRETRY_CONTROL_REG,
								pDat->PortList[i].Link*16,
								pDat->PortList[i].Link*16,
								&bits);
						isFound = TRUE;
					}
				/* Some other capability, keep looking */
				}
				else
				{
					/* If we are turning it off, that may mean the device was only ht1 capable,
					 * so don't complain that we can't do it.
					 */
					if (bits != 0)
					{
						if (pDat->HtBlock->AMD_CB_EventNotify)
						{
							sHtEventOptRequiredCap evt;
							evt.eSize = sizeof(sHtEventOptRequiredCap);
							evt.node = pDat->PortList[i].NodeID;
							evt.link = pDat->PortList[i].HostLink;
							evt.depth = pDat->PortList[i].HostDepth;

							pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_WARNING,
										HT_EVENT_OPT_REQUIRED_CAP_RETRY,
										(u8 *)&evt);
						}
						STOP_HERE;
					}
					isFound = TRUE;
				}
			} while (!isFound);

			/* Scrambling enable */
			isFound = FALSE;
			currentPtr = linkBase & (u32)0xFFFFF000; /* Set PCI Offset to 0 */
			do
			{
				AmdPCIFindNextCap(&currentPtr);
				if (currentPtr != ILLEGAL_SBDFO)
				{
					AmdPCIRead(currentPtr, &temp);
					/* HyperTransport Gen3 Capability? */
					if (IS_HT_GEN3_CAPABILITY(temp))
					{
						ASSERT(pDat->PortList[i].Link < 2);
						AmdPCIWriteBits((currentPtr +
							HTGEN3_LINK_TRAINING_0_REG +
							pDat->PortList[i].Link*HTGEN3_LINK01_OFFSET),
							3, 3, &bits);
						isFound = TRUE;
					}
					/* Some other capability, keep looking */
					}
					else
					{
					/* If we are turning it off, that may mean the device was only ht1 capable,
					 * so don't complain that we can't do it.
					 */
					if (bits != 0)
					{
						if (pDat->HtBlock->AMD_CB_EventNotify)
						{
							sHtEventOptRequiredCap evt;
							evt.eSize = sizeof(sHtEventOptRequiredCap);
							evt.node = pDat->PortList[i].NodeID;
							evt.link = pDat->PortList[i].HostLink;
							evt.depth = pDat->PortList[i].HostDepth;

							pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_WARNING,
										HT_EVENT_OPT_REQUIRED_CAP_GEN3,
										(u8 *)&evt);
						}
						STOP_HERE;
					}
					isFound = TRUE;
				}
			} while (!isFound);
		}
	}
}

/***************************************************************************//**
 *
 * void
 * fam0fWriteHTLinkCmdBufferAlloc(u8 node, u8 link, u8 req, u8 preq, u8 rsp, u8 prb)
 *
 *  Description:
 *	Set the command buffer allocations in the buffer count register for the node and link.
 *	The command buffer settings in the low 16 bits are the same on both
 *	family 10h and family 0fh northbridges.
 *
 *  Parameters:
 *	@param[in] node = The node to set allocations on
 *	@param[in] link = the link to set allocations on
 *	@param[in] req  = non-posted Request Command Buffers
 *	@param[in] preq = Posted Request Command Buffers
 *	@param[in] rsp  = Response Command Buffers
 *	@param[in] prb  = Probe Command Buffers
 *
 ******************************************************************************/
#ifndef HT_BUILD_NC_ONLY

static void fam0fWriteHTLinkCmdBufferAlloc(u8 node, u8 link, u8 req, u8 preq, u8 rsp, u8 prb)
{
	u32 temp;
	SBDFO currentPtr;

	currentPtr = makeLinkBase(node, link);
	currentPtr += HTHOST_BUFFER_COUNT_REG;

	/* non-posted Request Command Buffers */
	temp = req;
	AmdPCIWriteBits(currentPtr, 3, 0, &temp);
	/* Posted Request Command Buffers */
	temp = preq;
	AmdPCIWriteBits(currentPtr, 7, 4, &temp);
	/* Response Command Buffers */
	temp = rsp;
	AmdPCIWriteBits(currentPtr, 11, 8, &temp);
	/* Probe Command Buffers */
	temp = prb;
	AmdPCIWriteBits(currentPtr, 15, 12, &temp);
	/* LockBc */
	temp = 1;
	AmdPCIWriteBits(currentPtr, 31, 31, &temp);
}
#endif /* HT_BUILD_NC_ONLY */

/***************************************************************************//**
 *
 * void
 * fam0fWriteHTLinkDatBufferAlloc(u8 node, u8 link, u8 reqD, u8 preqD, u8 rspD)
 *
 *  Description:
 *	 Set the data buffer allocations in the buffer count register for the node and link.
 *	 The command buffer settings in the high 16 bits are not the same on both
 *	 family 10h and family 0fh northbridges.
 *
 *  Parameters:
 *	@param[in] node  = The node to set allocations on
 *	@param[in] link  = the link to set allocations on
 *	@param[in] reqD  = non-posted Request Data Buffers
 *	@param[in] preqD = Posted Request Data Buffers
 *	@param[in] rspD  = Response Data Buffers
 *
 ******************************************************************************/
#ifndef HT_BUILD_NC_ONLY

static void fam0fWriteHTLinkDatBufferAlloc(u8 node, u8 link, u8 reqD, u8 preqD, u8 rspD)
{
	u32 temp;
	SBDFO currentPtr;

	currentPtr = makeLinkBase(node, link);
	currentPtr += HTHOST_BUFFER_COUNT_REG;

	/* Request Data Buffers */
	temp = reqD;
	AmdPCIWriteBits(currentPtr, 18, 16, &temp);
	/* Posted Request Data Buffers */
	temp = preqD;
	AmdPCIWriteBits(currentPtr, 22, 20, &temp);
	/* Response Data Buffers */
	temp = rspD;
	AmdPCIWriteBits(currentPtr, 26, 24, &temp);
}
#endif /* HT_BUILD_NC_ONLY */

/***************************************************************************//**
 *
 * static void
 * ht3WriteTrafficDistribution(u32 links01, u32 links10, cNorthBridge *nb)
 *
 *  Description:
 *	 Set the traffic distribution register for the links provided.
 *
 *  Parameters:
 *	@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
 *
 ******************************************************************************/
static void ht3WriteTrafficDistribution(u32 links01, u32 links10, cNorthBridge *nb)
{
#ifndef HT_BUILD_NC_ONLY
	u32 temp;

	/* Node 0 */
	/* DstLnk */
	AmdPCIWriteBits(MAKE_SBDFO(makePCISegmentFromNode(0),
			makePCIBusFromNode(0),
			makePCIDeviceFromNode(0),
			CPU_HTNB_FUNC_00,
			REG_HT_TRAFFIC_DIST_0X164),
			23, 16, &links01);
	/* DstNode = 1, cHTPrbDistEn = 1, cHTRspDistEn = 1, cHTReqDistEn = 1 */
	temp = 0x0107;
	AmdPCIWriteBits(MAKE_SBDFO(makePCISegmentFromNode(0),
			makePCIBusFromNode(0),
			makePCIDeviceFromNode(0),
			CPU_HTNB_FUNC_00,
			REG_HT_TRAFFIC_DIST_0X164),
			15, 0, &temp);

	/* Node 1 */
	/* DstLnk */
	AmdPCIWriteBits(MAKE_SBDFO(makePCISegmentFromNode(1),
			makePCIBusFromNode(1),
			makePCIDeviceFromNode(1),
			CPU_HTNB_FUNC_00,
			REG_HT_TRAFFIC_DIST_0X164),
			23, 16, &links10);
	/* DstNode = 0, cHTPrbDistEn = 1, cHTRspDistEn = 1, cHTReqDistEn = 1 */
	temp = 0x0007;
	AmdPCIWriteBits(MAKE_SBDFO(makePCISegmentFromNode(1),
			makePCIBusFromNode(1),
			makePCIDeviceFromNode(1),
			CPU_HTNB_FUNC_00,
			REG_HT_TRAFFIC_DIST_0X164),
			15, 0, &temp);
#endif /* HT_BUILD_NC_ONLY */
}

/***************************************************************************//**
 *
 * static void
 * ht1WriteTrafficDistribution(u32 links01, u32 links10, cNorthBridge *nb)
 *
 *  Description:
 *	 Traffic distribution is more complex in this case as the routing table must be
 *	 adjusted to use one link for requests and the other for responses.  Also,
 *	 perform the buffer tunings on the links required for this config.
 *
 *  Parameters:
 *	@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
 *
 ******************************************************************************/
static void ht1WriteTrafficDistribution(u32 links01, u32 links10, cNorthBridge *nb)
{
#ifndef HT_BUILD_NC_ONLY
	u32 route01, route10;
	u8 req0, req1, rsp0, rsp1, nclink;

	/*
	 * Get the current request route for 0->1 and 1->0.  This will indicate which of the links
	 * in links01 are connected to which links in links10.  Since we have to route to distribute
	 * traffic, we need to know that.	The link used by htinit will become the request, probe link.
	 * the other link will be used for responses.
	 */

	/* Get the routes, and hang on to them, we will write them back updated. */
	AmdPCIRead(MAKE_SBDFO(makePCISegmentFromNode(0),
				makePCIBusFromNode(0),
				makePCIDeviceFromNode(0),
				CPU_HTNB_FUNC_00,
				REG_ROUTE1_0X44),
				&route01);
	AmdPCIRead(MAKE_SBDFO(makePCISegmentFromNode(1),
				makePCIBusFromNode(1),
				makePCIDeviceFromNode(1),
				CPU_HTNB_FUNC_00,
				REG_ROUTE0_0X40),
				&route10);

	/* Convert the request routes to a link number.  Note "0xE" is ht1 nb specific.
	 * Find the response link numbers.
	*/
	ASSERT((route01 & 0xE) && (route10 & 0xE));    /* no route! error! */
	req0 = (u8)AmdBitScanReverse((route01 & 0xE)) - 1;
	req1 = (u8)AmdBitScanReverse((route10 & 0xE)) - 1;
	/* Now, find the other link for the responses */
	rsp0 = (u8)AmdBitScanReverse((links01 & ~((u32)1 << req0)));
	rsp1 = (u8)AmdBitScanReverse((links10 & ~((u32)1 << req1)));

	/* ht1 nb restriction, must have exactly two links */
	ASSERT(((((links01 & ~((u32)1 << req0)) & ~((u32)1 << rsp0))) == 0)
		&& ((((links10 & ~((u32)1 << req1)) & ~((u32)1 << rsp1))) == 0));

	route01 = (route01 & ~0x0E00) | ((u32)0x0100<<(rsp0 + 1));
	route10 = (route10 & ~0x0E00) | ((u32)0x0100<<(rsp1 + 1));

	AmdPCIWrite(MAKE_SBDFO(makePCISegmentFromNode(0),
				makePCIBusFromNode(0),
				makePCIDeviceFromNode(0),
				CPU_HTNB_FUNC_00,
				REG_ROUTE1_0X44),
				&route01);

	AmdPCIWrite(MAKE_SBDFO(makePCISegmentFromNode(1),
				makePCIBusFromNode(1),
				makePCIDeviceFromNode(1),
				CPU_HTNB_FUNC_00,
				REG_ROUTE0_0X40),
				&route10);

	/* While we otherwise do buffer tunings elsewhere, for the dual cHT DP case with
	 * ht1 northbridges like family 0Fh, do the tunings here where we have all the
	 * link and route info at hand and don't need to recalculate it.
	 */

	/* Node 0, Request / Probe Link (note family F only has links < 4) */
	fam0fWriteHTLinkCmdBufferAlloc(0, req0, 6, 3, 1, 6);
	fam0fWriteHTLinkDatBufferAlloc(0, req0, 4, 3, 1);
	/* Node 0, Response Link (note family F only has links < 4) */
	fam0fWriteHTLinkCmdBufferAlloc(0, rsp0, 1, 0, 15, 0);
	fam0fWriteHTLinkDatBufferAlloc(0, rsp0, 1, 1, 6);
	/* Node 1, Request / Probe Link (note family F only has links < 4) */
	fam0fWriteHTLinkCmdBufferAlloc(1, req1, 6, 3, 1, 6);
	fam0fWriteHTLinkDatBufferAlloc(1, req1, 4, 3, 1);
	/* Node 1, Response Link (note family F only has links < 4) */
	fam0fWriteHTLinkCmdBufferAlloc(1, rsp1, 1, 0, 15, 0);
	fam0fWriteHTLinkDatBufferAlloc(1, rsp1, 1, 1, 6);

	/* Node 0, is the third link non-coherent? */
	nclink = (u8)AmdBitScanReverse(((u8)0x07 & ~((u32)1 << req0) & ~((u32)1 << rsp0)));
	if (nb->verifyLinkIsNonCoherent(0, nclink, nb))
	{
		fam0fWriteHTLinkCmdBufferAlloc(0, nclink, 6, 5, 2, 0);
	}

	/* Node 1, is the third link non-coherent? */
	nclink = (u8)AmdBitScanReverse(((u8)0x07 & ~((u32)1 << req1) & ~((u32)1 << rsp1)));
	if (nb->verifyLinkIsNonCoherent(1, nclink, nb))
	{
		fam0fWriteHTLinkCmdBufferAlloc(1, nclink, 6, 5, 2, 0);
	}
#endif /* HT_BUILD_NC_ONLY */
}

/***************************************************************************//**
 *
 * static void
 * fam0fBufferOptimizations(u8 node, sMainData *pDat, cNorthBridge *nb)
 *
 *  Description:
 *	 Buffer tunings are inherently northbridge specific. Check for specific configs
 *	 which require adjustments and apply any standard workarounds to this node.
 *
 *  Parameters:
 *	@param[in]  node      = the node to
 *	@param[in] *pDat  = coherent links from node 0 to 1
 *	@param[in]  nb = this northbridge
 *
 ******************************************************************************/
static void fam0fBufferOptimizations(u8 node, sMainData *pDat, cNorthBridge *nb)
{
#ifndef HT_BUILD_NC_ONLY
	u8 i;
	u32 temp;
	SBDFO currentPtr;

	ASSERT(node < nb->maxNodes);

	/* Fix the FIFO pointer register before changing speeds */
	currentPtr = MAKE_SBDFO(makePCISegmentFromNode(node),
				makePCIBusFromNode(node),
				makePCIDeviceFromNode(node),
				CPU_NB_FUNC_03,
				REG_NB_FIFOPTR_3XDC);
	for (i = 0; i < nb->maxLinks; i++)
	{
		temp = 0;
		if (nb->verifyLinkIsCoherent(node, i, nb))
		{
			temp = 0x26;
			ASSERT(i < 3);
			AmdPCIWriteBits(currentPtr, 8*i + 5, 8*i, &temp);
		}
		else
		{
			if (nb->verifyLinkIsNonCoherent(node, i, nb))
			{
				temp = 0x25;
				ASSERT(i < 3);
				AmdPCIWriteBits(currentPtr, 8*i + 5, 8*i, &temp);
			}
		}
	}
	/*
	 * 8P Buffer tuning.
	 * Either apply the BKDG tunings or, if applicable, apply the more restrictive errata 153
	 * workaround.
	 * If 8 nodes, Check this node for 'inner' or 'outer'.
	 * Tune each link based on coherent or non-coherent
	 */
	if (pDat->NodesDiscovered >= 6)
	{
		u8 j;
		BOOL isOuter;
		BOOL isErrata153;

		/* This is for family 0Fh, so assuming dual core max then 7 or 8 nodes are required
		 * to be in the situation of 14 or more cores.	 We checked nodes above, cross check
		 * that the number of cores is 14 or more. We want both 14 cores with at least 7 or 8 nodes
		 * not one condition alone, to apply the errata 153 workaround.  Otherwise, 7 or 8 rev F
		 * nodes use the BKDG tuning.
		 */

		isErrata153 = 0;

		AmdPCIReadBits (MAKE_SBDFO(makePCISegmentFromNode(0),
					makePCIBusFromNode(0),
					makePCIDeviceFromNode(0),
					CPU_HTNB_FUNC_00,
					REG_NODE_ID_0X60),
					19, 16, &temp);

		if (temp >= 14)
		{
			/* Check whether we need to do errata 153 tuning or BKDG tuning.
			 * Errata 153 applies to JH-1, JH-2 and older.  It is fixed in JH-3
			 * (and, one assumes, from there on).
			 */
			for (i = 0; i < (pDat->NodesDiscovered +1); i++)
			{
				AmdPCIReadBits(MAKE_SBDFO(makePCISegmentFromNode(i),
						makePCIBusFromNode(i),
						makePCIDeviceFromNode(i),
						CPU_NB_FUNC_03,
						REG_NB_CPUID_3XFC),
						7, 0, &temp);
				if (((u8)temp & ~0x40) < 0x13)
				{
					isErrata153 = 1;
					break;
				}
			}
		}

		for (i = 0; i < CPU_ADDR_NUM_CONFIG_MAPS; i++)
		{
			isOuter = FALSE;
			/* Check for outer node by scanning the config maps on node 0 for one
			 * which is assigned to this node.
			 */
			currentPtr = MAKE_SBDFO(makePCISegmentFromNode(0),
						makePCIBusFromNode(0),
						makePCIDeviceFromNode(0),
						CPU_ADDR_FUNC_01,
						REG_ADDR_CONFIG_MAP0_1XE0 + (4 * i));
			AmdPCIReadBits (currentPtr, 1, 0, &temp);
			/* Make sure this config map is valid, if it is it will be enabled for read/write */
			if (temp == 3)
			{
				/* It's valid, get the node (that node is an outer node) */
				AmdPCIReadBits (currentPtr, 6, 4, &temp);
				/* Is the node we're working on now? */
				if (node == (u8)temp)
				{
					/* This is an outer node.	Tune it appropriately. */
					for (j = 0; j < nb->maxLinks; j++)
					{
						if (isErrata153)
						{
							if (nb->verifyLinkIsCoherent(node, j, nb))
							{
								fam0fWriteHTLinkCmdBufferAlloc(node, j, 1, 1, 6, 4);
							}
							else
							{
								if (nb->verifyLinkIsNonCoherent(node, j, nb))
								{
									fam0fWriteHTLinkCmdBufferAlloc(node, j, 5, 4, 1, 0);
								}
							}
						}
						else
						{
							if (nb->verifyLinkIsCoherent(node, j, nb))
							{
								fam0fWriteHTLinkCmdBufferAlloc(node, j, 1, 1, 8, 5);
							}
						}
					}
					/*
					 * SRI to XBAR Buffer Counts are correct for outer node at power on defaults.
					 */
					isOuter = TRUE;
					break;
				}
			}
			/* We fill config maps in ascending order, so if we didn't use this one, we're done. */
			else break;
		}
		if (!isOuter)
		{
			if (isErrata153)
			{
				/* Tuning for inner node coherent links */
				for (j = 0; j < nb->maxLinks; j++)
				{
					if (nb->verifyLinkIsCoherent(node, j, nb))
					{
						fam0fWriteHTLinkCmdBufferAlloc(node, j, 2, 1, 5, 4);
					}

				}
				/* SRI to XBAR Buffer Count for inner nodes, zero DReq and DPReq */
				temp = 0;
				AmdPCIWriteBits (MAKE_SBDFO(makePCISegmentFromNode(node),
							makePCIBusFromNode(node),
							makePCIDeviceFromNode(node),
							CPU_NB_FUNC_03,
							REG_NB_SRI_XBAR_BUF_3X70),
							31, 28, &temp);
			}
		}

		/*
		 * Tune MCT to XBAR Buffer Count the same an all nodes, 2 Probes, 5 Response
		 */
		if (isErrata153)
		{
			temp = 0x25;
			AmdPCIWriteBits (MAKE_SBDFO(makePCISegmentFromNode(node),
						makePCIBusFromNode(node),
						makePCIDeviceFromNode(node),
						CPU_NB_FUNC_03,
						REG_NB_MCT_XBAR_BUF_3X78),
						14, 8, &temp);
		}
	}
#endif /* HT_BUILD_NC_ONLY */
}

/***************************************************************************//**
 *
 * static void
 * fam10BufferOptimizations(u8 node, sMainData *pDat, cNorthBridge *nb)
 *
 *  Description:
 *	 Buffer tunings are inherently northbridge specific. Check for specific configs
 *	 which require adjustments and apply any standard workarounds to this node.
 *
 *  Parameters:
 *	@param[in] node       = the node to tune
 *	@param[in] *pDat  = global state
 *	@param[in] nb = this northbridge
 *
 ******************************************************************************/
static void fam10BufferOptimizations(u8 node, sMainData *pDat, cNorthBridge *nb)
{
	u32 temp;
	SBDFO currentPtr;
	u8 i;

	ASSERT(node < nb->maxNodes);

	/*
	 * 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 < pDat->TotalLinks*2; i++)
	{
		if ((pDat->PortList[i].NodeID == node) && (pDat->PortList[i].Type == PORTLIST_TYPE_CPU))
		{
			/* If the link is greater than 4, this is a sublink 1, so it is not reganged. */
			if (pDat->PortList[i].Link < 4)
			{
				currentPtr = MAKE_SBDFO(makePCISegmentFromNode(node),
						makePCIBusFromNode(node),
						makePCIDeviceFromNode(node),
						CPU_NB_FUNC_03,
						REG_NB_LINK_XCS_TOKEN0_3X148 + 4*pDat->PortList[i].Link);
				if (pDat->PortList[i].SelRegang)
				{
					/* Handle all the regang Token count adjustments */

					/* Sublink 0: [Probe0tok] = 2 [Rsp0tok] = 2 [PReq0tok] = 2 [Req0tok] = 2 */
					temp = 0xAA;
					AmdPCIWriteBits(currentPtr, 7, 0, &temp);
					/* Sublink 1: [Probe1tok] = 0 [Rsp1tok] = 0 [PReq1tok] = 0 [Req1tok] = 0 */
					temp = 0;
					AmdPCIWriteBits(currentPtr, 23, 16, &temp);
					/* [FreeTok] = 3 */
					temp = 3;
					AmdPCIWriteBits(currentPtr, 15, 14, &temp);
				}
				else
				{
					/* Read the regang bit in hardware */
					AmdPCIReadBits(MAKE_SBDFO(makePCISegmentFromNode(pDat->PortList[i].NodeID),
							makePCIBusFromNode(pDat->PortList[i].NodeID),
							makePCIDeviceFromNode(pDat->PortList[i].NodeID),
							CPU_HTNB_FUNC_00,
							REG_HT_LINK_EXT_CONTROL0_0X170 + 4*pDat->PortList[i].Link),
							0, 0, &temp);
					if (temp == 1)
					{
						/* handle a minor adjustment for stapped 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;
						AmdPCIWriteBits(currentPtr, 15, 14, &temp);
					}
				}
			}
		}
	}
}

/***************************************************************************//**
 *
 * static void
 * fam15BufferOptimizations(u8 node, sMainData *pDat, cNorthBridge *nb)
 *
 *  Description:
 *	 Buffer tunings are inherently northbridge specific. Check for specific configs
 *	 which require adjustments and apply any standard workarounds to this node.
 *
 *  Parameters:
 *	@param[in] node       = the node to tune
 *	@param[in] *pDat  = global state
 *	@param[in] nb = this northbridge
 *
 ******************************************************************************/
static void fam15BufferOptimizations(u8 node, sMainData *pDat, cNorthBridge *nb)
{
	/* Buffer count setup on Family 15h is currently handled in cpuSetAMDPCI */
}

/*
 * North Bridge 'constructor'.
 *
 */

/***************************************************************************//**
 *
 * void
 * newNorthBridge(u8 node, cNorthBridge *nb)
 *
 *  Description:
 *	 Construct a new northbridge.  This routine encapsulates knowledge of how to tell
 *	 significant differences between families of supported northbridges and what routines
 *	 can be used in common and which are unique.  A fully populated northbridge interface
 *	 is provided by nb.
 *
 *  Parameters:
 *	  @param            node
 *	  @param[out]	    nb		 = the caller's northbridge structure to initialize.
 *
 ******************************************************************************/
void newNorthBridge(u8 node, cNorthBridge *nb)
{
	u32 match;
	u32 extFam, baseFam, model;

	cNorthBridge fam15 =
	{
#ifdef HT_BUILD_NC_ONLY
		8,
		1,
		12,
#else
		8,
		8,
		64,
#endif /* HT_BUILD_NC_ONLY*/
		writeRoutingTable,
		writeNodeID,
		readDefLnk,
		enableRoutingTables,
		verifyLinkIsCoherent,
		readTrueLinkFailStatus,
		readToken,
		writeToken,
		fam15GetNumCoresOnNode,
		setTotalNodesAndCores,
		limitNodes,
		writeFullRoutingTable,
		isCompatible,
		fam15IsCapable,
		(void (*)(u8, u8, cNorthBridge*))commonVoid,
		(BOOL (*)(u8, u8, sMainData*, cNorthBridge*))commonReturnFalse,
		readSbLink,
		verifyLinkIsNonCoherent,
		ht3SetCFGAddrMap,
		convertBitsToWidth,
		convertWidthToBits,
		fam15NorthBridgeFreqMask,
		gatherLinkData,
		setLinkData,
		ht3WriteTrafficDistribution,
		fam15BufferOptimizations,
		0x00000001,
		0x00000200,
		18,
		0x00000f06
	};

	cNorthBridge fam10 =
	{
#ifdef HT_BUILD_NC_ONLY
		8,
		1,
		12,
#else
		8,
		8,
		64,
#endif /* HT_BUILD_NC_ONLY*/
		writeRoutingTable,
		writeNodeID,
		readDefLnk,
		enableRoutingTables,
		verifyLinkIsCoherent,
		readTrueLinkFailStatus,
		readToken,
		writeToken,
		fam10GetNumCoresOnNode,
		setTotalNodesAndCores,
		limitNodes,
		writeFullRoutingTable,
		isCompatible,
		fam10IsCapable,
		(void (*)(u8, u8, cNorthBridge*))commonVoid,
		(BOOL (*)(u8, u8, sMainData*, cNorthBridge*))commonReturnFalse,
		readSbLink,
		verifyLinkIsNonCoherent,
		ht3SetCFGAddrMap,
		convertBitsToWidth,
		convertWidthToBits,
		fam10NorthBridgeFreqMask,
		gatherLinkData,
		setLinkData,
		ht3WriteTrafficDistribution,
		fam10BufferOptimizations,
		0x00000001,
		0x00000200,
		18,
		0x00000f01
	};

	cNorthBridge fam0f =
	{
#ifdef HT_BUILD_NC_ONLY
		3,
		1,
		12,
#else
		3,
		8,
		32,
#endif /* HT_BUILD_NC_ONLY*/
		writeRoutingTable,
		writeNodeID,
		readDefLnk,
		enableRoutingTables,
		verifyLinkIsCoherent,
		readTrueLinkFailStatus,
		readToken,
		writeToken,
		fam0FGetNumCoresOnNode,
		setTotalNodesAndCores,
		limitNodes,
		writeFullRoutingTable,
		isCompatible,
		fam0fIsCapable,
		fam0fStopLink,
		(BOOL (*)(u8, u8, sMainData*, cNorthBridge*))commonReturnFalse,
		readSbLink,
		verifyLinkIsNonCoherent,
		ht1SetCFGAddrMap,
		convertBitsToWidth,
		convertWidthToBits,
		ht1NorthBridgeFreqMask,
		gatherLinkData,
		setLinkData,
		ht1WriteTrafficDistribution,
		fam0fBufferOptimizations,
		0x00000001,
		0x00000100,
		16,
		0x00000f00
	};

	/* Start with enough of the key to identify the northbridge interface */
	AmdPCIReadBits(MAKE_SBDFO(makePCISegmentFromNode(node),
			makePCIBusFromNode(node),
			makePCIDeviceFromNode(node),
			CPU_NB_FUNC_03,
			REG_NB_CPUID_3XFC),
			27, 20, &extFam);
	AmdPCIReadBits(MAKE_SBDFO(makePCISegmentFromNode(node),
			makePCIBusFromNode(node),
			makePCIDeviceFromNode(node),
			CPU_NB_FUNC_03,
			REG_NB_CPUID_3XFC),
			11, 8, &baseFam);
	AmdPCIReadBits(MAKE_SBDFO(makePCISegmentFromNode(node),
			makePCIBusFromNode(node),
			makePCIDeviceFromNode(node),
			CPU_NB_FUNC_03,
			REG_NB_CPUID_3XFC),
			7, 4, &model);
	match = (u32)((baseFam << 8) | extFam);

	/* Test each in turn looking for a match.
	 * Initialize the struct if found.
	 */
	if (match == fam15.compatibleKey)
	{
		Amdmemcpy((void *)nb, (const void *)&fam15, (u32) sizeof(cNorthBridge));
	}
	else if (match == fam10.compatibleKey)
	{
		Amdmemcpy((void *)nb, (const void *)&fam10, (u32) sizeof(cNorthBridge));
	}
	else
	{
		if (match == fam0f.compatibleKey)
		{
			Amdmemcpy((void *)nb, (const void *)&fam0f, (u32) sizeof(cNorthBridge));
		}
		else
		{
		STOP_HERE;
		}
	}

	/* Update the initial limited key to the real one, which may include other matching info */
	nb->compatibleKey = makeKey(node);
}
