diff options
Diffstat (limited to 'src/northbridge/amd/amdht/h3finit.c')
-rw-r--r-- | src/northbridge/amd/amdht/h3finit.c | 1678 |
1 files changed, 1678 insertions, 0 deletions
diff --git a/src/northbridge/amd/amdht/h3finit.c b/src/northbridge/amd/amdht/h3finit.c new file mode 100644 index 0000000000..dd3e3813bd --- /dev/null +++ b/src/northbridge/amd/amdht/h3finit.c @@ -0,0 +1,1678 @@ +/* + * This file is part of the LinuxBIOS project. + * + * Copyright (C) 2007 Advanced Micro Devices, Inc. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + *---------------------------------------------------------------------------- + * MODULES USED + * + *---------------------------------------------------------------------------- + */ + +#undef FILECODE +#define FILECODE 0xF001 + +#include "comlib.h" +#include "h3finit.h" +#include "h3ffeat.h" +#include "h3ncmn.h" +#include "h3gtopo.h" +#include "AsPsNb.h" +/* this is pre-ram so include the required C files here */ +#include "comlib.c" +#include "AsPsNb.c" +#include "h3ncmn.c" + +/*---------------------------------------------------------------------------- + * DEFINITIONS AND MACROS + * + *---------------------------------------------------------------------------- + */ + +#undef FILECODE +#define FILECODE 0xF001 + +/* APIC defines from amdgesa.inc, which can't be included in to c code. */ +#define APIC_Base_BSP 8 +#define APIC_Base 0x1b + +/*---------------------------------------------------------------------------- + * TYPEDEFS AND STRUCTURES + * + *---------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * PROTOTYPES OF LOCAL FUNCTIONS + * + *---------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * EXPORTED FUNCTIONS + * + *---------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * LOCAL FUNCTIONS + * + *---------------------------------------------------------------------------- + */ +#ifndef HT_BUILD_NC_ONLY +/* + ************************************************************************** + * Routing table decompressor + ************************************************************************** + */ + +/* + ************************************************************************** + * Graph Support routines + * These routines provide support for dealing with the graph representation + * of the topologies, along with the routing table information for that topology. + * The routing information is compressed and these routines currently decompress + * 'on the fly'. A graph is represented as a set of routes. All the edges in the + * graph are routes; a direct route from node i to node j exists in the graph IFF + * there is an edge directly connecting node i to node j. All other routes designate + * the edge which the route to that node initially takes, by designating a node + * to which a direct connection exists. That is, the route to non-adjacent node j + * from node i specifies node k where node i directly connects to node k. + * + * + * pseudo definition of compressed graph: + * typedef struct + * { + * BIT broadcast[8]; + * uint4 responseRoute; + * uint4 requestRoute; + * } sRoute; + * typedef struct + * { + * u8 size; + * sRoute graph[size][size]; + * } sGraph; + * + ************************************************************************** + */ + +/*---------------------------------------------------------------------------------------- + * int + * graphHowManyNodes(u8 *graph) + * + * Description: + * Returns the number of nodes in the compressed graph + * + * Parameters: + * @param[in] u8 graph = a compressed graph + * @param[out] u8 results = the number of nodes in the graph + * --------------------------------------------------------------------------------------- + */ +int graphHowManyNodes(u8 *graph) +{ + return graph[0]; +} + +/*---------------------------------------------------------------------------------------- + * BOOL + * graphIsAdjacent(u8 *graph, u8 nodeA, u8 nodeB) + * + * Description: + * Returns true if NodeA is directly connected to NodeB, false otherwise + * (if NodeA == NodeB also returns false) + * Relies on rule that directly connected nodes always route requests directly. + * + * Parameters: + * @param[in] u8 graph = the graph to examine + * @param[in] u8 nodeA = the node number of the first node + * @param[in] u8 nodeB = the node number of the second node + * @param[out] BOOL results = true if nodeA connects to nodeB false if not + * --------------------------------------------------------------------------------------- + */ +BOOL graphIsAdjacent(u8 *graph, u8 nodeA, u8 nodeB) +{ + u8 size = graph[0]; + ASSERT(size <= MAX_NODES); + ASSERT((nodeA < size) && (nodeB < size)); + return (graph[1+(nodeA*size+nodeB)*2+1] & 0x0F) == nodeB; +} + +/*---------------------------------------------------------------------------------------- + * u8 + * graphGetRsp(u8 *graph, u8 nodeA, u8 nodeB) + * + * Description: + * Returns the graph node used by nodeA to route responses targeted at nodeB. + * This will be a node directly connected to nodeA (possibly nodeB itself), + * or "Route to Self" if nodeA and nodeB are the same node. + * Note that all node numbers are abstract node numbers of the topology graph, + * it is the responsibility of the caller to apply any permutation needed. + * + * Parameters: + * @param[in] u8 graph = the graph to examine + * @param[in] u8 nodeA = the node number of the first node + * @param[in] u8 nodeB = the node number of the second node + * @param[out] u8 results = The response route node + * --------------------------------------------------------------------------------------- + */ +u8 graphGetRsp(u8 *graph, u8 nodeA, u8 nodeB) +{ + u8 size = graph[0]; + ASSERT(size <= MAX_NODES); + ASSERT((nodeA < size) && (nodeB < size)); + return (graph[1+(nodeA*size+nodeB)*2+1] & 0xF0)>>4; +} + +/*---------------------------------------------------------------------------------------- + * u8 + * graphGetReq(u8 *graph, u8 nodeA, u8 nodeB) + * + * Description: + * Returns the graph node used by nodeA to route requests targeted at nodeB. + * This will be a node directly connected to nodeA (possibly nodeB itself), + * or "Route to Self" if nodeA and nodeB are the same node. + * Note that all node numbers are abstract node numbers of the topology graph, + * it is the responsibility of the caller to apply any permutation needed. + * + * Parameters: + * @param[in] u8 graph = the graph to examine + * @param[in] u8 nodeA = the node number of the first node + * @param[in] u8 nodeB = the node number of the second node + * @param[out] u8 results = The request route node + * --------------------------------------------------------------------------------------- + */ +u8 graphGetReq(u8 *graph, u8 nodeA, u8 nodeB) +{ + int size = graph[0]; + ASSERT(size <= MAX_NODES); + ASSERT((nodeA < size) && (nodeB < size)); + return (graph[1+(nodeA*size+nodeB)*2+1] & 0x0F); +} + +/*---------------------------------------------------------------------------------------- + * u8 + * graphGetBc(unsigned char *graph, int nodeA, int nodeB) + * + * Description: + * Returns a bit vector of nodes that nodeA should forward a broadcast from + * nodeB towards + * + * Parameters: + * @param[in] u8 graph = the graph to examine + * @param[in] u8 nodeA = the node number of the first node + * @param[in] u8 nodeB = the node number of the second node + * OU u8 results = the broadcast routes for nodeA from nodeB + * --------------------------------------------------------------------------------------- + */ +u8 graphGetBc(unsigned char *graph, int nodeA, int nodeB) +{ + int size = graph[0]; + ASSERT(size <= MAX_NODES); + ASSERT((nodeA < size) && (nodeB < size)); + return graph[1+(nodeA*size+nodeB)*2]; +} + + +/*************************************************************************** + *** GENERIC HYPERTRANSPORT DISCOVERY CODE *** + ***************************************************************************/ + +/*---------------------------------------------------------------------------------------- + * void + * routeFromBSP(u8 targetNode, u8 actualTarget, sMainData *pDat) + * + * Description: + * Ensure a request / response route from target node to bsp. Since target node is + * always a predecessor of actual target node, each node gets a route to actual target + * on the link that goes to target. The routing produced by this routine is adequate + * for config access during discovery, but NOT for coherency. + * + * Parameters: + * @param[in] u8 targetNode = the path to actual target goes through target + * @param[in] u8 actualTarget = the ultimate target being routed to + * @param[in] sMainData* pDat = our global state, port config info + * --------------------------------------------------------------------------------------- + */ +void routeFromBSP(u8 targetNode, u8 actualTarget, sMainData *pDat) +{ + u8 predecessorNode, predecessorLink, currentPair; + + if (targetNode == 0) + return; // BSP has no predecessor, stop + + // Search for the link that connects targetNode to its predecessor + currentPair = 0; + while (pDat->PortList[currentPair*2+1].NodeID != targetNode) + { + currentPair++; + ASSERT(currentPair < pDat->TotalLinks); + } + + predecessorNode = pDat->PortList[currentPair*2].NodeID; + predecessorLink = pDat->PortList[currentPair*2].Link; + + // Recursively call self to ensure the route from the BSP to the Predecessor + // Node is established + routeFromBSP(predecessorNode, actualTarget, pDat); + + pDat->nb->writeRoutingTable(predecessorNode, actualTarget, predecessorLink, pDat->nb); +} + +/*---------------------------------------------------------------------------------------- + * u8 + * convertNodeToLink(u8 srcNode, u8 targetNode, sMainData *pDat) + * + * Description: + * Return the link on source node which connects to target node + * + * Parameters: + * @param[in] u8 srcNode = the source node + * @param[in] u8 targetNode = the target node to find the link to + * @param[in] sMainData* pDat = our global state + * @param[out] u8 results = the link on source which connects to target + * --------------------------------------------------------------------------------------- + */ +u8 convertNodeToLink(u8 srcNode, u8 targetNode, sMainData *pDat) +{ + u8 targetlink = INVALID_LINK; + u8 k; + + for (k = 0; k < pDat->TotalLinks*2; k += 2) + { + if ((pDat->PortList[k+0].NodeID == srcNode) && (pDat->PortList[k+1].NodeID == targetNode)) + { + targetlink = pDat->PortList[k+0].Link; + break; + } + else if ((pDat->PortList[k+1].NodeID == srcNode) && (pDat->PortList[k+0].NodeID == targetNode)) + { + targetlink = pDat->PortList[k+1].Link; + break; + } + } + ASSERT(targetlink != INVALID_LINK); + + return targetlink; +} + + +/*---------------------------------------------------------------------------------------- + * void + * htDiscoveryFloodFill(sMainData *pDat) + * + * Description: + * Discover all coherent devices in the system, initializing some basics like node IDs + * and total nodes found in the process. As we go we also build a representation of the + * discovered system which we will use later to program the routing tables. During this + * step, the routing is via default link back to BSP and to each new node on the link it + * was discovered on (no coherency is active yet). + * + * Parameters: + * @param[in] sMainData* pDat = our global state + * --------------------------------------------------------------------------------------- + */ +void htDiscoveryFloodFill(sMainData *pDat) +{ + u8 currentNode = 0; + u8 currentLink; + + /* Entries are always added in pairs, the even indices are the 'source' + * side closest to the BSP, the odd indices are the 'destination' side + */ + + while (currentNode <= pDat->NodesDiscovered) + { + u32 temp; + + if (currentNode != 0) + { + /* Set path from BSP to currentNode */ + routeFromBSP(currentNode, currentNode, pDat); + + /* Set path from BSP to currentNode for currentNode+1 if + * currentNode+1 != MAX_NODES + */ + if (currentNode+1 != MAX_NODES) + routeFromBSP(currentNode, currentNode+1, pDat); + + /* Configure currentNode to route traffic to the BSP through its + * default link + */ + pDat->nb->writeRoutingTable(currentNode, 0, pDat->nb->readDefLnk(currentNode, pDat->nb), pDat->nb); + } + + /* Set currentNode's NodeID field to currentNode */ + pDat->nb->writeNodeID(currentNode, currentNode, pDat->nb); + + /* Enable routing tables on currentNode*/ + pDat->nb->enableRoutingTables(currentNode, pDat->nb); + + for (currentLink = 0; currentLink < pDat->nb->maxLinks; currentLink++) + { + BOOL linkfound; + u8 token; + + if (pDat->HtBlock->AMD_CB_IgnoreLink && pDat->HtBlock->AMD_CB_IgnoreLink(currentNode, currentLink)) + continue; + + if (pDat->nb->readTrueLinkFailStatus(currentNode, currentLink, pDat, pDat->nb)) + continue; + + /* Make sure that the link is connected, coherent, and ready */ + if (!pDat->nb->verifyLinkIsCoherent(currentNode, currentLink, pDat->nb)) + continue; + + + /* Test to see if the currentLink has already been explored */ + linkfound = FALSE; + for (temp = 0; temp < pDat->TotalLinks; temp++) + { + if ((pDat->PortList[temp*2+1].NodeID == currentNode) && + (pDat->PortList[temp*2+1].Link == currentLink)) + { + linkfound = TRUE; + break; + } + } + if (linkfound) + { + /* We had already expored this link */ + continue; + } + + if (pDat->nb->handleSpecialLinkCase(currentNode, currentLink, pDat, pDat->nb)) + { + continue; + } + + /* Modify currentNode's routing table to use currentLink to send + * traffic to currentNode+1 + */ + pDat->nb->writeRoutingTable(currentNode, currentNode+1, currentLink, pDat->nb); + + /* Check the northbridge of the node we just found, to make sure it is compatible + * before doing anything else to it. + */ + if (!pDat->nb->isCompatible(currentNode+1, pDat->nb)) + { + u8 nodeToKill; + + /* Notify BIOS of event (while variables are still the same) */ + if (pDat->HtBlock->AMD_CB_EventNotify) + { + sHtEventCohFamilyFeud evt = {sizeof(sHtEventCohFamilyFeud), + currentNode, + currentLink, + pDat->NodesDiscovered}; + + pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR, + HT_EVENT_COH_FAMILY_FEUD, + (u8 *)&evt); + } + + /* If node is not compatible, force boot to 1P + * If they are not compatible stop cHT init and: + * 1. Disable all cHT links on the BSP + * 2. Configure the BSP routing tables as a UP. + * 3. Notify main BIOS. + */ + pDat->NodesDiscovered = 0; + currentNode = 0; + pDat->TotalLinks = 0; + /* Abandon our coherent link data structure. At this point there may + * be coherent links on the BSP that are not yet in the portList, and + * we have to turn them off anyway. So depend on the hardware to tell us. + */ + for (currentLink = 0; currentLink < pDat->nb->maxLinks; currentLink++) + { + /* Stop all links which are connected, coherent, and ready */ + if (pDat->nb->verifyLinkIsCoherent(currentNode, currentLink, pDat->nb)) + pDat->nb->stopLink(currentNode, currentLink, pDat->nb); + } + + for (nodeToKill = 0; nodeToKill < pDat->nb->maxNodes; nodeToKill++) + { + pDat->nb->writeFullRoutingTable(0, nodeToKill, ROUTETOSELF, ROUTETOSELF, 0, pDat->nb); + } + + /* End Coherent Discovery */ + STOP_HERE; + break; + } + + /* Read token from Current+1 */ + token = pDat->nb->readToken(currentNode+1, pDat->nb); + ASSERT(token <= pDat->NodesDiscovered); + if (token == 0) + { + pDat->NodesDiscovered++; + ASSERT(pDat->NodesDiscovered < pDat->nb->maxNodes); + /* Check the capability of northbridges against the currently known configuration */ + if (!pDat->nb->isCapable(currentNode+1, pDat, pDat->nb)) + { + u8 nodeToKill; + + /* Notify BIOS of event */ + if (pDat->HtBlock->AMD_CB_EventNotify) + { + sHtEventCohMpCapMismatch evt = {sizeof(sHtEventCohMpCapMismatch), + currentNode, + currentLink, + pDat->sysMpCap, + pDat->NodesDiscovered}; + + pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR, + HT_EVENT_COH_MPCAP_MISMATCH, + (u8 *)&evt); + } + + pDat->NodesDiscovered = 0; + currentNode = 0; + pDat->TotalLinks = 0; + + for (nodeToKill = 0; nodeToKill < pDat->nb->maxNodes; nodeToKill++) + { + pDat->nb->writeFullRoutingTable(0, nodeToKill, ROUTETOSELF, ROUTETOSELF, 0, pDat->nb); + } + + /* End Coherent Discovery */ + STOP_HERE; + break; + } + + token = pDat->NodesDiscovered; + pDat->nb->writeToken(currentNode+1, token, pDat->nb); + /* Inform that we have discovered a node, so that logical id to + * socket mapping info can be recorded. + */ + if (pDat->HtBlock->AMD_CB_EventNotify) + { + sHtEventCohNodeDiscovered evt = {sizeof(sHtEventCohNodeDiscovered), + currentNode, + currentLink, + token}; + + pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_INFO, + HT_EVENT_COH_NODE_DISCOVERED, + (u8 *)&evt); + } + } + + if (pDat->TotalLinks == MAX_PLATFORM_LINKS) + { + /* + * Exceeded our capacity to describe all coherent links found in the system. + * Error strategy: + * Auto recovery is not possible because data space is already all used. + * If the callback is not implemented or returns we will continue to initialize + * the fabric we are capable of representing, adding no more nodes or links. + * This should yield a bootable topology, but likely not the one intended. + * We cannot continue discovery, there may not be any way to route a new + * node back to the BSP if we can't add links to our representation of the system. + */ + if (pDat->HtBlock->AMD_CB_EventNotify) + { + sHtEventCohLinkExceed evt = {sizeof(sHtEventCohLinkExceed), + currentNode, + currentLink, + token, + pDat->NodesDiscovered, + pDat->nb->maxLinks}; + + pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR, + HT_EVENT_COH_LINK_EXCEED, + (u8 *)&evt); + } + /* Force link and node loops to halt */ + STOP_HERE; + currentNode = pDat->NodesDiscovered; + break; + } + + pDat->PortList[pDat->TotalLinks*2].Type = PORTLIST_TYPE_CPU; + pDat->PortList[pDat->TotalLinks*2].Link = currentLink; + pDat->PortList[pDat->TotalLinks*2].NodeID = currentNode; + + pDat->PortList[pDat->TotalLinks*2+1].Type = PORTLIST_TYPE_CPU; + pDat->PortList[pDat->TotalLinks*2+1].Link = pDat->nb->readDefLnk(currentNode+1, pDat->nb); + pDat->PortList[pDat->TotalLinks*2+1].NodeID = token; + + pDat->TotalLinks++; + + if ( !pDat->sysMatrix[currentNode][token] ) + { + pDat->sysDegree[currentNode]++; + pDat->sysDegree[token]++; + pDat->sysMatrix[currentNode][token] = TRUE; + pDat->sysMatrix[token][currentNode] = TRUE; + } + } + currentNode++; + } +} + + +/*************************************************************************** + *** ISOMORPHISM BASED ROUTING TABLE GENERATION CODE *** + ***************************************************************************/ + +/*---------------------------------------------------------------------------------------- + * BOOL + * isoMorph(u8 i, sMainData *pDat) + * + * Description: + * Is graphA isomorphic to graphB? + * if this function returns true, then Perm will contain the permutation + * required to transform graphB into graphA. + * We also use the degree of each node, that is the number of connections it has, to + * speed up rejection of non-isomorphic graphs (if there is a node in graphA with n + * connections, there must be at least one unmatched in graphB with n connections). + * + * Parameters: + * @param[in] u8 i = the discovered node which we are trying to match + * with a permutation the topology + * @param[in]/@param[out] sMainData* pDat = our global state, degree and adjacency matrix, + * output a permutation if successful + * @param[out] BOOL results = the graphs are (or are not) isomorphic + * --------------------------------------------------------------------------------------- + */ +BOOL isoMorph(u8 i, sMainData *pDat) +{ + u8 j, k; + u8 nodecnt; + + /* We have only been called if nodecnt == pSelected->size ! */ + nodecnt = pDat->NodesDiscovered+1; + + if (i != nodecnt) + { + // Keep building the permutation + for (j = 0; j < nodecnt; j++) + { + // Make sure the degree matches + if (pDat->sysDegree[i] != pDat->dbDegree[j]) + continue; + + // Make sure that j hasn't been used yet (ought to use a "used" + // array instead, might be faster) + for (k = 0; k < i; k++) + { + if (pDat->Perm[k] == j) + break; + } + if (k != i) + continue; + pDat->Perm[i] = j; + if (isoMorph(i+1, pDat)) + return TRUE; + } + return FALSE; + } else { + // Test to see if the permutation is isomorphic + for (j = 0; j < nodecnt; j++) + { + for (k = 0; k < nodecnt; k++) + { + if ( pDat->sysMatrix[j][k] != + pDat->dbMatrix[pDat->Perm[j]][pDat->Perm[k]] ) + return FALSE; + } + } + return TRUE; + } +} + + +/*---------------------------------------------------------------------------------------- + * void + * lookupComputeAndLoadRoutingTables(sMainData *pDat) + * + * Description: + * Using the description of the fabric topology we discovered, try to find a match + * among the supported topologies. A supported topology description matches + * the discovered fabric if the nodes can be matched in such a way that all the nodes connected + * in one set are exactly the nodes connected in the other (formally, that the graphs are + * isomorphic). Which links are used is not really important to matching. If the graphs + * match, then there is a permutation of one that translates the node positions and linkages + * to the other. + * + * In order to make the isomorphism test efficient, we test for matched number of nodes + * (a 4 node fabric is not isomorphic to a 2 node topology), and provide degrees of nodes + * to the isomorphism test. + * + * The generic routing table solution for any topology is predetermined and represented + * as part of the topology. The permutation we computed tells us how to interpret the + * routing onto the fabric we discovered. We do this working backward from the last + * node discovered to the BSP, writing the routing tables as we go. + * + * Parameters: + * @param[in] sMainData* pDat = our global state, the discovered fabric, + * @param[out] degree matrix, permutation + * --------------------------------------------------------------------------------------- + */ +void lookupComputeAndLoadRoutingTables(sMainData *pDat) +{ + u8 **pTopologyList; + u8 *pSelected; + + int i, j, k, size; + + size = pDat->NodesDiscovered + 1; + /* Use the provided topology list or the internal, default one. */ + pTopologyList = pDat->HtBlock->topolist; + if (pTopologyList == NULL) + { + getAmdTopolist(&pTopologyList); + } + + pSelected = *pTopologyList; + while (pSelected != NULL) + { + if (graphHowManyNodes(pSelected) == size) + { + // Build Degree vector and Adjency Matrix for this entry + for (i = 0; i < size; i++) + { + pDat->dbDegree[i] = 0; + for (j = 0; j < size; j++) + { + if (graphIsAdjacent(pSelected, i, j)) + { + pDat->dbMatrix[i][j] = 1; + pDat->dbDegree[i]++; + } + else + { + pDat->dbMatrix[i][j] = 0; + } + } + } + if (isoMorph(0, pDat)) + break; // A matching topology was found + } + + pTopologyList++; + pSelected = *pTopologyList; + } + + if (pSelected != NULL) + { + // Compute the reverse Permutation + for (i = 0; i < size; i++) + { + pDat->ReversePerm[pDat->Perm[i]] = i; + } + + // Start with the last discovered node, and move towards the BSP + for (i = size-1; i >= 0; i--) + { + for (j = 0; j < size; j++) + { + u8 ReqTargetLink, RspTargetLink; + u8 ReqTargetNode, RspTargetNode; + + u8 AbstractBcTargetNodes = graphGetBc(pSelected, pDat->Perm[i], pDat->Perm[j]); + u32 BcTargetLinks = 0; + + for (k = 0; k < MAX_NODES; k++) + { + if (AbstractBcTargetNodes & ((u32)1<<k)) + { + BcTargetLinks |= (u32)1 << convertNodeToLink(i, pDat->ReversePerm[k], pDat); + } + } + + if (i == j) + { + ReqTargetLink = ROUTETOSELF; + RspTargetLink = ROUTETOSELF; + } + else + { + ReqTargetNode = graphGetReq(pSelected, pDat->Perm[i], pDat->Perm[j]); + ReqTargetLink = convertNodeToLink(i, pDat->ReversePerm[ReqTargetNode], pDat); + + RspTargetNode = graphGetRsp(pSelected, pDat->Perm[i], pDat->Perm[j]); + RspTargetLink = convertNodeToLink(i, pDat->ReversePerm[RspTargetNode], pDat); + } + + pDat->nb->writeFullRoutingTable(i, j, ReqTargetLink, RspTargetLink, BcTargetLinks, pDat->nb); + } + /* Clean up discovery 'footprint' that otherwise remains in the routing table. It didn't hurt + * anything, but might cause confusion during debug and validation. Do this by setting the + * route back to all self routes. Since it's the node that would be one more than actually installed, + * this only applies if less than maxNodes were found. + */ + if (size < pDat->nb->maxNodes) + { + pDat->nb->writeFullRoutingTable(i, size, ROUTETOSELF, ROUTETOSELF, 0, pDat->nb); + } + } + + } + else + { + /* + * No Matching Topology was found + * Error Strategy: + * Auto recovery doesn't seem likely, Force boot as 1P. + * For reporting, logging, provide number of nodes + * If not implemented or returns, boot as BSP uniprocessor. + */ + if (pDat->HtBlock->AMD_CB_EventNotify) + { + sHtEventCohNoTopology evt = {sizeof(sHtEventCohNoTopology), + pDat->NodesDiscovered}; + + pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR, + HT_EVENT_COH_NO_TOPOLOGY, + (u8 *)&evt); + } + STOP_HERE; + /* Force 1P */ + pDat->NodesDiscovered = 0; + pDat->TotalLinks = 0; + pDat->nb->enableRoutingTables(0, pDat->nb); + } +} +#endif /* HT_BUILD_NC_ONLY */ + + +/*---------------------------------------------------------------------------------------- + * void + * finializeCoherentInit(sMainData *pDat) + * + * Description: + * Find the total number of cores and update the number of nodes and cores in all cpus. + * Limit cpu config access to installed cpus. + * + * Parameters: + * @param[in] sMainData* pDat = our global state, number of nodes discovered. + * --------------------------------------------------------------------------------------- + */ +void finializeCoherentInit(sMainData *pDat) +{ + u8 curNode; + + u8 totalCores = 0; + for (curNode = 0; curNode < pDat->NodesDiscovered+1; curNode++) + { + totalCores += pDat->nb->getNumCoresOnNode(curNode, pDat->nb); + } + + for (curNode = 0; curNode < pDat->NodesDiscovered+1; curNode++) + { + pDat->nb->setTotalNodesAndCores(curNode, pDat->NodesDiscovered+1, totalCores, pDat->nb); + } + + for (curNode = 0; curNode < pDat->NodesDiscovered+1; curNode++) + { + pDat->nb->limitNodes(curNode, pDat->nb); + } + +} + +/*---------------------------------------------------------------------------------------- + * void + * coherentInit(sMainData *pDat) + * + * Description: + * Perform discovery and initialization of the coherent fabric. + * + * Parameters: + * @param[in] sMainData* pDat = our global state + * --------------------------------------------------------------------------------------- + */ +void coherentInit(sMainData *pDat) +{ + int i, j; + +#ifdef HT_BUILD_NC_ONLY + /* Replace discovery process with: + * No other nodes, no coherent links + * Enable routing tables on currentNode, for power on self route + */ + pDat->NodesDiscovered = 0; + pDat->TotalLinks = 0; + pDat->nb->enableRoutingTables(0, pDat->nb); +#else + pDat->NodesDiscovered = 0; + pDat->TotalLinks = 0; + for (i = 0; i < MAX_NODES; i++) + { + pDat->sysDegree[i] = 0; + for (j = 0; j < MAX_NODES; j++) + { + pDat->sysMatrix[i][j] = 0; + } + } + + htDiscoveryFloodFill(pDat); + lookupComputeAndLoadRoutingTables(pDat); +#endif + finializeCoherentInit(pDat); +} + +/*************************************************************************** + *** Non-coherent init code *** + *** Algorithms *** + ***************************************************************************/ +/*---------------------------------------------------------------------------------------- + * void + * processLink(u8 node, u8 link, sMainData *pDat) + * + * Description: + * Process a non-coherent link, enabling a range of bus numbers, and setting the device + * ID for all devices found + * + * Parameters: + * @param[in] u8 node = Node on which to process nc init + * @param[in] u8 link = The non-coherent link on that node + * @param[in] sMainData* pDat = our global state + * --------------------------------------------------------------------------------------- + */ +void processLink(u8 node, u8 link, sMainData *pDat) +{ + u8 secBus, subBus; + u32 currentBUID; + u32 temp; + u32 unitIDcnt; + SBDFO currentPtr; + u8 depth; + u8 *pSwapPtr; + + SBDFO lastSBDFO = ILLEGAL_SBDFO; + u8 lastLink = 0; + + ASSERT(node < pDat->nb->maxNodes && link < pDat->nb->maxLinks); + + if ((pDat->HtBlock->AMD_CB_OverrideBusNumbers == NULL) + || !pDat->HtBlock->AMD_CB_OverrideBusNumbers(node, link, &secBus, &subBus)) + { + /* Assign Bus numbers */ + if (pDat->AutoBusCurrent >= pDat->HtBlock->AutoBusMax) + { + /* If we run out of Bus Numbers notify, if call back unimplemented or if it + * returns, skip this chain + */ + if (pDat->HtBlock->AMD_CB_EventNotify) + { + sHTEventNcohBusMaxExceed evt = {sizeof(sHTEventNcohBusMaxExceed), node, link, pDat->AutoBusCurrent}; + + pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,HT_EVENT_NCOH_BUS_MAX_EXCEED,(u8 *)&evt); + } + STOP_HERE; + return; + } + + if (pDat->UsedCfgMapEntires >= 4) + { + /* If we have used all the PCI Config maps we can't add another chain. + * Notify and if call back is unimplemented or returns, skip this chain. + */ + if (pDat->HtBlock->AMD_CB_EventNotify) + { + sHtEventNcohCfgMapExceed evt = {sizeof(sHtEventNcohCfgMapExceed), node, link}; + + pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR, + HT_EVENT_NCOH_CFG_MAP_EXCEED, + (u8 *)&evt); + } + STOP_HERE; + return; + } + + secBus = pDat->AutoBusCurrent; + subBus = secBus + pDat->HtBlock->AutoBusIncrement-1; + pDat->AutoBusCurrent += pDat->HtBlock->AutoBusIncrement; + } + + pDat->nb->setCFGAddrMap(pDat->UsedCfgMapEntires, secBus, subBus, node, link, pDat, pDat->nb); + pDat->UsedCfgMapEntires++; + + if ((pDat->HtBlock->AMD_CB_ManualBUIDSwapList != NULL) + && pDat->HtBlock->AMD_CB_ManualBUIDSwapList(node, link, &pSwapPtr)) + { + /* Manual non-coherent BUID assignment */ + + /* Assign BUID's per manual override */ + while (*pSwapPtr != 0xFF) + { + currentPtr = MAKE_SBDFO(0, secBus, *pSwapPtr, 0, 0); + pSwapPtr++; + + do + { + AmdPCIFindNextCap(¤tPtr); + ASSERT(currentPtr != ILLEGAL_SBDFO); + AmdPCIRead(currentPtr, &temp); + } while (!IS_HT_SLAVE_CAPABILITY(temp)); + + currentBUID = *pSwapPtr; + pSwapPtr++; + AmdPCIWriteBits(currentPtr, 20, 16, ¤tBUID); + } + + /* Build chain of devices */ + depth = 0; + pSwapPtr++; + while (*pSwapPtr != 0xFF) + { + pDat->PortList[pDat->TotalLinks*2].NodeID = node; + if (depth == 0) + { + pDat->PortList[pDat->TotalLinks*2].Type = PORTLIST_TYPE_CPU; + pDat->PortList[pDat->TotalLinks*2].Link = link; + } + else + { + pDat->PortList[pDat->TotalLinks*2].Type = PORTLIST_TYPE_IO; + pDat->PortList[pDat->TotalLinks*2].Link = 1-lastLink; + pDat->PortList[pDat->TotalLinks*2].HostLink = link; + pDat->PortList[pDat->TotalLinks*2].HostDepth = depth-1; + pDat->PortList[pDat->TotalLinks*2].Pointer = lastSBDFO; + } + + pDat->PortList[pDat->TotalLinks*2+1].Type = PORTLIST_TYPE_IO; + pDat->PortList[pDat->TotalLinks*2+1].NodeID = node; + pDat->PortList[pDat->TotalLinks*2+1].HostLink = link; + pDat->PortList[pDat->TotalLinks*2+1].HostDepth = depth; + + currentPtr = MAKE_SBDFO(0, secBus, (*pSwapPtr & 0x3F), 0, 0); + do + { + AmdPCIFindNextCap(¤tPtr); + ASSERT(currentPtr != ILLEGAL_SBDFO); + AmdPCIRead(currentPtr, &temp); + } while (!IS_HT_SLAVE_CAPABILITY(temp)); + pDat->PortList[pDat->TotalLinks*2+1].Pointer = currentPtr; + lastSBDFO = currentPtr; + + /* Bit 6 indicates whether orientation override is desired. + * Bit 7 indicates the upstream link if overriding. + */ + /* assert catches at least the one known incorrect setting */ + ASSERT ((*pSwapPtr & 0x40) || (!(*pSwapPtr & 0x80))); + if (*pSwapPtr & 0x40) + { + /* Override the device's orientation */ + lastLink = *pSwapPtr >> 7; + } + else + { + /* Detect the device's orientation */ + AmdPCIReadBits(currentPtr, 26, 26, &temp); + lastLink = (u8)temp; + } + pDat->PortList[pDat->TotalLinks*2+1].Link = lastLink; + + depth++; + pDat->TotalLinks++; + pSwapPtr++; + } + } + else + { + /* Automatic non-coherent device detection */ + depth = 0; + currentBUID = 1; + while (1) + { + currentPtr = MAKE_SBDFO(0, secBus, 0, 0, 0); + + AmdPCIRead(currentPtr, &temp); + if (temp == 0xFFFFFFFF) + /* No device found at currentPtr */ + break; + + if (pDat->TotalLinks == MAX_PLATFORM_LINKS) + { + /* + * Exceeded our capacity to describe all non-coherent links found in the system. + * Error strategy: + * Auto recovery is not possible because data space is already all used. + */ + if (pDat->HtBlock->AMD_CB_EventNotify) + { + sHtEventNcohLinkExceed evt = {sizeof(sHtEventNcohLinkExceed), + node, + link, + depth, + pDat->nb->maxLinks}; + + pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR, + HT_EVENT_NCOH_LINK_EXCEED, + (u8 *)&evt); + } + /* Force link loop to halt */ + STOP_HERE; + break; + } + + pDat->PortList[pDat->TotalLinks*2].NodeID = node; + if (depth == 0) + { + pDat->PortList[pDat->TotalLinks*2].Type = PORTLIST_TYPE_CPU; + pDat->PortList[pDat->TotalLinks*2].Link = link; + } + else + { + pDat->PortList[pDat->TotalLinks*2].Type = PORTLIST_TYPE_IO; + pDat->PortList[pDat->TotalLinks*2].Link = 1-lastLink; + pDat->PortList[pDat->TotalLinks*2].HostLink = link; + pDat->PortList[pDat->TotalLinks*2].HostDepth = depth-1; + pDat->PortList[pDat->TotalLinks*2].Pointer = lastSBDFO; + } + + pDat->PortList[pDat->TotalLinks*2+1].Type = PORTLIST_TYPE_IO; + pDat->PortList[pDat->TotalLinks*2+1].NodeID = node; + pDat->PortList[pDat->TotalLinks*2+1].HostLink = link; + pDat->PortList[pDat->TotalLinks*2+1].HostDepth = depth; + + do + { + AmdPCIFindNextCap(¤tPtr); + ASSERT(currentPtr != ILLEGAL_SBDFO); + AmdPCIRead(currentPtr, &temp); + } while (!IS_HT_SLAVE_CAPABILITY(temp)); + + AmdPCIReadBits(currentPtr, 25, 21, &unitIDcnt); + if ((unitIDcnt + currentBUID > 31) || ((secBus == 0) && (unitIDcnt + currentBUID > 24))) + { + /* An error handler for the case where we run out of BUID's on a chain */ + if (pDat->HtBlock->AMD_CB_EventNotify) + { + sHtEventNcohBuidExceed evt = {sizeof(sHtEventNcohBuidExceed), + node, link, depth, (u8)currentBUID, (u8)unitIDcnt}; + + pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,HT_EVENT_NCOH_BUID_EXCEED,(u8 *)&evt); + } + STOP_HERE; + break; + } + AmdPCIWriteBits(currentPtr, 20, 16, ¤tBUID); + + + currentPtr += MAKE_SBDFO(0, 0, currentBUID, 0, 0); + AmdPCIReadBits(currentPtr, 20, 16, &temp); + if (temp != currentBUID) + { + /* An error handler for this critical error */ + if (pDat->HtBlock->AMD_CB_EventNotify) + { + sHtEventNcohDeviceFailed evt = {sizeof(sHtEventNcohDeviceFailed), + node, link, depth, (u8)currentBUID}; + + pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,HT_EVENT_NCOH_DEVICE_FAILED,(u8 *)&evt); + } + STOP_HERE; + break; + } + + AmdPCIReadBits(currentPtr, 26, 26, &temp); + pDat->PortList[pDat->TotalLinks*2+1].Link = (u8)temp; + pDat->PortList[pDat->TotalLinks*2+1].Pointer = currentPtr; + + lastLink = (u8)temp; + lastSBDFO = currentPtr; + + depth++; + pDat->TotalLinks++; + currentBUID += unitIDcnt; + } + if (pDat->HtBlock->AMD_CB_EventNotify) + { + /* Provide information on automatic device results */ + sHtEventNcohAutoDepth evt = {sizeof(sHtEventNcohAutoDepth), node, link, (depth - 1)}; + + pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_INFO,HT_EVENT_NCOH_AUTO_DEPTH,(u8 *)&evt); + } + } +} + + +/*---------------------------------------------------------------------------------------- + * void + * ncInit(sMainData *pDat) + * + * Description: + * Initialize the non-coherent fabric. Begin with the compat link on the BSP, then + * find and initialize all other non-coherent chains. + * + * Parameters: + * @param[in] sMainData* pDat = our global state + * --------------------------------------------------------------------------------------- + */ +void ncInit(sMainData *pDat) +{ + u8 node, link; + u8 compatLink; + + compatLink = pDat->nb->readSbLink(pDat->nb); + processLink(0, compatLink, pDat); + + for (node = 0; node <= pDat->NodesDiscovered; node++) + { + for (link = 0; link < pDat->nb->maxLinks; link++) + { + if (pDat->HtBlock->AMD_CB_IgnoreLink && pDat->HtBlock->AMD_CB_IgnoreLink(node, link)) + continue; // Skip the link + + if (node == 0 && link == compatLink) + continue; + + if (pDat->nb->readTrueLinkFailStatus(node, link, pDat, pDat->nb)) + continue; + + if (pDat->nb->verifyLinkIsNonCoherent(node, link, pDat->nb)) + processLink(node, link, pDat); + } + } +} + +/*************************************************************************** + *** Link Optimization *** + ***************************************************************************/ + +/*---------------------------------------------------------------------------------------- + * void + * regangLinks(sMainData *pDat) + * + * Description: + * Test the sublinks of a link to see if they qualify to be reganged. If they do, + * update the port list data to indicate that this should be done. Note that no + * actual hardware state is changed in this routine. + * + * Parameters: + * @param[in,out] sMainData* pDat = our global state + * --------------------------------------------------------------------------------------- + */ +void regangLinks(sMainData *pDat) +{ +#ifndef HT_BUILD_NC_ONLY + u8 i, j; + for (i = 0; i < pDat->TotalLinks*2; i += 2) + { + ASSERT(pDat->PortList[i].Type < 2 && pDat->PortList[i].Link < pDat->nb->maxLinks); // Data validation + ASSERT(pDat->PortList[i+1].Type < 2 && pDat->PortList[i+1].Link < pDat->nb->maxLinks); // data validation + ASSERT(!(pDat->PortList[i].Type == PORTLIST_TYPE_IO && pDat->PortList[i+1].Type == PORTLIST_TYPE_CPU)); // ensure src is closer to the bsp than dst + + /* Regang is false unless we pass all conditions below */ + pDat->PortList[i].SelRegang = FALSE; + pDat->PortList[i+1].SelRegang = FALSE; + + if ( (pDat->PortList[i].Type != PORTLIST_TYPE_CPU) || (pDat->PortList[i+1].Type != PORTLIST_TYPE_CPU)) + continue; // Only process cpu to cpu links + + for (j = i+2; j < pDat->TotalLinks*2; j += 2) + { + if ( (pDat->PortList[j].Type != PORTLIST_TYPE_CPU) || (pDat->PortList[j+1].Type != PORTLIST_TYPE_CPU) ) + continue; // Only process cpu to cpu links + + if (pDat->PortList[i].NodeID != pDat->PortList[j].NodeID) + continue; // Links must be from the same source + + if (pDat->PortList[i+1].NodeID != pDat->PortList[j+1].NodeID) + continue; // Link must be to the same target + + if ((pDat->PortList[i].Link & 3) != (pDat->PortList[j].Link & 3)) + continue; // Ensure same source base port + + if ((pDat->PortList[i+1].Link & 3) != (pDat->PortList[j+1].Link & 3)) + continue; // Ensure same destination base port + + if ((pDat->PortList[i].Link & 4) != (pDat->PortList[i+1].Link & 4)) + continue; // Ensure sublink0 routes to sublink0 + + ASSERT((pDat->PortList[j].Link & 4) == (pDat->PortList[j+1].Link & 4)); // (therefore sublink1 routes to sublink1) + + if (pDat->HtBlock->AMD_CB_SkipRegang && + pDat->HtBlock->AMD_CB_SkipRegang(pDat->PortList[i].NodeID, + pDat->PortList[i].Link & 0x03, + pDat->PortList[i+1].NodeID, + pDat->PortList[i+1].Link & 0x03)) + { + continue; // Skip regang + } + + + pDat->PortList[i].Link &= 0x03; // Force to point to sublink0 + pDat->PortList[i+1].Link &= 0x03; + pDat->PortList[i].SelRegang = TRUE; // Enable link reganging + pDat->PortList[i+1].SelRegang = TRUE; + pDat->PortList[i].PrvWidthOutCap = HT_WIDTH_16_BITS; + pDat->PortList[i+1].PrvWidthOutCap = HT_WIDTH_16_BITS; + pDat->PortList[i].PrvWidthInCap = HT_WIDTH_16_BITS; + pDat->PortList[i+1].PrvWidthInCap = HT_WIDTH_16_BITS; + + // Delete PortList[j, j+1], slow but easy to debug implementation + pDat->TotalLinks--; + Amdmemcpy(&(pDat->PortList[j]), &(pDat->PortList[j+2]), sizeof(sPortDescriptor)*(pDat->TotalLinks*2-j)); + Amdmemset(&(pDat->PortList[pDat->TotalLinks*2]), INVALID_LINK, sizeof(sPortDescriptor)*2); + + ////High performance, but would make debuging harder due to 'shuffling' of the records + ////Amdmemcpy(PortList[TotalPorts-2], PortList[j], SIZEOF(sPortDescriptor)*2); + ////TotalPorts -=2; + + break; // Exit loop, advance to PortList[i+2] + } + } +#endif /* HT_BUILD_NC_ONLY */ +} + +/*---------------------------------------------------------------------------------------- + * void + * selectOptimalWidthAndFrequency(sMainData *pDat) + * + * Description: + * For all links: + * Examine both sides of a link and determine the optimal frequency and width, + * taking into account externally provided limits and enforcing any other limit + * or matching rules as applicable except sublink balancing. Update the port + * list date with the optimal settings. + * Note no hardware state changes in this routine. + * + * Parameters: + * @param[in,out] sMainData* pDat = our global state, port list data + * --------------------------------------------------------------------------------------- + */ +void selectOptimalWidthAndFrequency(sMainData *pDat) +{ + u8 i, j; + u32 temp; + u16 cbPCBFreqLimit; + u8 cbPCBABDownstreamWidth; + u8 cbPCBBAUpstreamWidth; + + for (i = 0; i < pDat->TotalLinks*2; i += 2) + { + cbPCBFreqLimit = 0xFFFF; + cbPCBABDownstreamWidth = 16; + cbPCBBAUpstreamWidth = 16; + + if ( (pDat->PortList[i].Type == PORTLIST_TYPE_CPU) && (pDat->PortList[i+1].Type == PORTLIST_TYPE_CPU)) + { + if (pDat->HtBlock->AMD_CB_Cpu2CpuPCBLimits) + { + pDat->HtBlock->AMD_CB_Cpu2CpuPCBLimits( + pDat->PortList[i].NodeID, + pDat->PortList[i].Link, + pDat->PortList[i+1].NodeID, + pDat->PortList[i+1].Link, + &cbPCBABDownstreamWidth, + &cbPCBBAUpstreamWidth, &cbPCBFreqLimit + ); + } + } + else + { + if (pDat->HtBlock->AMD_CB_IOPCBLimits) + { + pDat->HtBlock->AMD_CB_IOPCBLimits( + pDat->PortList[i+1].NodeID, + pDat->PortList[i+1].HostLink, + pDat->PortList[i+1].HostDepth, + &cbPCBABDownstreamWidth, + &cbPCBBAUpstreamWidth, &cbPCBFreqLimit + ); + } + } + + + temp = pDat->PortList[i].PrvFrequencyCap; + temp &= pDat->PortList[i+1].PrvFrequencyCap; + temp &= cbPCBFreqLimit; + pDat->PortList[i].CompositeFrequencyCap = (u16)temp; + pDat->PortList[i+1].CompositeFrequencyCap = (u16)temp; + + ASSERT (temp != 0); + for (j = 15; ; j--) + { + if (temp & ((u32)1 << j)) + break; + } + + pDat->PortList[i].SelFrequency = j; + pDat->PortList[i+1].SelFrequency = j; + + temp = pDat->PortList[i].PrvWidthOutCap; + if (pDat->PortList[i+1].PrvWidthInCap < temp) + temp = pDat->PortList[i+1].PrvWidthInCap; + if (cbPCBABDownstreamWidth < temp) + temp = cbPCBABDownstreamWidth; + pDat->PortList[i].SelWidthOut = (u8)temp; + pDat->PortList[i+1].SelWidthIn = (u8)temp; + + temp = pDat->PortList[i].PrvWidthInCap; + if (pDat->PortList[i+1].PrvWidthOutCap < temp) + temp = pDat->PortList[i+1].PrvWidthOutCap; + if (cbPCBBAUpstreamWidth < temp) + temp = cbPCBBAUpstreamWidth; + pDat->PortList[i].SelWidthIn = (u8)temp; + pDat->PortList[i+1].SelWidthOut = (u8)temp; + + } +} + +/*---------------------------------------------------------------------------------------- + * void + * hammerSublinkFixup(sMainData *pDat) + * + * Description: + * Iterate through all links, checking the frequency of each sublink pair. Make the + * adjustment to the port list data so that the frequencies are at a valid ratio, + * reducing frequency as needed to achieve this. (All links support the minimum 200 MHz + * frequency.) Repeat the above until no adjustments are needed. + * Note no hardware state changes in this routine. + * + * Parameters: + * @param[in,out] sMainData* pDat = our global state, link state and port list + * --------------------------------------------------------------------------------------- + */ +void hammerSublinkFixup(sMainData *pDat) +{ +#ifndef HT_BUILD_NC_ONLY + u8 i, j, k; + BOOL changes, downgrade; + + u8 hiIndex; + u8 hiFreq, loFreq; + + u32 temp; + + do + { + changes = FALSE; + for (i = 0; i < pDat->TotalLinks*2; i++) + { + if (pDat->PortList[i].Type != PORTLIST_TYPE_CPU) // Must be a CPU link + continue; + if (pDat->PortList[i].Link < 4) // Only look for for sublink1's + continue; + + for (j = 0; j < pDat->TotalLinks*2; j++) + { + // Step 1. Find the matching sublink0 + if (pDat->PortList[j].Type != PORTLIST_TYPE_CPU) + continue; + if (pDat->PortList[j].NodeID != pDat->PortList[i].NodeID) + continue; + if (pDat->PortList[j].Link != (pDat->PortList[i].Link & 0x03)) + continue; + + // Step 2. Check for an illegal frequency ratio + if (pDat->PortList[i].SelFrequency >= pDat->PortList[j].SelFrequency) + { + hiIndex = i; + hiFreq = pDat->PortList[i].SelFrequency; + loFreq = pDat->PortList[j].SelFrequency; + } + else + { + hiIndex = j; + hiFreq = pDat->PortList[j].SelFrequency; + loFreq = pDat->PortList[i].SelFrequency; + } + + if (hiFreq == loFreq) + break; // The frequencies are 1:1, no need to do anything + + downgrade = FALSE; + + if (hiFreq == 13) + { + if ((loFreq != 7) && //{13, 7} 2400MHz / 1200MHz 2:1 + (loFreq != 4) && //{13, 4} 2400MHz / 600MHz 4:1 + (loFreq != 2) ) //{13, 2} 2400MHz / 400MHz 6:1 + downgrade = TRUE; + } + else if (hiFreq == 11) + { + if ((loFreq != 6)) //{11, 6} 2000MHz / 1000MHz 2:1 + downgrade = TRUE; + } + else if (hiFreq == 9) + { + if ((loFreq != 5) && //{ 9, 5} 1600MHz / 800MHz 2:1 + (loFreq != 2) && //{ 9, 2} 1600MHz / 400MHz 4:1 + (loFreq != 0) ) //{ 9, 0} 1600MHz / 200Mhz 8:1 + downgrade = TRUE; + } + else if (hiFreq == 7) + { + if ((loFreq != 4) && //{ 7, 4} 1200MHz / 600MHz 2:1 + (loFreq != 0) ) //{ 7, 0} 1200MHz / 200MHz 6:1 + downgrade = TRUE; + } + else if (hiFreq == 5) + { + if ((loFreq != 2) && //{ 5, 2} 800MHz / 400MHz 2:1 + (loFreq != 0) ) //{ 5, 0} 800MHz / 200MHz 4:1 + downgrade = TRUE; + } + else if (hiFreq == 2) + { + if ((loFreq != 0)) //{ 2, 0} 400MHz / 200MHz 2:1 + downgrade = TRUE; + } + else + { + downgrade = TRUE; // no legal ratios for hiFreq + } + + // Step 3. Downgrade the higher of the two frequencies, and set nochanges to FALSE + if (downgrade) + { + // Although the problem was with the port specified by hiIndex, we need to + // downgrade both ends of the link. + hiIndex = hiIndex & 0xFE; // Select the 'upstream' (i.e. even) port + + temp = pDat->PortList[hiIndex].CompositeFrequencyCap; + + // Remove hiFreq from the list of valid frequencies + temp = temp & ~((u32)1 << hiFreq); + ASSERT (temp != 0); + pDat->PortList[hiIndex].CompositeFrequencyCap = (u16)temp; + pDat->PortList[hiIndex+1].CompositeFrequencyCap = (u16)temp; + + for (k = 15; ; k--) + { + if (temp & ((u32)1 << k)) + break; + } + + pDat->PortList[hiIndex].SelFrequency = k; + pDat->PortList[hiIndex+1].SelFrequency = k; + + changes = TRUE; + } + } + } + } while (changes); // Repeat until a valid configuration is reached +#endif /* HT_BUILD_NC_ONLY */ +} + +/*---------------------------------------------------------------------------------------- + * void + * linkOptimization(sMainData *pDat) + * + * Description: + * Based on link capabilities, apply optimization rules to come up with the real best + * settings, including several external limit decision from call backs. This includes + * handling of sublinks. Finally, after the port list data is updated, set the hardware + * state for all links. + * + * Parameters: + * @param[in] sMainData* pDat = our global state + * --------------------------------------------------------------------------------------- + */ +void linkOptimization(sMainData *pDat) +{ + pDat->nb->gatherLinkData(pDat, pDat->nb); + regangLinks(pDat); + selectOptimalWidthAndFrequency(pDat); + hammerSublinkFixup(pDat); + pDat->nb->setLinkData(pDat, pDat->nb); +} + + +/*---------------------------------------------------------------------------------------- + * void + * trafficDistribution(sMainData *pDat) + * + * Description: + * In the case of a two node system with both sublinks used, enable the traffic + * distribution feature. + * + * Parameters: + * @param[in] sMainData* pDat = our global state, port list data + * --------------------------------------------------------------------------------------- + */ +void trafficDistribution(sMainData *pDat) +{ +#ifndef HT_BUILD_NC_ONLY + u32 links01, links10; + u8 linkCount; + u8 i; + + // Traffic Distribution is only used when there are exactly two nodes in the system + if (pDat->NodesDiscovered+1 != 2) + return; + + links01 = 0; + links10 = 0; + linkCount = 0; + for (i = 0; i < pDat->TotalLinks*2; i += 2) + { + if ((pDat->PortList[i].Type == PORTLIST_TYPE_CPU) && (pDat->PortList[i+1].Type == PORTLIST_TYPE_CPU)) + { + links01 |= (u32)1 << pDat->PortList[i].Link; + links10 |= (u32)1 << pDat->PortList[i+1].Link; + linkCount++; + } + } + ASSERT(linkCount != 0); + if (linkCount == 1) + return; // Don't setup Traffic Distribution if only one link is being used + + pDat->nb->writeTrafficDistribution(links01, links10, pDat->nb); +#endif /* HT_BUILD_NC_ONLY */ +} + +/*---------------------------------------------------------------------------------------- + * void + * tuning(sMainData *pDat) + * + * Description: + * Handle system and performance tunings, such as traffic distribution, fifo and + * buffer tuning, and special config tunings. + * + * Parameters: + * @param[in] sMainData* pDat = our global state, port list data + * --------------------------------------------------------------------------------------- + */ +void tuning(sMainData *pDat) +{ + u8 i; + + /* See if traffic distribution can be done and do it if so + * or allow system specific customization + */ + if ((pDat->HtBlock->AMD_CB_CustomizeTrafficDistribution == NULL) + || !pDat->HtBlock->AMD_CB_CustomizeTrafficDistribution()) + { + trafficDistribution(pDat); + } + + /* For each node, invoke northbridge specific buffer tunings or + * system specific customizations. + */ + for (i=0; i < pDat->NodesDiscovered + 1; i++) + { + if ((pDat->HtBlock->AMD_CB_CustomizeBuffers == NULL) + || !pDat->HtBlock->AMD_CB_CustomizeBuffers(i)) + { + pDat->nb->bufferOptimizations(i, pDat, pDat->nb); + } + } +} + +/*---------------------------------------------------------------------------------------- + * BOOL + * isSanityCheckOk() + * + * Description: + * Perform any general sanity checks which should prevent HT from running if they fail. + * Currently only the "Must run on BSP only" check. + * + * Parameters: + * @param[out] result BOOL = true if check is ok, false if it failed + * --------------------------------------------------------------------------------------- + */ +BOOL isSanityCheckOk() +{ + uint64 qValue; + + AmdMSRRead(APIC_Base, &qValue); + + return ((qValue.lo & ((u32)1 << APIC_Base_BSP)) != 0); +} + +/*************************************************************************** + *** HT Initialize *** + ***************************************************************************/ + +/*---------------------------------------------------------------------------------------- + * void + * htInitialize(AMD_HTBLOCK *pBlock) + * + * Description: + * This is the top level external interface for Hypertransport Initialization. + * Create our initial internal state, initialize the coherent fabric, + * initialize the non-coherent chains, and perform any required fabric tuning or + * optimization. + * + * Parameters: + * @param[in] AMD_HTBLOCK* pBlock = Our Initial State including possible + * topologies and routings, non coherent bus + * assignment info, and actual + * wrapper or OEM call back routines. + * --------------------------------------------------------------------------------------- + */ +void amdHtInitialize(AMD_HTBLOCK *pBlock) +{ + sMainData pDat; + cNorthBridge nb; + + if (isSanityCheckOk()) + { + newNorthBridge(0, &nb); + + pDat.HtBlock = pBlock; + pDat.nb = &nb; + pDat.sysMpCap = nb.maxNodes; + nb.isCapable(0, &pDat, pDat.nb); + coherentInit(&pDat); + + pDat.AutoBusCurrent = pBlock->AutoBusStart; + pDat.UsedCfgMapEntires = 0; + ncInit(&pDat); + linkOptimization(&pDat); + tuning(&pDat); + } +} |