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 u8 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(u8 *graph, u8 nodeA, u8 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(u8 *graph, u8 nodeA, u8 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;
419 evt.eSize = sizeof(sHtEventCohFamilyFeud);
420 evt.node = currentNode;
421 evt.link = currentLink;
422 evt.totalNodes = pDat->NodesDiscovered;
424 pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,
425 HT_EVENT_COH_FAMILY_FEUD,
429 /* If node is not compatible, force boot to 1P
430 * If they are not compatible stop cHT init and:
431 * 1. Disable all cHT links on the BSP
432 * 2. Configure the BSP routing tables as a UP.
433 * 3. Notify main BIOS.
435 pDat->NodesDiscovered = 0;
437 pDat->TotalLinks = 0;
438 /* Abandon our coherent link data structure. At this point there may
439 * be coherent links on the BSP that are not yet in the portList, and
440 * we have to turn them off anyway. So depend on the hardware to tell us.
442 for (currentLink = 0; currentLink < pDat->nb->maxLinks; currentLink++)
444 /* Stop all links which are connected, coherent, and ready */
445 if (pDat->nb->verifyLinkIsCoherent(currentNode, currentLink, pDat->nb))
446 pDat->nb->stopLink(currentNode, currentLink, pDat->nb);
449 for (nodeToKill = 0; nodeToKill < pDat->nb->maxNodes; nodeToKill++)
451 pDat->nb->writeFullRoutingTable(0, nodeToKill, ROUTETOSELF, ROUTETOSELF, 0, pDat->nb);
454 /* End Coherent Discovery */
459 /* Read token from Current+1 */
460 token = pDat->nb->readToken(currentNode+1, pDat->nb);
461 ASSERT(token <= pDat->NodesDiscovered);
464 pDat->NodesDiscovered++;
465 ASSERT(pDat->NodesDiscovered < pDat->nb->maxNodes);
466 /* Check the capability of northbridges against the currently known configuration */
467 if (!pDat->nb->isCapable(currentNode+1, pDat, pDat->nb))
471 /* Notify BIOS of event */
472 if (pDat->HtBlock->AMD_CB_EventNotify)
474 sHtEventCohMpCapMismatch evt;
475 evt.eSize = sizeof(sHtEventCohMpCapMismatch);
476 evt.node = currentNode;
477 evt.link = currentLink;
478 evt.sysMpCap = pDat->sysMpCap;
479 evt.totalNodes = pDat->NodesDiscovered;
481 pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,
482 HT_EVENT_COH_MPCAP_MISMATCH,
486 pDat->NodesDiscovered = 0;
488 pDat->TotalLinks = 0;
490 for (nodeToKill = 0; nodeToKill < pDat->nb->maxNodes; nodeToKill++)
492 pDat->nb->writeFullRoutingTable(0, nodeToKill, ROUTETOSELF, ROUTETOSELF, 0, pDat->nb);
495 /* End Coherent Discovery */
500 token = pDat->NodesDiscovered;
501 pDat->nb->writeToken(currentNode+1, token, pDat->nb);
502 /* Inform that we have discovered a node, so that logical id to
503 * socket mapping info can be recorded.
505 if (pDat->HtBlock->AMD_CB_EventNotify)
507 sHtEventCohNodeDiscovered evt;
508 evt.eSize = sizeof(sHtEventCohNodeDiscovered);
509 evt.node = currentNode;
510 evt.link = currentLink;
513 pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_INFO,
514 HT_EVENT_COH_NODE_DISCOVERED,
519 if (pDat->TotalLinks == MAX_PLATFORM_LINKS)
522 * Exceeded our capacity to describe all coherent links found in the system.
524 * Auto recovery is not possible because data space is already all used.
525 * If the callback is not implemented or returns we will continue to initialize
526 * the fabric we are capable of representing, adding no more nodes or links.
527 * This should yield a bootable topology, but likely not the one intended.
528 * We cannot continue discovery, there may not be any way to route a new
529 * node back to the BSP if we can't add links to our representation of the system.
531 if (pDat->HtBlock->AMD_CB_EventNotify)
533 sHtEventCohLinkExceed evt;
534 evt.eSize = sizeof(sHtEventCohLinkExceed);
535 evt.node = currentNode;
536 evt.link = currentLink;
537 evt.targetNode = token;
538 evt.totalNodes = pDat->NodesDiscovered;
539 evt.maxLinks = pDat->nb->maxLinks;
541 pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,
542 HT_EVENT_COH_LINK_EXCEED,
545 /* Force link and node loops to halt */
547 currentNode = pDat->NodesDiscovered;
551 pDat->PortList[pDat->TotalLinks*2].Type = PORTLIST_TYPE_CPU;
552 pDat->PortList[pDat->TotalLinks*2].Link = currentLink;
553 pDat->PortList[pDat->TotalLinks*2].NodeID = currentNode;
555 pDat->PortList[pDat->TotalLinks*2+1].Type = PORTLIST_TYPE_CPU;
556 pDat->PortList[pDat->TotalLinks*2+1].Link = pDat->nb->readDefLnk(currentNode+1, pDat->nb);
557 pDat->PortList[pDat->TotalLinks*2+1].NodeID = token;
561 if ( !pDat->sysMatrix[currentNode][token] )
563 pDat->sysDegree[currentNode]++;
564 pDat->sysDegree[token]++;
565 pDat->sysMatrix[currentNode][token] = TRUE;
566 pDat->sysMatrix[token][currentNode] = TRUE;
574 /***************************************************************************
575 *** ISOMORPHISM BASED ROUTING TABLE GENERATION CODE ***
576 ***************************************************************************/
578 /*----------------------------------------------------------------------------------------
580 * isoMorph(u8 i, sMainData *pDat)
583 * Is graphA isomorphic to graphB?
584 * if this function returns true, then Perm will contain the permutation
585 * required to transform graphB into graphA.
586 * We also use the degree of each node, that is the number of connections it has, to
587 * speed up rejection of non-isomorphic graphs (if there is a node in graphA with n
588 * connections, there must be at least one unmatched in graphB with n connections).
591 * @param[in] u8 i = the discovered node which we are trying to match
592 * with a permutation the topology
593 * @param[in]/@param[out] sMainData* pDat = our global state, degree and adjacency matrix,
594 * output a permutation if successful
595 * @param[out] BOOL results = the graphs are (or are not) isomorphic
596 * ---------------------------------------------------------------------------------------
598 BOOL isoMorph(u8 i, sMainData *pDat)
603 /* We have only been called if nodecnt == pSelected->size ! */
604 nodecnt = pDat->NodesDiscovered+1;
608 /* Keep building the permutation */
609 for (j = 0; j < nodecnt; j++)
611 /* Make sure the degree matches */
612 if (pDat->sysDegree[i] != pDat->dbDegree[j])
615 /* Make sure that j hasn't been used yet (ought to use a "used" */
616 /* array instead, might be faster) */
617 for (k = 0; k < i; k++)
619 if (pDat->Perm[k] == j)
625 if (isoMorph(i+1, pDat))
630 /* Test to see if the permutation is isomorphic */
631 for (j = 0; j < nodecnt; j++)
633 for (k = 0; k < nodecnt; k++)
635 if ( pDat->sysMatrix[j][k] !=
636 pDat->dbMatrix[pDat->Perm[j]][pDat->Perm[k]] )
645 /*----------------------------------------------------------------------------------------
647 * lookupComputeAndLoadRoutingTables(sMainData *pDat)
650 * Using the description of the fabric topology we discovered, try to find a match
651 * among the supported topologies. A supported topology description matches
652 * the discovered fabric if the nodes can be matched in such a way that all the nodes connected
653 * in one set are exactly the nodes connected in the other (formally, that the graphs are
654 * isomorphic). Which links are used is not really important to matching. If the graphs
655 * match, then there is a permutation of one that translates the node positions and linkages
658 * In order to make the isomorphism test efficient, we test for matched number of nodes
659 * (a 4 node fabric is not isomorphic to a 2 node topology), and provide degrees of nodes
660 * to the isomorphism test.
662 * The generic routing table solution for any topology is predetermined and represented
663 * as part of the topology. The permutation we computed tells us how to interpret the
664 * routing onto the fabric we discovered. We do this working backward from the last
665 * node discovered to the BSP, writing the routing tables as we go.
668 * @param[in] sMainData* pDat = our global state, the discovered fabric,
669 * @param[out] degree matrix, permutation
670 * ---------------------------------------------------------------------------------------
672 void lookupComputeAndLoadRoutingTables(sMainData *pDat)
679 size = pDat->NodesDiscovered + 1;
680 /* Use the provided topology list or the internal, default one. */
681 pTopologyList = pDat->HtBlock->topolist;
682 if (pTopologyList == NULL)
684 getAmdTopolist(&pTopologyList);
687 pSelected = *pTopologyList;
688 while (pSelected != NULL)
690 if (graphHowManyNodes(pSelected) == size)
692 /* Build Degree vector and Adjency Matrix for this entry */
693 for (i = 0; i < size; i++)
695 pDat->dbDegree[i] = 0;
696 for (j = 0; j < size; j++)
698 if (graphIsAdjacent(pSelected, i, j))
700 pDat->dbMatrix[i][j] = 1;
705 pDat->dbMatrix[i][j] = 0;
709 if (isoMorph(0, pDat))
710 break; /* A matching topology was found */
714 pSelected = *pTopologyList;
717 if (pSelected != NULL)
719 /* Compute the reverse Permutation */
720 for (i = 0; i < size; i++)
722 pDat->ReversePerm[pDat->Perm[i]] = i;
725 /* Start with the last discovered node, and move towards the BSP */
726 for (i = size-1; i >= 0; i--)
728 for (j = 0; j < size; j++)
730 u8 ReqTargetLink, RspTargetLink;
731 u8 ReqTargetNode, RspTargetNode;
733 u8 AbstractBcTargetNodes = graphGetBc(pSelected, pDat->Perm[i], pDat->Perm[j]);
734 u32 BcTargetLinks = 0;
736 for (k = 0; k < MAX_NODES; k++)
738 if (AbstractBcTargetNodes & ((u32)1<<k))
740 BcTargetLinks |= (u32)1 << convertNodeToLink(i, pDat->ReversePerm[k], pDat);
746 ReqTargetLink = ROUTETOSELF;
747 RspTargetLink = ROUTETOSELF;
751 ReqTargetNode = graphGetReq(pSelected, pDat->Perm[i], pDat->Perm[j]);
752 ReqTargetLink = convertNodeToLink(i, pDat->ReversePerm[ReqTargetNode], pDat);
754 RspTargetNode = graphGetRsp(pSelected, pDat->Perm[i], pDat->Perm[j]);
755 RspTargetLink = convertNodeToLink(i, pDat->ReversePerm[RspTargetNode], pDat);
758 pDat->nb->writeFullRoutingTable(i, j, ReqTargetLink, RspTargetLink, BcTargetLinks, pDat->nb);
760 /* Clean up discovery 'footprint' that otherwise remains in the routing table. It didn't hurt
761 * anything, but might cause confusion during debug and validation. Do this by setting the
762 * route back to all self routes. Since it's the node that would be one more than actually installed,
763 * this only applies if less than maxNodes were found.
765 if (size < pDat->nb->maxNodes)
767 pDat->nb->writeFullRoutingTable(i, size, ROUTETOSELF, ROUTETOSELF, 0, pDat->nb);
775 * No Matching Topology was found
777 * Auto recovery doesn't seem likely, Force boot as 1P.
778 * For reporting, logging, provide number of nodes
779 * If not implemented or returns, boot as BSP uniprocessor.
781 if (pDat->HtBlock->AMD_CB_EventNotify)
783 sHtEventCohNoTopology evt;
784 evt.eSize = sizeof(sHtEventCohNoTopology);
785 evt.totalNodes = pDat->NodesDiscovered;
787 pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,
788 HT_EVENT_COH_NO_TOPOLOGY,
793 pDat->NodesDiscovered = 0;
794 pDat->TotalLinks = 0;
795 pDat->nb->enableRoutingTables(0, pDat->nb);
798 #endif /* HT_BUILD_NC_ONLY */
801 /*----------------------------------------------------------------------------------------
803 * finializeCoherentInit(sMainData *pDat)
806 * Find the total number of cores and update the number of nodes and cores in all cpus.
807 * Limit cpu config access to installed cpus.
810 * @param[in] sMainData* pDat = our global state, number of nodes discovered.
811 * ---------------------------------------------------------------------------------------
813 void finializeCoherentInit(sMainData *pDat)
818 for (curNode = 0; curNode < pDat->NodesDiscovered+1; curNode++)
820 totalCores += pDat->nb->getNumCoresOnNode(curNode, pDat->nb);
823 for (curNode = 0; curNode < pDat->NodesDiscovered+1; curNode++)
825 pDat->nb->setTotalNodesAndCores(curNode, pDat->NodesDiscovered+1, totalCores, pDat->nb);
828 for (curNode = 0; curNode < pDat->NodesDiscovered+1; curNode++)
830 pDat->nb->limitNodes(curNode, pDat->nb);
835 /*----------------------------------------------------------------------------------------
837 * coherentInit(sMainData *pDat)
840 * Perform discovery and initialization of the coherent fabric.
843 * @param[in] sMainData* pDat = our global state
844 * ---------------------------------------------------------------------------------------
846 void coherentInit(sMainData *pDat)
850 #ifdef HT_BUILD_NC_ONLY
851 /* Replace discovery process with:
852 * No other nodes, no coherent links
853 * Enable routing tables on currentNode, for power on self route
855 pDat->NodesDiscovered = 0;
856 pDat->TotalLinks = 0;
857 pDat->nb->enableRoutingTables(0, pDat->nb);
859 pDat->NodesDiscovered = 0;
860 pDat->TotalLinks = 0;
861 for (i = 0; i < MAX_NODES; i++)
863 pDat->sysDegree[i] = 0;
864 for (j = 0; j < MAX_NODES; j++)
866 pDat->sysMatrix[i][j] = 0;
870 htDiscoveryFloodFill(pDat);
871 lookupComputeAndLoadRoutingTables(pDat);
873 finializeCoherentInit(pDat);
876 /***************************************************************************
877 *** Non-coherent init code ***
879 ***************************************************************************/
880 /*----------------------------------------------------------------------------------------
882 * processLink(u8 node, u8 link, sMainData *pDat)
885 * Process a non-coherent link, enabling a range of bus numbers, and setting the device
886 * ID for all devices found
889 * @param[in] u8 node = Node on which to process nc init
890 * @param[in] u8 link = The non-coherent link on that node
891 * @param[in] sMainData* pDat = our global state
892 * ---------------------------------------------------------------------------------------
894 void processLink(u8 node, u8 link, sMainData *pDat)
904 SBDFO lastSBDFO = ILLEGAL_SBDFO;
907 ASSERT(node < pDat->nb->maxNodes && link < pDat->nb->maxLinks);
909 if ((pDat->HtBlock->AMD_CB_OverrideBusNumbers == NULL)
910 || !pDat->HtBlock->AMD_CB_OverrideBusNumbers(node, link, &secBus, &subBus))
912 /* Assign Bus numbers */
913 if (pDat->AutoBusCurrent >= pDat->HtBlock->AutoBusMax)
915 /* If we run out of Bus Numbers notify, if call back unimplemented or if it
916 * returns, skip this chain
918 if (pDat->HtBlock->AMD_CB_EventNotify)
920 sHTEventNcohBusMaxExceed evt;
921 evt.eSize = sizeof(sHTEventNcohBusMaxExceed);
924 evt.bus = pDat->AutoBusCurrent;
926 pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,HT_EVENT_NCOH_BUS_MAX_EXCEED,(u8 *)&evt);
932 if (pDat->UsedCfgMapEntires >= 4)
934 /* If we have used all the PCI Config maps we can't add another chain.
935 * Notify and if call back is unimplemented or returns, skip this chain.
937 if (pDat->HtBlock->AMD_CB_EventNotify)
939 sHtEventNcohCfgMapExceed evt;
940 evt.eSize = sizeof(sHtEventNcohCfgMapExceed);
944 pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,
945 HT_EVENT_NCOH_CFG_MAP_EXCEED,
952 secBus = pDat->AutoBusCurrent;
953 subBus = secBus + pDat->HtBlock->AutoBusIncrement-1;
954 pDat->AutoBusCurrent += pDat->HtBlock->AutoBusIncrement;
957 pDat->nb->setCFGAddrMap(pDat->UsedCfgMapEntires, secBus, subBus, node, link, pDat, pDat->nb);
958 pDat->UsedCfgMapEntires++;
960 if ((pDat->HtBlock->AMD_CB_ManualBUIDSwapList != NULL)
961 && pDat->HtBlock->AMD_CB_ManualBUIDSwapList(node, link, &pSwapPtr))
963 /* Manual non-coherent BUID assignment */
965 /* Assign BUID's per manual override */
966 while (*pSwapPtr != 0xFF)
968 currentPtr = MAKE_SBDFO(0, secBus, *pSwapPtr, 0, 0);
973 AmdPCIFindNextCap(¤tPtr);
974 ASSERT(currentPtr != ILLEGAL_SBDFO);
975 AmdPCIRead(currentPtr, &temp);
976 } while (!IS_HT_SLAVE_CAPABILITY(temp));
978 currentBUID = *pSwapPtr;
980 AmdPCIWriteBits(currentPtr, 20, 16, ¤tBUID);
983 /* Build chain of devices */
986 while (*pSwapPtr != 0xFF)
988 pDat->PortList[pDat->TotalLinks*2].NodeID = node;
991 pDat->PortList[pDat->TotalLinks*2].Type = PORTLIST_TYPE_CPU;
992 pDat->PortList[pDat->TotalLinks*2].Link = link;
996 pDat->PortList[pDat->TotalLinks*2].Type = PORTLIST_TYPE_IO;
997 pDat->PortList[pDat->TotalLinks*2].Link = 1-lastLink;
998 pDat->PortList[pDat->TotalLinks*2].HostLink = link;
999 pDat->PortList[pDat->TotalLinks*2].HostDepth = depth-1;
1000 pDat->PortList[pDat->TotalLinks*2].Pointer = lastSBDFO;
1003 pDat->PortList[pDat->TotalLinks*2+1].Type = PORTLIST_TYPE_IO;
1004 pDat->PortList[pDat->TotalLinks*2+1].NodeID = node;
1005 pDat->PortList[pDat->TotalLinks*2+1].HostLink = link;
1006 pDat->PortList[pDat->TotalLinks*2+1].HostDepth = depth;
1008 currentPtr = MAKE_SBDFO(0, secBus, (*pSwapPtr & 0x3F), 0, 0);
1011 AmdPCIFindNextCap(¤tPtr);
1012 ASSERT(currentPtr != ILLEGAL_SBDFO);
1013 AmdPCIRead(currentPtr, &temp);
1014 } while (!IS_HT_SLAVE_CAPABILITY(temp));
1015 pDat->PortList[pDat->TotalLinks*2+1].Pointer = currentPtr;
1016 lastSBDFO = currentPtr;
1018 /* Bit 6 indicates whether orientation override is desired.
1019 * Bit 7 indicates the upstream link if overriding.
1021 /* assert catches at least the one known incorrect setting */
1022 ASSERT ((*pSwapPtr & 0x40) || (!(*pSwapPtr & 0x80)));
1023 if (*pSwapPtr & 0x40)
1025 /* Override the device's orientation */
1026 lastLink = *pSwapPtr >> 7;
1030 /* Detect the device's orientation */
1031 AmdPCIReadBits(currentPtr, 26, 26, &temp);
1032 lastLink = (u8)temp;
1034 pDat->PortList[pDat->TotalLinks*2+1].Link = lastLink;
1043 /* Automatic non-coherent device detection */
1048 currentPtr = MAKE_SBDFO(0, secBus, 0, 0, 0);
1050 AmdPCIRead(currentPtr, &temp);
1051 if (temp == 0xFFFFFFFF)
1052 /* No device found at currentPtr */
1055 if (pDat->TotalLinks == MAX_PLATFORM_LINKS)
1058 * Exceeded our capacity to describe all non-coherent links found in the system.
1060 * Auto recovery is not possible because data space is already all used.
1062 if (pDat->HtBlock->AMD_CB_EventNotify)
1064 sHtEventNcohLinkExceed evt;
1065 evt.eSize = sizeof(sHtEventNcohLinkExceed);
1069 evt.maxLinks = pDat->nb->maxLinks;
1071 pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,
1072 HT_EVENT_NCOH_LINK_EXCEED,
1075 /* Force link loop to halt */
1080 pDat->PortList[pDat->TotalLinks*2].NodeID = node;
1083 pDat->PortList[pDat->TotalLinks*2].Type = PORTLIST_TYPE_CPU;
1084 pDat->PortList[pDat->TotalLinks*2].Link = link;
1088 pDat->PortList[pDat->TotalLinks*2].Type = PORTLIST_TYPE_IO;
1089 pDat->PortList[pDat->TotalLinks*2].Link = 1-lastLink;
1090 pDat->PortList[pDat->TotalLinks*2].HostLink = link;
1091 pDat->PortList[pDat->TotalLinks*2].HostDepth = depth-1;
1092 pDat->PortList[pDat->TotalLinks*2].Pointer = lastSBDFO;
1095 pDat->PortList[pDat->TotalLinks*2+1].Type = PORTLIST_TYPE_IO;
1096 pDat->PortList[pDat->TotalLinks*2+1].NodeID = node;
1097 pDat->PortList[pDat->TotalLinks*2+1].HostLink = link;
1098 pDat->PortList[pDat->TotalLinks*2+1].HostDepth = depth;
1102 AmdPCIFindNextCap(¤tPtr);
1103 ASSERT(currentPtr != ILLEGAL_SBDFO);
1104 AmdPCIRead(currentPtr, &temp);
1105 } while (!IS_HT_SLAVE_CAPABILITY(temp));
1107 AmdPCIReadBits(currentPtr, 25, 21, &unitIDcnt);
1108 if ((unitIDcnt + currentBUID > 31) || ((secBus == 0) && (unitIDcnt + currentBUID > 24)))
1110 /* An error handler for the case where we run out of BUID's on a chain */
1111 if (pDat->HtBlock->AMD_CB_EventNotify)
1113 sHtEventNcohBuidExceed evt;
1114 evt.eSize = sizeof(sHtEventNcohBuidExceed);
1118 evt.currentBUID = (uint8)currentBUID;
1119 evt.unitCount = (uint8)unitIDcnt;
1121 pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,HT_EVENT_NCOH_BUID_EXCEED,(u8 *)&evt);
1126 AmdPCIWriteBits(currentPtr, 20, 16, ¤tBUID);
1129 currentPtr += MAKE_SBDFO(0, 0, currentBUID, 0, 0);
1130 AmdPCIReadBits(currentPtr, 20, 16, &temp);
1131 if (temp != currentBUID)
1133 /* An error handler for this critical error */
1134 if (pDat->HtBlock->AMD_CB_EventNotify)
1136 sHtEventNcohDeviceFailed evt;
1137 evt.eSize = sizeof(sHtEventNcohDeviceFailed);
1141 evt.attemptedBUID = (uint8)currentBUID;
1143 pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,HT_EVENT_NCOH_DEVICE_FAILED,(u8 *)&evt);
1149 AmdPCIReadBits(currentPtr, 26, 26, &temp);
1150 pDat->PortList[pDat->TotalLinks*2+1].Link = (u8)temp;
1151 pDat->PortList[pDat->TotalLinks*2+1].Pointer = currentPtr;
1153 lastLink = (u8)temp;
1154 lastSBDFO = currentPtr;
1158 currentBUID += unitIDcnt;
1160 if (pDat->HtBlock->AMD_CB_EventNotify)
1162 /* Provide information on automatic device results */
1163 sHtEventNcohAutoDepth evt;
1164 evt.eSize = sizeof(sHtEventNcohAutoDepth);
1167 evt.depth = (depth - 1);
1169 pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_INFO,HT_EVENT_NCOH_AUTO_DEPTH,(u8 *)&evt);
1175 /*----------------------------------------------------------------------------------------
1177 * ncInit(sMainData *pDat)
1180 * Initialize the non-coherent fabric. Begin with the compat link on the BSP, then
1181 * find and initialize all other non-coherent chains.
1184 * @param[in] sMainData* pDat = our global state
1185 * ---------------------------------------------------------------------------------------
1187 void ncInit(sMainData *pDat)
1192 compatLink = pDat->nb->readSbLink(pDat->nb);
1193 processLink(0, compatLink, pDat);
1195 for (node = 0; node <= pDat->NodesDiscovered; node++)
1197 for (link = 0; link < pDat->nb->maxLinks; link++)
1199 if (pDat->HtBlock->AMD_CB_IgnoreLink && pDat->HtBlock->AMD_CB_IgnoreLink(node, link))
1200 continue; /* Skip the link */
1202 if (node == 0 && link == compatLink)
1205 if (pDat->nb->readTrueLinkFailStatus(node, link, pDat, pDat->nb))
1208 if (pDat->nb->verifyLinkIsNonCoherent(node, link, pDat->nb))
1209 processLink(node, link, pDat);
1214 /***************************************************************************
1215 *** Link Optimization ***
1216 ***************************************************************************/
1218 /*----------------------------------------------------------------------------------------
1220 * regangLinks(sMainData *pDat)
1223 * Test the sublinks of a link to see if they qualify to be reganged. If they do,
1224 * update the port list data to indicate that this should be done. Note that no
1225 * actual hardware state is changed in this routine.
1228 * @param[in,out] sMainData* pDat = our global state
1229 * ---------------------------------------------------------------------------------------
1231 void regangLinks(sMainData *pDat)
1233 #ifndef HT_BUILD_NC_ONLY
1235 for (i = 0; i < pDat->TotalLinks*2; i += 2)
1237 ASSERT(pDat->PortList[i].Type < 2 && pDat->PortList[i].Link < pDat->nb->maxLinks); /* Data validation */
1238 ASSERT(pDat->PortList[i+1].Type < 2 && pDat->PortList[i+1].Link < pDat->nb->maxLinks); /* data validation */
1239 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 */
1241 /* Regang is false unless we pass all conditions below */
1242 pDat->PortList[i].SelRegang = FALSE;
1243 pDat->PortList[i+1].SelRegang = FALSE;
1245 if ( (pDat->PortList[i].Type != PORTLIST_TYPE_CPU) || (pDat->PortList[i+1].Type != PORTLIST_TYPE_CPU))
1246 continue; /* Only process cpu to cpu links */
1248 for (j = i+2; j < pDat->TotalLinks*2; j += 2)
1250 if ( (pDat->PortList[j].Type != PORTLIST_TYPE_CPU) || (pDat->PortList[j+1].Type != PORTLIST_TYPE_CPU) )
1251 continue; /* Only process cpu to cpu links */
1253 if (pDat->PortList[i].NodeID != pDat->PortList[j].NodeID)
1254 continue; /* Links must be from the same source */
1256 if (pDat->PortList[i+1].NodeID != pDat->PortList[j+1].NodeID)
1257 continue; /* Link must be to the same target */
1259 if ((pDat->PortList[i].Link & 3) != (pDat->PortList[j].Link & 3))
1260 continue; /* Ensure same source base port */
1262 if ((pDat->PortList[i+1].Link & 3) != (pDat->PortList[j+1].Link & 3))
1263 continue; /* Ensure same destination base port */
1265 if ((pDat->PortList[i].Link & 4) != (pDat->PortList[i+1].Link & 4))
1266 continue; /* Ensure sublink0 routes to sublink0 */
1268 ASSERT((pDat->PortList[j].Link & 4) == (pDat->PortList[j+1].Link & 4)); /* (therefore sublink1 routes to sublink1) */
1270 if (pDat->HtBlock->AMD_CB_SkipRegang &&
1271 pDat->HtBlock->AMD_CB_SkipRegang(pDat->PortList[i].NodeID,
1272 pDat->PortList[i].Link & 0x03,
1273 pDat->PortList[i+1].NodeID,
1274 pDat->PortList[i+1].Link & 0x03))
1276 continue; /* Skip regang */
1280 pDat->PortList[i].Link &= 0x03; /* Force to point to sublink0 */
1281 pDat->PortList[i+1].Link &= 0x03;
1282 pDat->PortList[i].SelRegang = TRUE; /* Enable link reganging */
1283 pDat->PortList[i+1].SelRegang = TRUE;
1284 pDat->PortList[i].PrvWidthOutCap = HT_WIDTH_16_BITS;
1285 pDat->PortList[i+1].PrvWidthOutCap = HT_WIDTH_16_BITS;
1286 pDat->PortList[i].PrvWidthInCap = HT_WIDTH_16_BITS;
1287 pDat->PortList[i+1].PrvWidthInCap = HT_WIDTH_16_BITS;
1289 /* Delete PortList[j, j+1], slow but easy to debug implementation */
1291 Amdmemcpy(&(pDat->PortList[j]), &(pDat->PortList[j+2]), sizeof(sPortDescriptor)*(pDat->TotalLinks*2-j));
1292 Amdmemset(&(pDat->PortList[pDat->TotalLinks*2]), INVALID_LINK, sizeof(sPortDescriptor)*2);
1294 /* //High performance, but would make debuging harder due to 'shuffling' of the records */
1295 /* //Amdmemcpy(PortList[TotalPorts-2], PortList[j], SIZEOF(sPortDescriptor)*2); */
1296 /* //TotalPorts -=2; */
1298 break; /* Exit loop, advance to PortList[i+2] */
1301 #endif /* HT_BUILD_NC_ONLY */
1304 /*----------------------------------------------------------------------------------------
1306 * selectOptimalWidthAndFrequency(sMainData *pDat)
1310 * Examine both sides of a link and determine the optimal frequency and width,
1311 * taking into account externally provided limits and enforcing any other limit
1312 * or matching rules as applicable except sublink balancing. Update the port
1313 * list date with the optimal settings.
1314 * Note no hardware state changes in this routine.
1317 * @param[in,out] sMainData* pDat = our global state, port list data
1318 * ---------------------------------------------------------------------------------------
1320 void selectOptimalWidthAndFrequency(sMainData *pDat)
1325 u8 cbPCBABDownstreamWidth;
1326 u8 cbPCBBAUpstreamWidth;
1328 for (i = 0; i < pDat->TotalLinks*2; i += 2)
1330 cbPCBFreqLimit = 0xFFFF;
1331 cbPCBABDownstreamWidth = 16;
1332 cbPCBBAUpstreamWidth = 16;
1334 if ( (pDat->PortList[i].Type == PORTLIST_TYPE_CPU) && (pDat->PortList[i+1].Type == PORTLIST_TYPE_CPU))
1336 if (pDat->HtBlock->AMD_CB_Cpu2CpuPCBLimits)
1338 pDat->HtBlock->AMD_CB_Cpu2CpuPCBLimits(
1339 pDat->PortList[i].NodeID,
1340 pDat->PortList[i].Link,
1341 pDat->PortList[i+1].NodeID,
1342 pDat->PortList[i+1].Link,
1343 &cbPCBABDownstreamWidth,
1344 &cbPCBBAUpstreamWidth, &cbPCBFreqLimit
1350 if (pDat->HtBlock->AMD_CB_IOPCBLimits)
1352 pDat->HtBlock->AMD_CB_IOPCBLimits(
1353 pDat->PortList[i+1].NodeID,
1354 pDat->PortList[i+1].HostLink,
1355 pDat->PortList[i+1].HostDepth,
1356 &cbPCBABDownstreamWidth,
1357 &cbPCBBAUpstreamWidth, &cbPCBFreqLimit
1363 temp = pDat->PortList[i].PrvFrequencyCap;
1364 temp &= pDat->PortList[i+1].PrvFrequencyCap;
1365 temp &= cbPCBFreqLimit;
1366 pDat->PortList[i].CompositeFrequencyCap = (u16)temp;
1367 pDat->PortList[i+1].CompositeFrequencyCap = (u16)temp;
1372 if (temp & ((u32)1 << j))
1376 pDat->PortList[i].SelFrequency = j;
1377 pDat->PortList[i+1].SelFrequency = j;
1379 temp = pDat->PortList[i].PrvWidthOutCap;
1380 if (pDat->PortList[i+1].PrvWidthInCap < temp)
1381 temp = pDat->PortList[i+1].PrvWidthInCap;
1382 if (cbPCBABDownstreamWidth < temp)
1383 temp = cbPCBABDownstreamWidth;
1384 pDat->PortList[i].SelWidthOut = (u8)temp;
1385 pDat->PortList[i+1].SelWidthIn = (u8)temp;
1387 temp = pDat->PortList[i].PrvWidthInCap;
1388 if (pDat->PortList[i+1].PrvWidthOutCap < temp)
1389 temp = pDat->PortList[i+1].PrvWidthOutCap;
1390 if (cbPCBBAUpstreamWidth < temp)
1391 temp = cbPCBBAUpstreamWidth;
1392 pDat->PortList[i].SelWidthIn = (u8)temp;
1393 pDat->PortList[i+1].SelWidthOut = (u8)temp;
1398 /*----------------------------------------------------------------------------------------
1400 * hammerSublinkFixup(sMainData *pDat)
1403 * Iterate through all links, checking the frequency of each sublink pair. Make the
1404 * adjustment to the port list data so that the frequencies are at a valid ratio,
1405 * reducing frequency as needed to achieve this. (All links support the minimum 200 MHz
1406 * frequency.) Repeat the above until no adjustments are needed.
1407 * Note no hardware state changes in this routine.
1410 * @param[in,out] sMainData* pDat = our global state, link state and port list
1411 * ---------------------------------------------------------------------------------------
1413 void hammerSublinkFixup(sMainData *pDat)
1415 #ifndef HT_BUILD_NC_ONLY
1417 BOOL changes, downgrade;
1427 for (i = 0; i < pDat->TotalLinks*2; i++)
1429 if (pDat->PortList[i].Type != PORTLIST_TYPE_CPU) /* Must be a CPU link */
1431 if (pDat->PortList[i].Link < 4) /* Only look for for sublink1's */
1434 for (j = 0; j < pDat->TotalLinks*2; j++)
1436 /* Step 1. Find the matching sublink0 */
1437 if (pDat->PortList[j].Type != PORTLIST_TYPE_CPU)
1439 if (pDat->PortList[j].NodeID != pDat->PortList[i].NodeID)
1441 if (pDat->PortList[j].Link != (pDat->PortList[i].Link & 0x03))
1444 /* Step 2. Check for an illegal frequency ratio */
1445 if (pDat->PortList[i].SelFrequency >= pDat->PortList[j].SelFrequency)
1448 hiFreq = pDat->PortList[i].SelFrequency;
1449 loFreq = pDat->PortList[j].SelFrequency;
1454 hiFreq = pDat->PortList[j].SelFrequency;
1455 loFreq = pDat->PortList[i].SelFrequency;
1458 if (hiFreq == loFreq)
1459 break; /* The frequencies are 1:1, no need to do anything */
1465 if ((loFreq != 7) && /* {13, 7} 2400MHz / 1200MHz 2:1 */
1466 (loFreq != 4) && /* {13, 4} 2400MHz / 600MHz 4:1 */
1467 (loFreq != 2) ) /* {13, 2} 2400MHz / 400MHz 6:1 */
1470 else if (hiFreq == 11)
1472 if ((loFreq != 6)) /* {11, 6} 2000MHz / 1000MHz 2:1 */
1475 else if (hiFreq == 9)
1477 if ((loFreq != 5) && /* { 9, 5} 1600MHz / 800MHz 2:1 */
1478 (loFreq != 2) && /* { 9, 2} 1600MHz / 400MHz 4:1 */
1479 (loFreq != 0) ) /* { 9, 0} 1600MHz / 200Mhz 8:1 */
1482 else if (hiFreq == 7)
1484 if ((loFreq != 4) && /* { 7, 4} 1200MHz / 600MHz 2:1 */
1485 (loFreq != 0) ) /* { 7, 0} 1200MHz / 200MHz 6:1 */
1488 else if (hiFreq == 5)
1490 if ((loFreq != 2) && /* { 5, 2} 800MHz / 400MHz 2:1 */
1491 (loFreq != 0) ) /* { 5, 0} 800MHz / 200MHz 4:1 */
1494 else if (hiFreq == 2)
1496 if ((loFreq != 0)) /* { 2, 0} 400MHz / 200MHz 2:1 */
1501 downgrade = TRUE; /* no legal ratios for hiFreq */
1504 /* Step 3. Downgrade the higher of the two frequencies, and set nochanges to FALSE */
1507 /* Although the problem was with the port specified by hiIndex, we need to */
1508 /* downgrade both ends of the link. */
1509 hiIndex = hiIndex & 0xFE; /* Select the 'upstream' (i.e. even) port */
1511 temp = pDat->PortList[hiIndex].CompositeFrequencyCap;
1513 /* Remove hiFreq from the list of valid frequencies */
1514 temp = temp & ~((uint32)1 << hiFreq);
1516 pDat->PortList[hiIndex].CompositeFrequencyCap = (uint16)temp;
1517 pDat->PortList[hiIndex+1].CompositeFrequencyCap = (uint16)temp;
1521 if (temp & ((u32)1 << k))
1525 pDat->PortList[hiIndex].SelFrequency = k;
1526 pDat->PortList[hiIndex+1].SelFrequency = k;
1532 } while (changes); /* Repeat until a valid configuration is reached */
1533 #endif /* HT_BUILD_NC_ONLY */
1536 /*----------------------------------------------------------------------------------------
1538 * linkOptimization(sMainData *pDat)
1541 * Based on link capabilities, apply optimization rules to come up with the real best
1542 * settings, including several external limit decision from call backs. This includes
1543 * handling of sublinks. Finally, after the port list data is updated, set the hardware
1544 * state for all links.
1547 * @param[in] sMainData* pDat = our global state
1548 * ---------------------------------------------------------------------------------------
1550 void linkOptimization(sMainData *pDat)
1552 pDat->nb->gatherLinkData(pDat, pDat->nb);
1554 selectOptimalWidthAndFrequency(pDat);
1555 hammerSublinkFixup(pDat);
1556 pDat->nb->setLinkData(pDat, pDat->nb);
1560 /*----------------------------------------------------------------------------------------
1562 * trafficDistribution(sMainData *pDat)
1565 * In the case of a two node system with both sublinks used, enable the traffic
1566 * distribution feature.
1569 * @param[in] sMainData* pDat = our global state, port list data
1570 * ---------------------------------------------------------------------------------------
1572 void trafficDistribution(sMainData *pDat)
1574 #ifndef HT_BUILD_NC_ONLY
1575 u32 links01, links10;
1579 /* Traffic Distribution is only used when there are exactly two nodes in the system */
1580 if (pDat->NodesDiscovered+1 != 2)
1586 for (i = 0; i < pDat->TotalLinks*2; i += 2)
1588 if ((pDat->PortList[i].Type == PORTLIST_TYPE_CPU) && (pDat->PortList[i+1].Type == PORTLIST_TYPE_CPU))
1590 links01 |= (u32)1 << pDat->PortList[i].Link;
1591 links10 |= (u32)1 << pDat->PortList[i+1].Link;
1595 ASSERT(linkCount != 0);
1597 return; /* Don't setup Traffic Distribution if only one link is being used */
1599 pDat->nb->writeTrafficDistribution(links01, links10, pDat->nb);
1600 #endif /* HT_BUILD_NC_ONLY */
1603 /*----------------------------------------------------------------------------------------
1605 * tuning(sMainData *pDat)
1608 * Handle system and performance tunings, such as traffic distribution, fifo and
1609 * buffer tuning, and special config tunings.
1612 * @param[in] sMainData* pDat = our global state, port list data
1613 * ---------------------------------------------------------------------------------------
1615 void tuning(sMainData *pDat)
1619 /* See if traffic distribution can be done and do it if so
1620 * or allow system specific customization
1622 if ((pDat->HtBlock->AMD_CB_CustomizeTrafficDistribution == NULL)
1623 || !pDat->HtBlock->AMD_CB_CustomizeTrafficDistribution())
1625 trafficDistribution(pDat);
1628 /* For each node, invoke northbridge specific buffer tunings or
1629 * system specific customizations.
1631 for (i=0; i < pDat->NodesDiscovered + 1; i++)
1633 if ((pDat->HtBlock->AMD_CB_CustomizeBuffers == NULL)
1634 || !pDat->HtBlock->AMD_CB_CustomizeBuffers(i))
1636 pDat->nb->bufferOptimizations(i, pDat, pDat->nb);
1641 /*----------------------------------------------------------------------------------------
1646 * Perform any general sanity checks which should prevent HT from running if they fail.
1647 * Currently only the "Must run on BSP only" check.
1650 * @param[out] result BOOL = true if check is ok, false if it failed
1651 * ---------------------------------------------------------------------------------------
1653 BOOL isSanityCheckOk()
1657 AmdMSRRead(APIC_Base, &qValue);
1659 return ((qValue.lo & ((u32)1 << APIC_Base_BSP)) != 0);
1662 /***************************************************************************
1663 *** HT Initialize ***
1664 ***************************************************************************/
1666 /*----------------------------------------------------------------------------------------
1668 * htInitialize(AMD_HTBLOCK *pBlock)
1671 * This is the top level external interface for Hypertransport Initialization.
1672 * Create our initial internal state, initialize the coherent fabric,
1673 * initialize the non-coherent chains, and perform any required fabric tuning or
1677 * @param[in] AMD_HTBLOCK* pBlock = Our Initial State including possible
1678 * topologies and routings, non coherent bus
1679 * assignment info, and actual
1680 * wrapper or OEM call back routines.
1681 * ---------------------------------------------------------------------------------------
1683 void amdHtInitialize(AMD_HTBLOCK *pBlock)
1688 if (isSanityCheckOk())
1690 newNorthBridge(0, &nb);
1692 pDat.HtBlock = pBlock;
1694 pDat.sysMpCap = nb.maxNodes;
1695 nb.isCapable(0, &pDat, pDat.nb);
1696 coherentInit(&pDat);
1698 pDat.AutoBusCurrent = pBlock->AutoBusStart;
1699 pDat.UsedCfgMapEntires = 0;
1701 linkOptimization(&pDat);