2 * This file is part of the coreboot project.
4 * Copyright (C) 2007 Advanced Micro Devices, Inc.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 *----------------------------------------------------------------------------
24 *----------------------------------------------------------------------------
28 #define FILECODE 0xF001
36 /* this is pre-ram so include the required C files here */
41 /*----------------------------------------------------------------------------
42 * DEFINITIONS AND MACROS
44 *----------------------------------------------------------------------------
48 #define FILECODE 0xF001
50 /* APIC defines from amdgesa.inc, which can't be included in to c code. */
51 #define APIC_Base_BSP 8
52 #define APIC_Base 0x1b
54 /*----------------------------------------------------------------------------
55 * TYPEDEFS AND STRUCTURES
57 *----------------------------------------------------------------------------
60 /*----------------------------------------------------------------------------
61 * PROTOTYPES OF LOCAL FUNCTIONS
63 *----------------------------------------------------------------------------
66 /*----------------------------------------------------------------------------
69 *----------------------------------------------------------------------------
72 /*----------------------------------------------------------------------------
75 *----------------------------------------------------------------------------
77 #ifndef HT_BUILD_NC_ONLY
79 **************************************************************************
80 * Routing table decompressor
81 **************************************************************************
85 **************************************************************************
86 * Graph Support routines
87 * These routines provide support for dealing with the graph representation
88 * of the topologies, along with the routing table information for that topology.
89 * The routing information is compressed and these routines currently decompress
90 * 'on the fly'. A graph is represented as a set of routes. All the edges in the
91 * graph are routes; a direct route from node i to node j exists in the graph IFF
92 * there is an edge directly connecting node i to node j. All other routes designate
93 * the edge which the route to that node initially takes, by designating a node
94 * to which a direct connection exists. That is, the route to non-adjacent node j
95 * from node i specifies node k where node i directly connects to node k.
98 * pseudo definition of compressed graph:
102 * uint4 responseRoute;
103 * uint4 requestRoute;
108 * sRoute graph[size][size];
111 **************************************************************************
114 /*----------------------------------------------------------------------------------------
116 * graphHowManyNodes(u8 *graph)
119 * Returns the number of nodes in the compressed graph
122 * @param[in] u8 graph = a compressed graph
123 * @param[out] u8 results = the number of nodes in the graph
124 * ---------------------------------------------------------------------------------------
126 int graphHowManyNodes(u8 *graph)
131 /*----------------------------------------------------------------------------------------
133 * graphIsAdjacent(u8 *graph, u8 nodeA, u8 nodeB)
136 * Returns true if NodeA is directly connected to NodeB, false otherwise
137 * (if NodeA == NodeB also returns false)
138 * Relies on rule that directly connected nodes always route requests directly.
141 * @param[in] u8 graph = the graph to examine
142 * @param[in] u8 nodeA = the node number of the first node
143 * @param[in] u8 nodeB = the node number of the second node
144 * @param[out] BOOL results = true if nodeA connects to nodeB false if not
145 * ---------------------------------------------------------------------------------------
147 BOOL graphIsAdjacent(u8 *graph, u8 nodeA, u8 nodeB)
150 ASSERT(size <= MAX_NODES);
151 ASSERT((nodeA < size) && (nodeB < size));
152 return (graph[1+(nodeA*size+nodeB)*2+1] & 0x0F) == nodeB;
155 /*----------------------------------------------------------------------------------------
157 * graphGetRsp(u8 *graph, u8 nodeA, u8 nodeB)
160 * Returns the graph node used by nodeA to route responses targeted at nodeB.
161 * This will be a node directly connected to nodeA (possibly nodeB itself),
162 * or "Route to Self" if nodeA and nodeB are the same node.
163 * Note that all node numbers are abstract node numbers of the topology graph,
164 * it is the responsibility of the caller to apply any permutation needed.
167 * @param[in] u8 graph = the graph to examine
168 * @param[in] u8 nodeA = the node number of the first node
169 * @param[in] u8 nodeB = the node number of the second node
170 * @param[out] u8 results = The response route node
171 * ---------------------------------------------------------------------------------------
173 u8 graphGetRsp(u8 *graph, u8 nodeA, u8 nodeB)
176 ASSERT(size <= MAX_NODES);
177 ASSERT((nodeA < size) && (nodeB < size));
178 return (graph[1+(nodeA*size+nodeB)*2+1] & 0xF0)>>4;
181 /*----------------------------------------------------------------------------------------
183 * graphGetReq(u8 *graph, u8 nodeA, u8 nodeB)
186 * Returns the graph node used by nodeA to route requests targeted at nodeB.
187 * This will be a node directly connected to nodeA (possibly nodeB itself),
188 * or "Route to Self" if nodeA and nodeB are the same node.
189 * Note that all node numbers are abstract node numbers of the topology graph,
190 * it is the responsibility of the caller to apply any permutation needed.
193 * @param[in] u8 graph = the graph to examine
194 * @param[in] u8 nodeA = the node number of the first node
195 * @param[in] u8 nodeB = the node number of the second node
196 * @param[out] u8 results = The request route node
197 * ---------------------------------------------------------------------------------------
199 u8 graphGetReq(u8 *graph, u8 nodeA, u8 nodeB)
202 ASSERT(size <= MAX_NODES);
203 ASSERT((nodeA < size) && (nodeB < size));
204 return (graph[1+(nodeA*size+nodeB)*2+1] & 0x0F);
207 /*----------------------------------------------------------------------------------------
209 * graphGetBc(unsigned char *graph, int nodeA, int nodeB)
212 * Returns a bit vector of nodes that nodeA should forward a broadcast from
216 * @param[in] u8 graph = the graph to examine
217 * @param[in] u8 nodeA = the node number of the first node
218 * @param[in] u8 nodeB = the node number of the second node
219 * OU u8 results = the broadcast routes for nodeA from nodeB
220 * ---------------------------------------------------------------------------------------
222 u8 graphGetBc(unsigned char *graph, int nodeA, int nodeB)
225 ASSERT(size <= MAX_NODES);
226 ASSERT((nodeA < size) && (nodeB < size));
227 return graph[1+(nodeA*size+nodeB)*2];
231 /***************************************************************************
232 *** GENERIC HYPERTRANSPORT DISCOVERY CODE ***
233 ***************************************************************************/
235 /*----------------------------------------------------------------------------------------
237 * routeFromBSP(u8 targetNode, u8 actualTarget, sMainData *pDat)
240 * Ensure a request / response route from target node to bsp. Since target node is
241 * always a predecessor of actual target node, each node gets a route to actual target
242 * on the link that goes to target. The routing produced by this routine is adequate
243 * for config access during discovery, but NOT for coherency.
246 * @param[in] u8 targetNode = the path to actual target goes through target
247 * @param[in] u8 actualTarget = the ultimate target being routed to
248 * @param[in] sMainData* pDat = our global state, port config info
249 * ---------------------------------------------------------------------------------------
251 void routeFromBSP(u8 targetNode, u8 actualTarget, sMainData *pDat)
253 u8 predecessorNode, predecessorLink, currentPair;
256 return; /* BSP has no predecessor, stop */
258 /* Search for the link that connects targetNode to its predecessor */
260 while (pDat->PortList[currentPair*2+1].NodeID != targetNode)
263 ASSERT(currentPair < pDat->TotalLinks);
266 predecessorNode = pDat->PortList[currentPair*2].NodeID;
267 predecessorLink = pDat->PortList[currentPair*2].Link;
269 /* Recursively call self to ensure the route from the BSP to the Predecessor */
270 /* Node is established */
271 routeFromBSP(predecessorNode, actualTarget, pDat);
273 pDat->nb->writeRoutingTable(predecessorNode, actualTarget, predecessorLink, pDat->nb);
276 /*----------------------------------------------------------------------------------------
278 * convertNodeToLink(u8 srcNode, u8 targetNode, sMainData *pDat)
281 * Return the link on source node which connects to target node
284 * @param[in] u8 srcNode = the source node
285 * @param[in] u8 targetNode = the target node to find the link to
286 * @param[in] sMainData* pDat = our global state
287 * @param[out] u8 results = the link on source which connects to target
288 * ---------------------------------------------------------------------------------------
290 u8 convertNodeToLink(u8 srcNode, u8 targetNode, sMainData *pDat)
292 u8 targetlink = INVALID_LINK;
295 for (k = 0; k < pDat->TotalLinks*2; k += 2)
297 if ((pDat->PortList[k+0].NodeID == srcNode) && (pDat->PortList[k+1].NodeID == targetNode))
299 targetlink = pDat->PortList[k+0].Link;
302 else if ((pDat->PortList[k+1].NodeID == srcNode) && (pDat->PortList[k+0].NodeID == targetNode))
304 targetlink = pDat->PortList[k+1].Link;
308 ASSERT(targetlink != INVALID_LINK);
314 /*----------------------------------------------------------------------------------------
316 * htDiscoveryFloodFill(sMainData *pDat)
319 * Discover all coherent devices in the system, initializing some basics like node IDs
320 * and total nodes found in the process. As we go we also build a representation of the
321 * discovered system which we will use later to program the routing tables. During this
322 * step, the routing is via default link back to BSP and to each new node on the link it
323 * was discovered on (no coherency is active yet).
326 * @param[in] sMainData* pDat = our global state
327 * ---------------------------------------------------------------------------------------
329 void htDiscoveryFloodFill(sMainData *pDat)
334 /* Entries are always added in pairs, the even indices are the 'source'
335 * side closest to the BSP, the odd indices are the 'destination' side
338 while (currentNode <= pDat->NodesDiscovered)
342 if (currentNode != 0)
344 /* Set path from BSP to currentNode */
345 routeFromBSP(currentNode, currentNode, pDat);
347 /* Set path from BSP to currentNode for currentNode+1 if
348 * currentNode+1 != MAX_NODES
350 if (currentNode+1 != MAX_NODES)
351 routeFromBSP(currentNode, currentNode+1, pDat);
353 /* Configure currentNode to route traffic to the BSP through its
356 pDat->nb->writeRoutingTable(currentNode, 0, pDat->nb->readDefLnk(currentNode, pDat->nb), pDat->nb);
359 /* Set currentNode's NodeID field to currentNode */
360 pDat->nb->writeNodeID(currentNode, currentNode, pDat->nb);
362 /* Enable routing tables on currentNode*/
363 pDat->nb->enableRoutingTables(currentNode, pDat->nb);
365 for (currentLink = 0; currentLink < pDat->nb->maxLinks; currentLink++)
370 if (pDat->HtBlock->AMD_CB_IgnoreLink && pDat->HtBlock->AMD_CB_IgnoreLink(currentNode, currentLink))
373 if (pDat->nb->readTrueLinkFailStatus(currentNode, currentLink, pDat, pDat->nb))
376 /* Make sure that the link is connected, coherent, and ready */
377 if (!pDat->nb->verifyLinkIsCoherent(currentNode, currentLink, pDat->nb))
381 /* Test to see if the currentLink has already been explored */
383 for (temp = 0; temp < pDat->TotalLinks; temp++)
385 if ((pDat->PortList[temp*2+1].NodeID == currentNode) &&
386 (pDat->PortList[temp*2+1].Link == currentLink))
394 /* We had already expored this link */
398 if (pDat->nb->handleSpecialLinkCase(currentNode, currentLink, pDat, pDat->nb))
403 /* Modify currentNode's routing table to use currentLink to send
404 * traffic to currentNode+1
406 pDat->nb->writeRoutingTable(currentNode, currentNode+1, currentLink, pDat->nb);
408 /* Check the northbridge of the node we just found, to make sure it is compatible
409 * before doing anything else to it.
411 if (!pDat->nb->isCompatible(currentNode+1, pDat->nb))
415 /* Notify BIOS of event (while variables are still the same) */
416 if (pDat->HtBlock->AMD_CB_EventNotify)
418 sHtEventCohFamilyFeud evt = {sizeof(sHtEventCohFamilyFeud),
421 pDat->NodesDiscovered};
423 pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,
424 HT_EVENT_COH_FAMILY_FEUD,
428 /* If node is not compatible, force boot to 1P
429 * If they are not compatible stop cHT init and:
430 * 1. Disable all cHT links on the BSP
431 * 2. Configure the BSP routing tables as a UP.
432 * 3. Notify main BIOS.
434 pDat->NodesDiscovered = 0;
436 pDat->TotalLinks = 0;
437 /* Abandon our coherent link data structure. At this point there may
438 * be coherent links on the BSP that are not yet in the portList, and
439 * we have to turn them off anyway. So depend on the hardware to tell us.
441 for (currentLink = 0; currentLink < pDat->nb->maxLinks; currentLink++)
443 /* Stop all links which are connected, coherent, and ready */
444 if (pDat->nb->verifyLinkIsCoherent(currentNode, currentLink, pDat->nb))
445 pDat->nb->stopLink(currentNode, currentLink, pDat->nb);
448 for (nodeToKill = 0; nodeToKill < pDat->nb->maxNodes; nodeToKill++)
450 pDat->nb->writeFullRoutingTable(0, nodeToKill, ROUTETOSELF, ROUTETOSELF, 0, pDat->nb);
453 /* End Coherent Discovery */
458 /* Read token from Current+1 */
459 token = pDat->nb->readToken(currentNode+1, pDat->nb);
460 ASSERT(token <= pDat->NodesDiscovered);
463 pDat->NodesDiscovered++;
464 ASSERT(pDat->NodesDiscovered < pDat->nb->maxNodes);
465 /* Check the capability of northbridges against the currently known configuration */
466 if (!pDat->nb->isCapable(currentNode+1, pDat, pDat->nb))
470 /* Notify BIOS of event */
471 if (pDat->HtBlock->AMD_CB_EventNotify)
473 sHtEventCohMpCapMismatch evt = {sizeof(sHtEventCohMpCapMismatch),
477 pDat->NodesDiscovered};
479 pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,
480 HT_EVENT_COH_MPCAP_MISMATCH,
484 pDat->NodesDiscovered = 0;
486 pDat->TotalLinks = 0;
488 for (nodeToKill = 0; nodeToKill < pDat->nb->maxNodes; nodeToKill++)
490 pDat->nb->writeFullRoutingTable(0, nodeToKill, ROUTETOSELF, ROUTETOSELF, 0, pDat->nb);
493 /* End Coherent Discovery */
498 token = pDat->NodesDiscovered;
499 pDat->nb->writeToken(currentNode+1, token, pDat->nb);
500 /* Inform that we have discovered a node, so that logical id to
501 * socket mapping info can be recorded.
503 if (pDat->HtBlock->AMD_CB_EventNotify)
505 sHtEventCohNodeDiscovered evt = {sizeof(sHtEventCohNodeDiscovered),
510 pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_INFO,
511 HT_EVENT_COH_NODE_DISCOVERED,
516 if (pDat->TotalLinks == MAX_PLATFORM_LINKS)
519 * Exceeded our capacity to describe all coherent links found in the system.
521 * Auto recovery is not possible because data space is already all used.
522 * If the callback is not implemented or returns we will continue to initialize
523 * the fabric we are capable of representing, adding no more nodes or links.
524 * This should yield a bootable topology, but likely not the one intended.
525 * We cannot continue discovery, there may not be any way to route a new
526 * node back to the BSP if we can't add links to our representation of the system.
528 if (pDat->HtBlock->AMD_CB_EventNotify)
530 sHtEventCohLinkExceed evt = {sizeof(sHtEventCohLinkExceed),
534 pDat->NodesDiscovered,
537 pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,
538 HT_EVENT_COH_LINK_EXCEED,
541 /* Force link and node loops to halt */
543 currentNode = pDat->NodesDiscovered;
547 pDat->PortList[pDat->TotalLinks*2].Type = PORTLIST_TYPE_CPU;
548 pDat->PortList[pDat->TotalLinks*2].Link = currentLink;
549 pDat->PortList[pDat->TotalLinks*2].NodeID = currentNode;
551 pDat->PortList[pDat->TotalLinks*2+1].Type = PORTLIST_TYPE_CPU;
552 pDat->PortList[pDat->TotalLinks*2+1].Link = pDat->nb->readDefLnk(currentNode+1, pDat->nb);
553 pDat->PortList[pDat->TotalLinks*2+1].NodeID = token;
557 if ( !pDat->sysMatrix[currentNode][token] )
559 pDat->sysDegree[currentNode]++;
560 pDat->sysDegree[token]++;
561 pDat->sysMatrix[currentNode][token] = TRUE;
562 pDat->sysMatrix[token][currentNode] = TRUE;
570 /***************************************************************************
571 *** ISOMORPHISM BASED ROUTING TABLE GENERATION CODE ***
572 ***************************************************************************/
574 /*----------------------------------------------------------------------------------------
576 * isoMorph(u8 i, sMainData *pDat)
579 * Is graphA isomorphic to graphB?
580 * if this function returns true, then Perm will contain the permutation
581 * required to transform graphB into graphA.
582 * We also use the degree of each node, that is the number of connections it has, to
583 * speed up rejection of non-isomorphic graphs (if there is a node in graphA with n
584 * connections, there must be at least one unmatched in graphB with n connections).
587 * @param[in] u8 i = the discovered node which we are trying to match
588 * with a permutation the topology
589 * @param[in]/@param[out] sMainData* pDat = our global state, degree and adjacency matrix,
590 * output a permutation if successful
591 * @param[out] BOOL results = the graphs are (or are not) isomorphic
592 * ---------------------------------------------------------------------------------------
594 BOOL isoMorph(u8 i, sMainData *pDat)
599 /* We have only been called if nodecnt == pSelected->size ! */
600 nodecnt = pDat->NodesDiscovered+1;
604 /* Keep building the permutation */
605 for (j = 0; j < nodecnt; j++)
607 /* Make sure the degree matches */
608 if (pDat->sysDegree[i] != pDat->dbDegree[j])
611 /* Make sure that j hasn't been used yet (ought to use a "used" */
612 /* array instead, might be faster) */
613 for (k = 0; k < i; k++)
615 if (pDat->Perm[k] == j)
621 if (isoMorph(i+1, pDat))
626 /* Test to see if the permutation is isomorphic */
627 for (j = 0; j < nodecnt; j++)
629 for (k = 0; k < nodecnt; k++)
631 if ( pDat->sysMatrix[j][k] !=
632 pDat->dbMatrix[pDat->Perm[j]][pDat->Perm[k]] )
641 /*----------------------------------------------------------------------------------------
643 * lookupComputeAndLoadRoutingTables(sMainData *pDat)
646 * Using the description of the fabric topology we discovered, try to find a match
647 * among the supported topologies. A supported topology description matches
648 * the discovered fabric if the nodes can be matched in such a way that all the nodes connected
649 * in one set are exactly the nodes connected in the other (formally, that the graphs are
650 * isomorphic). Which links are used is not really important to matching. If the graphs
651 * match, then there is a permutation of one that translates the node positions and linkages
654 * In order to make the isomorphism test efficient, we test for matched number of nodes
655 * (a 4 node fabric is not isomorphic to a 2 node topology), and provide degrees of nodes
656 * to the isomorphism test.
658 * The generic routing table solution for any topology is predetermined and represented
659 * as part of the topology. The permutation we computed tells us how to interpret the
660 * routing onto the fabric we discovered. We do this working backward from the last
661 * node discovered to the BSP, writing the routing tables as we go.
664 * @param[in] sMainData* pDat = our global state, the discovered fabric,
665 * @param[out] degree matrix, permutation
666 * ---------------------------------------------------------------------------------------
668 void lookupComputeAndLoadRoutingTables(sMainData *pDat)
675 size = pDat->NodesDiscovered + 1;
676 /* Use the provided topology list or the internal, default one. */
677 pTopologyList = pDat->HtBlock->topolist;
678 if (pTopologyList == NULL)
680 getAmdTopolist(&pTopologyList);
683 pSelected = *pTopologyList;
684 while (pSelected != NULL)
686 if (graphHowManyNodes(pSelected) == size)
688 /* Build Degree vector and Adjency Matrix for this entry */
689 for (i = 0; i < size; i++)
691 pDat->dbDegree[i] = 0;
692 for (j = 0; j < size; j++)
694 if (graphIsAdjacent(pSelected, i, j))
696 pDat->dbMatrix[i][j] = 1;
701 pDat->dbMatrix[i][j] = 0;
705 if (isoMorph(0, pDat))
706 break; /* A matching topology was found */
710 pSelected = *pTopologyList;
713 if (pSelected != NULL)
715 /* Compute the reverse Permutation */
716 for (i = 0; i < size; i++)
718 pDat->ReversePerm[pDat->Perm[i]] = i;
721 /* Start with the last discovered node, and move towards the BSP */
722 for (i = size-1; i >= 0; i--)
724 for (j = 0; j < size; j++)
726 u8 ReqTargetLink, RspTargetLink;
727 u8 ReqTargetNode, RspTargetNode;
729 u8 AbstractBcTargetNodes = graphGetBc(pSelected, pDat->Perm[i], pDat->Perm[j]);
730 u32 BcTargetLinks = 0;
732 for (k = 0; k < MAX_NODES; k++)
734 if (AbstractBcTargetNodes & ((u32)1<<k))
736 BcTargetLinks |= (u32)1 << convertNodeToLink(i, pDat->ReversePerm[k], pDat);
742 ReqTargetLink = ROUTETOSELF;
743 RspTargetLink = ROUTETOSELF;
747 ReqTargetNode = graphGetReq(pSelected, pDat->Perm[i], pDat->Perm[j]);
748 ReqTargetLink = convertNodeToLink(i, pDat->ReversePerm[ReqTargetNode], pDat);
750 RspTargetNode = graphGetRsp(pSelected, pDat->Perm[i], pDat->Perm[j]);
751 RspTargetLink = convertNodeToLink(i, pDat->ReversePerm[RspTargetNode], pDat);
754 pDat->nb->writeFullRoutingTable(i, j, ReqTargetLink, RspTargetLink, BcTargetLinks, pDat->nb);
756 /* Clean up discovery 'footprint' that otherwise remains in the routing table. It didn't hurt
757 * anything, but might cause confusion during debug and validation. Do this by setting the
758 * route back to all self routes. Since it's the node that would be one more than actually installed,
759 * this only applies if less than maxNodes were found.
761 if (size < pDat->nb->maxNodes)
763 pDat->nb->writeFullRoutingTable(i, size, ROUTETOSELF, ROUTETOSELF, 0, pDat->nb);
771 * No Matching Topology was found
773 * Auto recovery doesn't seem likely, Force boot as 1P.
774 * For reporting, logging, provide number of nodes
775 * If not implemented or returns, boot as BSP uniprocessor.
777 if (pDat->HtBlock->AMD_CB_EventNotify)
779 sHtEventCohNoTopology evt = {sizeof(sHtEventCohNoTopology),
780 pDat->NodesDiscovered};
782 pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,
783 HT_EVENT_COH_NO_TOPOLOGY,
788 pDat->NodesDiscovered = 0;
789 pDat->TotalLinks = 0;
790 pDat->nb->enableRoutingTables(0, pDat->nb);
793 #endif /* HT_BUILD_NC_ONLY */
796 /*----------------------------------------------------------------------------------------
798 * finializeCoherentInit(sMainData *pDat)
801 * Find the total number of cores and update the number of nodes and cores in all cpus.
802 * Limit cpu config access to installed cpus.
805 * @param[in] sMainData* pDat = our global state, number of nodes discovered.
806 * ---------------------------------------------------------------------------------------
808 void finializeCoherentInit(sMainData *pDat)
813 for (curNode = 0; curNode < pDat->NodesDiscovered+1; curNode++)
815 totalCores += pDat->nb->getNumCoresOnNode(curNode, pDat->nb);
818 for (curNode = 0; curNode < pDat->NodesDiscovered+1; curNode++)
820 pDat->nb->setTotalNodesAndCores(curNode, pDat->NodesDiscovered+1, totalCores, pDat->nb);
823 for (curNode = 0; curNode < pDat->NodesDiscovered+1; curNode++)
825 pDat->nb->limitNodes(curNode, pDat->nb);
830 /*----------------------------------------------------------------------------------------
832 * coherentInit(sMainData *pDat)
835 * Perform discovery and initialization of the coherent fabric.
838 * @param[in] sMainData* pDat = our global state
839 * ---------------------------------------------------------------------------------------
841 void coherentInit(sMainData *pDat)
845 #ifdef HT_BUILD_NC_ONLY
846 /* Replace discovery process with:
847 * No other nodes, no coherent links
848 * Enable routing tables on currentNode, for power on self route
850 pDat->NodesDiscovered = 0;
851 pDat->TotalLinks = 0;
852 pDat->nb->enableRoutingTables(0, pDat->nb);
854 pDat->NodesDiscovered = 0;
855 pDat->TotalLinks = 0;
856 for (i = 0; i < MAX_NODES; i++)
858 pDat->sysDegree[i] = 0;
859 for (j = 0; j < MAX_NODES; j++)
861 pDat->sysMatrix[i][j] = 0;
865 htDiscoveryFloodFill(pDat);
866 lookupComputeAndLoadRoutingTables(pDat);
868 finializeCoherentInit(pDat);
871 /***************************************************************************
872 *** Non-coherent init code ***
874 ***************************************************************************/
875 /*----------------------------------------------------------------------------------------
877 * processLink(u8 node, u8 link, sMainData *pDat)
880 * Process a non-coherent link, enabling a range of bus numbers, and setting the device
881 * ID for all devices found
884 * @param[in] u8 node = Node on which to process nc init
885 * @param[in] u8 link = The non-coherent link on that node
886 * @param[in] sMainData* pDat = our global state
887 * ---------------------------------------------------------------------------------------
889 void processLink(u8 node, u8 link, sMainData *pDat)
899 SBDFO lastSBDFO = ILLEGAL_SBDFO;
902 ASSERT(node < pDat->nb->maxNodes && link < pDat->nb->maxLinks);
904 if ((pDat->HtBlock->AMD_CB_OverrideBusNumbers == NULL)
905 || !pDat->HtBlock->AMD_CB_OverrideBusNumbers(node, link, &secBus, &subBus))
907 /* Assign Bus numbers */
908 if (pDat->AutoBusCurrent >= pDat->HtBlock->AutoBusMax)
910 /* If we run out of Bus Numbers notify, if call back unimplemented or if it
911 * returns, skip this chain
913 if (pDat->HtBlock->AMD_CB_EventNotify)
915 sHTEventNcohBusMaxExceed evt = {sizeof(sHTEventNcohBusMaxExceed), node, link, pDat->AutoBusCurrent};
917 pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,HT_EVENT_NCOH_BUS_MAX_EXCEED,(u8 *)&evt);
923 if (pDat->UsedCfgMapEntires >= 4)
925 /* If we have used all the PCI Config maps we can't add another chain.
926 * Notify and if call back is unimplemented or returns, skip this chain.
928 if (pDat->HtBlock->AMD_CB_EventNotify)
930 sHtEventNcohCfgMapExceed evt = {sizeof(sHtEventNcohCfgMapExceed), node, link};
932 pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,
933 HT_EVENT_NCOH_CFG_MAP_EXCEED,
940 secBus = pDat->AutoBusCurrent;
941 subBus = secBus + pDat->HtBlock->AutoBusIncrement-1;
942 pDat->AutoBusCurrent += pDat->HtBlock->AutoBusIncrement;
945 pDat->nb->setCFGAddrMap(pDat->UsedCfgMapEntires, secBus, subBus, node, link, pDat, pDat->nb);
946 pDat->UsedCfgMapEntires++;
948 if ((pDat->HtBlock->AMD_CB_ManualBUIDSwapList != NULL)
949 && pDat->HtBlock->AMD_CB_ManualBUIDSwapList(node, link, &pSwapPtr))
951 /* Manual non-coherent BUID assignment */
953 /* Assign BUID's per manual override */
954 while (*pSwapPtr != 0xFF)
956 currentPtr = MAKE_SBDFO(0, secBus, *pSwapPtr, 0, 0);
961 AmdPCIFindNextCap(¤tPtr);
962 ASSERT(currentPtr != ILLEGAL_SBDFO);
963 AmdPCIRead(currentPtr, &temp);
964 } while (!IS_HT_SLAVE_CAPABILITY(temp));
966 currentBUID = *pSwapPtr;
968 AmdPCIWriteBits(currentPtr, 20, 16, ¤tBUID);
971 /* Build chain of devices */
974 while (*pSwapPtr != 0xFF)
976 pDat->PortList[pDat->TotalLinks*2].NodeID = node;
979 pDat->PortList[pDat->TotalLinks*2].Type = PORTLIST_TYPE_CPU;
980 pDat->PortList[pDat->TotalLinks*2].Link = link;
984 pDat->PortList[pDat->TotalLinks*2].Type = PORTLIST_TYPE_IO;
985 pDat->PortList[pDat->TotalLinks*2].Link = 1-lastLink;
986 pDat->PortList[pDat->TotalLinks*2].HostLink = link;
987 pDat->PortList[pDat->TotalLinks*2].HostDepth = depth-1;
988 pDat->PortList[pDat->TotalLinks*2].Pointer = lastSBDFO;
991 pDat->PortList[pDat->TotalLinks*2+1].Type = PORTLIST_TYPE_IO;
992 pDat->PortList[pDat->TotalLinks*2+1].NodeID = node;
993 pDat->PortList[pDat->TotalLinks*2+1].HostLink = link;
994 pDat->PortList[pDat->TotalLinks*2+1].HostDepth = depth;
996 currentPtr = MAKE_SBDFO(0, secBus, (*pSwapPtr & 0x3F), 0, 0);
999 AmdPCIFindNextCap(¤tPtr);
1000 ASSERT(currentPtr != ILLEGAL_SBDFO);
1001 AmdPCIRead(currentPtr, &temp);
1002 } while (!IS_HT_SLAVE_CAPABILITY(temp));
1003 pDat->PortList[pDat->TotalLinks*2+1].Pointer = currentPtr;
1004 lastSBDFO = currentPtr;
1006 /* Bit 6 indicates whether orientation override is desired.
1007 * Bit 7 indicates the upstream link if overriding.
1009 /* assert catches at least the one known incorrect setting */
1010 ASSERT ((*pSwapPtr & 0x40) || (!(*pSwapPtr & 0x80)));
1011 if (*pSwapPtr & 0x40)
1013 /* Override the device's orientation */
1014 lastLink = *pSwapPtr >> 7;
1018 /* Detect the device's orientation */
1019 AmdPCIReadBits(currentPtr, 26, 26, &temp);
1020 lastLink = (u8)temp;
1022 pDat->PortList[pDat->TotalLinks*2+1].Link = lastLink;
1031 /* Automatic non-coherent device detection */
1036 currentPtr = MAKE_SBDFO(0, secBus, 0, 0, 0);
1038 AmdPCIRead(currentPtr, &temp);
1039 if (temp == 0xFFFFFFFF)
1040 /* No device found at currentPtr */
1043 if (pDat->TotalLinks == MAX_PLATFORM_LINKS)
1046 * Exceeded our capacity to describe all non-coherent links found in the system.
1048 * Auto recovery is not possible because data space is already all used.
1050 if (pDat->HtBlock->AMD_CB_EventNotify)
1052 sHtEventNcohLinkExceed evt = {sizeof(sHtEventNcohLinkExceed),
1056 pDat->nb->maxLinks};
1058 pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,
1059 HT_EVENT_NCOH_LINK_EXCEED,
1062 /* Force link loop to halt */
1067 pDat->PortList[pDat->TotalLinks*2].NodeID = node;
1070 pDat->PortList[pDat->TotalLinks*2].Type = PORTLIST_TYPE_CPU;
1071 pDat->PortList[pDat->TotalLinks*2].Link = link;
1075 pDat->PortList[pDat->TotalLinks*2].Type = PORTLIST_TYPE_IO;
1076 pDat->PortList[pDat->TotalLinks*2].Link = 1-lastLink;
1077 pDat->PortList[pDat->TotalLinks*2].HostLink = link;
1078 pDat->PortList[pDat->TotalLinks*2].HostDepth = depth-1;
1079 pDat->PortList[pDat->TotalLinks*2].Pointer = lastSBDFO;
1082 pDat->PortList[pDat->TotalLinks*2+1].Type = PORTLIST_TYPE_IO;
1083 pDat->PortList[pDat->TotalLinks*2+1].NodeID = node;
1084 pDat->PortList[pDat->TotalLinks*2+1].HostLink = link;
1085 pDat->PortList[pDat->TotalLinks*2+1].HostDepth = depth;
1089 AmdPCIFindNextCap(¤tPtr);
1090 ASSERT(currentPtr != ILLEGAL_SBDFO);
1091 AmdPCIRead(currentPtr, &temp);
1092 } while (!IS_HT_SLAVE_CAPABILITY(temp));
1094 AmdPCIReadBits(currentPtr, 25, 21, &unitIDcnt);
1095 if ((unitIDcnt + currentBUID > 31) || ((secBus == 0) && (unitIDcnt + currentBUID > 24)))
1097 /* An error handler for the case where we run out of BUID's on a chain */
1098 if (pDat->HtBlock->AMD_CB_EventNotify)
1100 sHtEventNcohBuidExceed evt = {sizeof(sHtEventNcohBuidExceed),
1101 node, link, depth, (u8)currentBUID, (u8)unitIDcnt};
1103 pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,HT_EVENT_NCOH_BUID_EXCEED,(u8 *)&evt);
1108 AmdPCIWriteBits(currentPtr, 20, 16, ¤tBUID);
1111 currentPtr += MAKE_SBDFO(0, 0, currentBUID, 0, 0);
1112 AmdPCIReadBits(currentPtr, 20, 16, &temp);
1113 if (temp != currentBUID)
1115 /* An error handler for this critical error */
1116 if (pDat->HtBlock->AMD_CB_EventNotify)
1118 sHtEventNcohDeviceFailed evt = {sizeof(sHtEventNcohDeviceFailed),
1119 node, link, depth, (u8)currentBUID};
1121 pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,HT_EVENT_NCOH_DEVICE_FAILED,(u8 *)&evt);
1127 AmdPCIReadBits(currentPtr, 26, 26, &temp);
1128 pDat->PortList[pDat->TotalLinks*2+1].Link = (u8)temp;
1129 pDat->PortList[pDat->TotalLinks*2+1].Pointer = currentPtr;
1131 lastLink = (u8)temp;
1132 lastSBDFO = currentPtr;
1136 currentBUID += unitIDcnt;
1138 if (pDat->HtBlock->AMD_CB_EventNotify)
1140 /* Provide information on automatic device results */
1141 sHtEventNcohAutoDepth evt = {sizeof(sHtEventNcohAutoDepth), node, link, (depth - 1)};
1143 pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_INFO,HT_EVENT_NCOH_AUTO_DEPTH,(u8 *)&evt);
1149 /*----------------------------------------------------------------------------------------
1151 * ncInit(sMainData *pDat)
1154 * Initialize the non-coherent fabric. Begin with the compat link on the BSP, then
1155 * find and initialize all other non-coherent chains.
1158 * @param[in] sMainData* pDat = our global state
1159 * ---------------------------------------------------------------------------------------
1161 void ncInit(sMainData *pDat)
1166 compatLink = pDat->nb->readSbLink(pDat->nb);
1167 processLink(0, compatLink, pDat);
1169 for (node = 0; node <= pDat->NodesDiscovered; node++)
1171 for (link = 0; link < pDat->nb->maxLinks; link++)
1173 if (pDat->HtBlock->AMD_CB_IgnoreLink && pDat->HtBlock->AMD_CB_IgnoreLink(node, link))
1174 continue; /* Skip the link */
1176 if (node == 0 && link == compatLink)
1179 if (pDat->nb->readTrueLinkFailStatus(node, link, pDat, pDat->nb))
1182 if (pDat->nb->verifyLinkIsNonCoherent(node, link, pDat->nb))
1183 processLink(node, link, pDat);
1188 /***************************************************************************
1189 *** Link Optimization ***
1190 ***************************************************************************/
1192 /*----------------------------------------------------------------------------------------
1194 * regangLinks(sMainData *pDat)
1197 * Test the sublinks of a link to see if they qualify to be reganged. If they do,
1198 * update the port list data to indicate that this should be done. Note that no
1199 * actual hardware state is changed in this routine.
1202 * @param[in,out] sMainData* pDat = our global state
1203 * ---------------------------------------------------------------------------------------
1205 void regangLinks(sMainData *pDat)
1207 #ifndef HT_BUILD_NC_ONLY
1209 for (i = 0; i < pDat->TotalLinks*2; i += 2)
1211 ASSERT(pDat->PortList[i].Type < 2 && pDat->PortList[i].Link < pDat->nb->maxLinks); /* Data validation */
1212 ASSERT(pDat->PortList[i+1].Type < 2 && pDat->PortList[i+1].Link < pDat->nb->maxLinks); /* data validation */
1213 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 */
1215 /* Regang is false unless we pass all conditions below */
1216 pDat->PortList[i].SelRegang = FALSE;
1217 pDat->PortList[i+1].SelRegang = FALSE;
1219 if ( (pDat->PortList[i].Type != PORTLIST_TYPE_CPU) || (pDat->PortList[i+1].Type != PORTLIST_TYPE_CPU))
1220 continue; /* Only process cpu to cpu links */
1222 for (j = i+2; j < pDat->TotalLinks*2; j += 2)
1224 if ( (pDat->PortList[j].Type != PORTLIST_TYPE_CPU) || (pDat->PortList[j+1].Type != PORTLIST_TYPE_CPU) )
1225 continue; /* Only process cpu to cpu links */
1227 if (pDat->PortList[i].NodeID != pDat->PortList[j].NodeID)
1228 continue; /* Links must be from the same source */
1230 if (pDat->PortList[i+1].NodeID != pDat->PortList[j+1].NodeID)
1231 continue; /* Link must be to the same target */
1233 if ((pDat->PortList[i].Link & 3) != (pDat->PortList[j].Link & 3))
1234 continue; /* Ensure same source base port */
1236 if ((pDat->PortList[i+1].Link & 3) != (pDat->PortList[j+1].Link & 3))
1237 continue; /* Ensure same destination base port */
1239 if ((pDat->PortList[i].Link & 4) != (pDat->PortList[i+1].Link & 4))
1240 continue; /* Ensure sublink0 routes to sublink0 */
1242 ASSERT((pDat->PortList[j].Link & 4) == (pDat->PortList[j+1].Link & 4)); /* (therefore sublink1 routes to sublink1) */
1244 if (pDat->HtBlock->AMD_CB_SkipRegang &&
1245 pDat->HtBlock->AMD_CB_SkipRegang(pDat->PortList[i].NodeID,
1246 pDat->PortList[i].Link & 0x03,
1247 pDat->PortList[i+1].NodeID,
1248 pDat->PortList[i+1].Link & 0x03))
1250 continue; /* Skip regang */
1254 pDat->PortList[i].Link &= 0x03; /* Force to point to sublink0 */
1255 pDat->PortList[i+1].Link &= 0x03;
1256 pDat->PortList[i].SelRegang = TRUE; /* Enable link reganging */
1257 pDat->PortList[i+1].SelRegang = TRUE;
1258 pDat->PortList[i].PrvWidthOutCap = HT_WIDTH_16_BITS;
1259 pDat->PortList[i+1].PrvWidthOutCap = HT_WIDTH_16_BITS;
1260 pDat->PortList[i].PrvWidthInCap = HT_WIDTH_16_BITS;
1261 pDat->PortList[i+1].PrvWidthInCap = HT_WIDTH_16_BITS;
1263 /* Delete PortList[j, j+1], slow but easy to debug implementation */
1265 Amdmemcpy(&(pDat->PortList[j]), &(pDat->PortList[j+2]), sizeof(sPortDescriptor)*(pDat->TotalLinks*2-j));
1266 Amdmemset(&(pDat->PortList[pDat->TotalLinks*2]), INVALID_LINK, sizeof(sPortDescriptor)*2);
1268 /* //High performance, but would make debuging harder due to 'shuffling' of the records */
1269 /* //Amdmemcpy(PortList[TotalPorts-2], PortList[j], SIZEOF(sPortDescriptor)*2); */
1270 /* //TotalPorts -=2; */
1272 break; /* Exit loop, advance to PortList[i+2] */
1275 #endif /* HT_BUILD_NC_ONLY */
1278 /*----------------------------------------------------------------------------------------
1280 * selectOptimalWidthAndFrequency(sMainData *pDat)
1284 * Examine both sides of a link and determine the optimal frequency and width,
1285 * taking into account externally provided limits and enforcing any other limit
1286 * or matching rules as applicable except sublink balancing. Update the port
1287 * list date with the optimal settings.
1288 * Note no hardware state changes in this routine.
1291 * @param[in,out] sMainData* pDat = our global state, port list data
1292 * ---------------------------------------------------------------------------------------
1294 void selectOptimalWidthAndFrequency(sMainData *pDat)
1299 u8 cbPCBABDownstreamWidth;
1300 u8 cbPCBBAUpstreamWidth;
1302 for (i = 0; i < pDat->TotalLinks*2; i += 2)
1304 cbPCBFreqLimit = 0xFFFF;
1305 cbPCBABDownstreamWidth = 16;
1306 cbPCBBAUpstreamWidth = 16;
1308 if ( (pDat->PortList[i].Type == PORTLIST_TYPE_CPU) && (pDat->PortList[i+1].Type == PORTLIST_TYPE_CPU))
1310 if (pDat->HtBlock->AMD_CB_Cpu2CpuPCBLimits)
1312 pDat->HtBlock->AMD_CB_Cpu2CpuPCBLimits(
1313 pDat->PortList[i].NodeID,
1314 pDat->PortList[i].Link,
1315 pDat->PortList[i+1].NodeID,
1316 pDat->PortList[i+1].Link,
1317 &cbPCBABDownstreamWidth,
1318 &cbPCBBAUpstreamWidth, &cbPCBFreqLimit
1324 if (pDat->HtBlock->AMD_CB_IOPCBLimits)
1326 pDat->HtBlock->AMD_CB_IOPCBLimits(
1327 pDat->PortList[i+1].NodeID,
1328 pDat->PortList[i+1].HostLink,
1329 pDat->PortList[i+1].HostDepth,
1330 &cbPCBABDownstreamWidth,
1331 &cbPCBBAUpstreamWidth, &cbPCBFreqLimit
1337 temp = pDat->PortList[i].PrvFrequencyCap;
1338 temp &= pDat->PortList[i+1].PrvFrequencyCap;
1339 temp &= cbPCBFreqLimit;
1340 pDat->PortList[i].CompositeFrequencyCap = (u16)temp;
1341 pDat->PortList[i+1].CompositeFrequencyCap = (u16)temp;
1346 if (temp & ((u32)1 << j))
1350 pDat->PortList[i].SelFrequency = j;
1351 pDat->PortList[i+1].SelFrequency = j;
1353 temp = pDat->PortList[i].PrvWidthOutCap;
1354 if (pDat->PortList[i+1].PrvWidthInCap < temp)
1355 temp = pDat->PortList[i+1].PrvWidthInCap;
1356 if (cbPCBABDownstreamWidth < temp)
1357 temp = cbPCBABDownstreamWidth;
1358 pDat->PortList[i].SelWidthOut = (u8)temp;
1359 pDat->PortList[i+1].SelWidthIn = (u8)temp;
1361 temp = pDat->PortList[i].PrvWidthInCap;
1362 if (pDat->PortList[i+1].PrvWidthOutCap < temp)
1363 temp = pDat->PortList[i+1].PrvWidthOutCap;
1364 if (cbPCBBAUpstreamWidth < temp)
1365 temp = cbPCBBAUpstreamWidth;
1366 pDat->PortList[i].SelWidthIn = (u8)temp;
1367 pDat->PortList[i+1].SelWidthOut = (u8)temp;
1372 /*----------------------------------------------------------------------------------------
1374 * hammerSublinkFixup(sMainData *pDat)
1377 * Iterate through all links, checking the frequency of each sublink pair. Make the
1378 * adjustment to the port list data so that the frequencies are at a valid ratio,
1379 * reducing frequency as needed to achieve this. (All links support the minimum 200 MHz
1380 * frequency.) Repeat the above until no adjustments are needed.
1381 * Note no hardware state changes in this routine.
1384 * @param[in,out] sMainData* pDat = our global state, link state and port list
1385 * ---------------------------------------------------------------------------------------
1387 void hammerSublinkFixup(sMainData *pDat)
1389 #ifndef HT_BUILD_NC_ONLY
1391 BOOL changes, downgrade;
1401 for (i = 0; i < pDat->TotalLinks*2; i++)
1403 if (pDat->PortList[i].Type != PORTLIST_TYPE_CPU) /* Must be a CPU link */
1405 if (pDat->PortList[i].Link < 4) /* Only look for for sublink1's */
1408 for (j = 0; j < pDat->TotalLinks*2; j++)
1410 /* Step 1. Find the matching sublink0 */
1411 if (pDat->PortList[j].Type != PORTLIST_TYPE_CPU)
1413 if (pDat->PortList[j].NodeID != pDat->PortList[i].NodeID)
1415 if (pDat->PortList[j].Link != (pDat->PortList[i].Link & 0x03))
1418 /* Step 2. Check for an illegal frequency ratio */
1419 if (pDat->PortList[i].SelFrequency >= pDat->PortList[j].SelFrequency)
1422 hiFreq = pDat->PortList[i].SelFrequency;
1423 loFreq = pDat->PortList[j].SelFrequency;
1428 hiFreq = pDat->PortList[j].SelFrequency;
1429 loFreq = pDat->PortList[i].SelFrequency;
1432 if (hiFreq == loFreq)
1433 break; /* The frequencies are 1:1, no need to do anything */
1439 if ((loFreq != 7) && /* {13, 7} 2400MHz / 1200MHz 2:1 */
1440 (loFreq != 4) && /* {13, 4} 2400MHz / 600MHz 4:1 */
1441 (loFreq != 2) ) /* {13, 2} 2400MHz / 400MHz 6:1 */
1444 else if (hiFreq == 11)
1446 if ((loFreq != 6)) /* {11, 6} 2000MHz / 1000MHz 2:1 */
1449 else if (hiFreq == 9)
1451 if ((loFreq != 5) && /* { 9, 5} 1600MHz / 800MHz 2:1 */
1452 (loFreq != 2) && /* { 9, 2} 1600MHz / 400MHz 4:1 */
1453 (loFreq != 0) ) /* { 9, 0} 1600MHz / 200Mhz 8:1 */
1456 else if (hiFreq == 7)
1458 if ((loFreq != 4) && /* { 7, 4} 1200MHz / 600MHz 2:1 */
1459 (loFreq != 0) ) /* { 7, 0} 1200MHz / 200MHz 6:1 */
1462 else if (hiFreq == 5)
1464 if ((loFreq != 2) && /* { 5, 2} 800MHz / 400MHz 2:1 */
1465 (loFreq != 0) ) /* { 5, 0} 800MHz / 200MHz 4:1 */
1468 else if (hiFreq == 2)
1470 if ((loFreq != 0)) /* { 2, 0} 400MHz / 200MHz 2:1 */
1475 downgrade = TRUE; /* no legal ratios for hiFreq */
1478 /* Step 3. Downgrade the higher of the two frequencies, and set nochanges to FALSE */
1481 /* Although the problem was with the port specified by hiIndex, we need to */
1482 /* downgrade both ends of the link. */
1483 hiIndex = hiIndex & 0xFE; /* Select the 'upstream' (i.e. even) port */
1485 temp = pDat->PortList[hiIndex].CompositeFrequencyCap;
1487 /* Remove hiFreq from the list of valid frequencies */
1488 temp = temp & ~((uint32)1 << hiFreq);
1490 pDat->PortList[hiIndex].CompositeFrequencyCap = (uint16)temp;
1491 pDat->PortList[hiIndex+1].CompositeFrequencyCap = (uint16)temp;
1495 if (temp & ((u32)1 << k))
1499 pDat->PortList[hiIndex].SelFrequency = k;
1500 pDat->PortList[hiIndex+1].SelFrequency = k;
1506 } while (changes); /* Repeat until a valid configuration is reached */
1507 #endif /* HT_BUILD_NC_ONLY */
1510 /*----------------------------------------------------------------------------------------
1512 * linkOptimization(sMainData *pDat)
1515 * Based on link capabilities, apply optimization rules to come up with the real best
1516 * settings, including several external limit decision from call backs. This includes
1517 * handling of sublinks. Finally, after the port list data is updated, set the hardware
1518 * state for all links.
1521 * @param[in] sMainData* pDat = our global state
1522 * ---------------------------------------------------------------------------------------
1524 void linkOptimization(sMainData *pDat)
1526 pDat->nb->gatherLinkData(pDat, pDat->nb);
1528 selectOptimalWidthAndFrequency(pDat);
1529 hammerSublinkFixup(pDat);
1530 pDat->nb->setLinkData(pDat, pDat->nb);
1534 /*----------------------------------------------------------------------------------------
1536 * trafficDistribution(sMainData *pDat)
1539 * In the case of a two node system with both sublinks used, enable the traffic
1540 * distribution feature.
1543 * @param[in] sMainData* pDat = our global state, port list data
1544 * ---------------------------------------------------------------------------------------
1546 void trafficDistribution(sMainData *pDat)
1548 #ifndef HT_BUILD_NC_ONLY
1549 u32 links01, links10;
1553 /* Traffic Distribution is only used when there are exactly two nodes in the system */
1554 if (pDat->NodesDiscovered+1 != 2)
1560 for (i = 0; i < pDat->TotalLinks*2; i += 2)
1562 if ((pDat->PortList[i].Type == PORTLIST_TYPE_CPU) && (pDat->PortList[i+1].Type == PORTLIST_TYPE_CPU))
1564 links01 |= (u32)1 << pDat->PortList[i].Link;
1565 links10 |= (u32)1 << pDat->PortList[i+1].Link;
1569 ASSERT(linkCount != 0);
1571 return; /* Don't setup Traffic Distribution if only one link is being used */
1573 pDat->nb->writeTrafficDistribution(links01, links10, pDat->nb);
1574 #endif /* HT_BUILD_NC_ONLY */
1577 /*----------------------------------------------------------------------------------------
1579 * tuning(sMainData *pDat)
1582 * Handle system and performance tunings, such as traffic distribution, fifo and
1583 * buffer tuning, and special config tunings.
1586 * @param[in] sMainData* pDat = our global state, port list data
1587 * ---------------------------------------------------------------------------------------
1589 void tuning(sMainData *pDat)
1593 /* See if traffic distribution can be done and do it if so
1594 * or allow system specific customization
1596 if ((pDat->HtBlock->AMD_CB_CustomizeTrafficDistribution == NULL)
1597 || !pDat->HtBlock->AMD_CB_CustomizeTrafficDistribution())
1599 trafficDistribution(pDat);
1602 /* For each node, invoke northbridge specific buffer tunings or
1603 * system specific customizations.
1605 for (i=0; i < pDat->NodesDiscovered + 1; i++)
1607 if ((pDat->HtBlock->AMD_CB_CustomizeBuffers == NULL)
1608 || !pDat->HtBlock->AMD_CB_CustomizeBuffers(i))
1610 pDat->nb->bufferOptimizations(i, pDat, pDat->nb);
1615 /*----------------------------------------------------------------------------------------
1620 * Perform any general sanity checks which should prevent HT from running if they fail.
1621 * Currently only the "Must run on BSP only" check.
1624 * @param[out] result BOOL = true if check is ok, false if it failed
1625 * ---------------------------------------------------------------------------------------
1627 BOOL isSanityCheckOk()
1631 AmdMSRRead(APIC_Base, &qValue);
1633 return ((qValue.lo & ((u32)1 << APIC_Base_BSP)) != 0);
1636 /***************************************************************************
1637 *** HT Initialize ***
1638 ***************************************************************************/
1640 /*----------------------------------------------------------------------------------------
1642 * htInitialize(AMD_HTBLOCK *pBlock)
1645 * This is the top level external interface for Hypertransport Initialization.
1646 * Create our initial internal state, initialize the coherent fabric,
1647 * initialize the non-coherent chains, and perform any required fabric tuning or
1651 * @param[in] AMD_HTBLOCK* pBlock = Our Initial State including possible
1652 * topologies and routings, non coherent bus
1653 * assignment info, and actual
1654 * wrapper or OEM call back routines.
1655 * ---------------------------------------------------------------------------------------
1657 void amdHtInitialize(AMD_HTBLOCK *pBlock)
1662 if (isSanityCheckOk())
1664 newNorthBridge(0, &nb);
1666 pDat.HtBlock = pBlock;
1668 pDat.sysMpCap = nb.maxNodes;
1669 nb.isCapable(0, &pDat, pDat.nb);
1670 coherentInit(&pDat);
1672 pDat.AutoBusCurrent = pBlock->AutoBusStart;
1673 pDat.UsedCfgMapEntires = 0;
1675 linkOptimization(&pDat);