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 static 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 static 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 static 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 static 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 static 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 static 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 static 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 static 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 static 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 static 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 static 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 static void coherentInit(sMainData
*pDat
)
848 #ifdef HT_BUILD_NC_ONLY
849 /* Replace discovery process with:
850 * No other nodes, no coherent links
851 * Enable routing tables on currentNode, for power on self route
853 pDat
->NodesDiscovered
= 0;
854 pDat
->TotalLinks
= 0;
855 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 static 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 static 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 static 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 static void selectOptimalWidthAndFrequency(sMainData
*pDat
)
1325 u8 cbPCBABDownstreamWidth
;
1326 u8 cbPCBBAUpstreamWidth
;
1328 for (i
= 0; i
< pDat
->TotalLinks
*2; i
+= 2)
1330 #if CONFIG_EXPERT && CONFIG_LIMIT_HT_SPEED_200
1331 cbPCBFreqLimit
= 0x0001;
1332 #elif CONFIG_EXPERT && CONFIG_LIMIT_HT_SPEED_300
1333 cbPCBFreqLimit
= 0x0003;
1334 #elif CONFIG_EXPERT && CONFIG_LIMIT_HT_SPEED_400
1335 cbPCBFreqLimit
= 0x0007;
1336 #elif CONFIG_EXPERT && CONFIG_LIMIT_HT_SPEED_500
1337 cbPCBFreqLimit
= 0x000F;
1338 #elif CONFIG_EXPERT && CONFIG_LIMIT_HT_SPEED_600
1339 cbPCBFreqLimit
= 0x001F;
1340 #elif CONFIG_EXPERT && CONFIG_LIMIT_HT_SPEED_800
1341 cbPCBFreqLimit
= 0x003F;
1342 #elif CONFIG_EXPERT && CONFIG_LIMIT_HT_SPEED_1000
1343 cbPCBFreqLimit
= 0x007F;
1344 #elif CONFIG_EXPERT && CONFIG_LIMIT_HT_SPEED_1200
1345 cbPCBFreqLimit
= 0x00FF;
1346 #elif CONFIG_EXPERT && CONFIG_LIMIT_HT_SPEED_1400
1347 cbPCBFreqLimit
= 0x01FF;
1348 #elif CONFIG_EXPERT && CONFIG_LIMIT_HT_SPEED_1600
1349 cbPCBFreqLimit
= 0x03FF;
1350 #elif CONFIG_EXPERT && CONFIG_LIMIT_HT_SPEED_1800
1351 cbPCBFreqLimit
= 0x07FF;
1352 #elif CONFIG_EXPERT && CONFIG_LIMIT_HT_SPEED_2000
1353 cbPCBFreqLimit
= 0x0FFF;
1354 #elif CONFIG_EXPERT && CONFIG_LIMIT_HT_SPEED_2200
1355 cbPCBFreqLimit
= 0x1FFF;
1356 #elif CONFIG_EXPERT && CONFIG_LIMIT_HT_SPEED_2400
1357 cbPCBFreqLimit
= 0x3FFF;
1358 #elif CONFIG_EXPERT && CONFIG_LIMIT_HT_SPEED_2600
1359 cbPCBFreqLimit
= 0x7FFF;
1361 cbPCBFreqLimit
= 0xFFFF; // Maximum allowed by autoconfiguration
1364 #if CONFIG_EXPERT && CONFIG_LIMIT_HT_DOWN_WIDTH_8
1365 cbPCBABDownstreamWidth
= 8;
1367 cbPCBABDownstreamWidth
= 16;
1370 #if CONFIG_EXPERT && CONFIG_LIMIT_HT_UP_WIDTH_8
1371 cbPCBBAUpstreamWidth
= 8;
1373 cbPCBBAUpstreamWidth
= 16;
1376 if ( (pDat
->PortList
[i
].Type
== PORTLIST_TYPE_CPU
) && (pDat
->PortList
[i
+1].Type
== PORTLIST_TYPE_CPU
))
1378 if (pDat
->HtBlock
->AMD_CB_Cpu2CpuPCBLimits
)
1380 pDat
->HtBlock
->AMD_CB_Cpu2CpuPCBLimits(
1381 pDat
->PortList
[i
].NodeID
,
1382 pDat
->PortList
[i
].Link
,
1383 pDat
->PortList
[i
+1].NodeID
,
1384 pDat
->PortList
[i
+1].Link
,
1385 &cbPCBABDownstreamWidth
,
1386 &cbPCBBAUpstreamWidth
, &cbPCBFreqLimit
1392 if (pDat
->HtBlock
->AMD_CB_IOPCBLimits
)
1394 pDat
->HtBlock
->AMD_CB_IOPCBLimits(
1395 pDat
->PortList
[i
+1].NodeID
,
1396 pDat
->PortList
[i
+1].HostLink
,
1397 pDat
->PortList
[i
+1].HostDepth
,
1398 &cbPCBABDownstreamWidth
,
1399 &cbPCBBAUpstreamWidth
, &cbPCBFreqLimit
1405 temp
= pDat
->PortList
[i
].PrvFrequencyCap
;
1406 temp
&= pDat
->PortList
[i
+1].PrvFrequencyCap
;
1407 temp
&= cbPCBFreqLimit
;
1408 pDat
->PortList
[i
].CompositeFrequencyCap
= (u16
)temp
;
1409 pDat
->PortList
[i
+1].CompositeFrequencyCap
= (u16
)temp
;
1414 if (temp
& ((u32
)1 << j
))
1418 pDat
->PortList
[i
].SelFrequency
= j
;
1419 pDat
->PortList
[i
+1].SelFrequency
= j
;
1421 temp
= pDat
->PortList
[i
].PrvWidthOutCap
;
1422 if (pDat
->PortList
[i
+1].PrvWidthInCap
< temp
)
1423 temp
= pDat
->PortList
[i
+1].PrvWidthInCap
;
1424 if (cbPCBABDownstreamWidth
< temp
)
1425 temp
= cbPCBABDownstreamWidth
;
1426 pDat
->PortList
[i
].SelWidthOut
= (u8
)temp
;
1427 pDat
->PortList
[i
+1].SelWidthIn
= (u8
)temp
;
1429 temp
= pDat
->PortList
[i
].PrvWidthInCap
;
1430 if (pDat
->PortList
[i
+1].PrvWidthOutCap
< temp
)
1431 temp
= pDat
->PortList
[i
+1].PrvWidthOutCap
;
1432 if (cbPCBBAUpstreamWidth
< temp
)
1433 temp
= cbPCBBAUpstreamWidth
;
1434 pDat
->PortList
[i
].SelWidthIn
= (u8
)temp
;
1435 pDat
->PortList
[i
+1].SelWidthOut
= (u8
)temp
;
1440 /*----------------------------------------------------------------------------------------
1442 * hammerSublinkFixup(sMainData *pDat)
1445 * Iterate through all links, checking the frequency of each sublink pair. Make the
1446 * adjustment to the port list data so that the frequencies are at a valid ratio,
1447 * reducing frequency as needed to achieve this. (All links support the minimum 200 MHz
1448 * frequency.) Repeat the above until no adjustments are needed.
1449 * Note no hardware state changes in this routine.
1452 * @param[in,out] sMainData* pDat = our global state, link state and port list
1453 * ---------------------------------------------------------------------------------------
1455 static void hammerSublinkFixup(sMainData
*pDat
)
1457 #ifndef HT_BUILD_NC_ONLY
1459 BOOL changes
, downgrade
;
1469 for (i
= 0; i
< pDat
->TotalLinks
*2; i
++)
1471 if (pDat
->PortList
[i
].Type
!= PORTLIST_TYPE_CPU
) /* Must be a CPU link */
1473 if (pDat
->PortList
[i
].Link
< 4) /* Only look for for sublink1's */
1476 for (j
= 0; j
< pDat
->TotalLinks
*2; j
++)
1478 /* Step 1. Find the matching sublink0 */
1479 if (pDat
->PortList
[j
].Type
!= PORTLIST_TYPE_CPU
)
1481 if (pDat
->PortList
[j
].NodeID
!= pDat
->PortList
[i
].NodeID
)
1483 if (pDat
->PortList
[j
].Link
!= (pDat
->PortList
[i
].Link
& 0x03))
1486 /* Step 2. Check for an illegal frequency ratio */
1487 if (pDat
->PortList
[i
].SelFrequency
>= pDat
->PortList
[j
].SelFrequency
)
1490 hiFreq
= pDat
->PortList
[i
].SelFrequency
;
1491 loFreq
= pDat
->PortList
[j
].SelFrequency
;
1496 hiFreq
= pDat
->PortList
[j
].SelFrequency
;
1497 loFreq
= pDat
->PortList
[i
].SelFrequency
;
1500 if (hiFreq
== loFreq
)
1501 break; /* The frequencies are 1:1, no need to do anything */
1507 if ((loFreq
!= 7) && /* {13, 7} 2400MHz / 1200MHz 2:1 */
1508 (loFreq
!= 4) && /* {13, 4} 2400MHz / 600MHz 4:1 */
1509 (loFreq
!= 2) ) /* {13, 2} 2400MHz / 400MHz 6:1 */
1512 else if (hiFreq
== 11)
1514 if ((loFreq
!= 6)) /* {11, 6} 2000MHz / 1000MHz 2:1 */
1517 else if (hiFreq
== 9)
1519 if ((loFreq
!= 5) && /* { 9, 5} 1600MHz / 800MHz 2:1 */
1520 (loFreq
!= 2) && /* { 9, 2} 1600MHz / 400MHz 4:1 */
1521 (loFreq
!= 0) ) /* { 9, 0} 1600MHz / 200MHz 8:1 */
1524 else if (hiFreq
== 7)
1526 if ((loFreq
!= 4) && /* { 7, 4} 1200MHz / 600MHz 2:1 */
1527 (loFreq
!= 0) ) /* { 7, 0} 1200MHz / 200MHz 6:1 */
1530 else if (hiFreq
== 5)
1532 if ((loFreq
!= 2) && /* { 5, 2} 800MHz / 400MHz 2:1 */
1533 (loFreq
!= 0) ) /* { 5, 0} 800MHz / 200MHz 4:1 */
1536 else if (hiFreq
== 2)
1538 if ((loFreq
!= 0)) /* { 2, 0} 400MHz / 200MHz 2:1 */
1543 downgrade
= TRUE
; /* no legal ratios for hiFreq */
1546 /* Step 3. Downgrade the higher of the two frequencies, and set nochanges to FALSE */
1549 /* Although the problem was with the port specified by hiIndex, we need to */
1550 /* downgrade both ends of the link. */
1551 hiIndex
= hiIndex
& 0xFE; /* Select the 'upstream' (i.e. even) port */
1553 temp
= pDat
->PortList
[hiIndex
].CompositeFrequencyCap
;
1555 /* Remove hiFreq from the list of valid frequencies */
1556 temp
= temp
& ~((uint32
)1 << hiFreq
);
1558 pDat
->PortList
[hiIndex
].CompositeFrequencyCap
= (uint16
)temp
;
1559 pDat
->PortList
[hiIndex
+1].CompositeFrequencyCap
= (uint16
)temp
;
1563 if (temp
& ((u32
)1 << k
))
1567 pDat
->PortList
[hiIndex
].SelFrequency
= k
;
1568 pDat
->PortList
[hiIndex
+1].SelFrequency
= k
;
1574 } while (changes
); /* Repeat until a valid configuration is reached */
1575 #endif /* HT_BUILD_NC_ONLY */
1578 /*----------------------------------------------------------------------------------------
1580 * linkOptimization(sMainData *pDat)
1583 * Based on link capabilities, apply optimization rules to come up with the real best
1584 * settings, including several external limit decision from call backs. This includes
1585 * handling of sublinks. Finally, after the port list data is updated, set the hardware
1586 * state for all links.
1589 * @param[in] sMainData* pDat = our global state
1590 * ---------------------------------------------------------------------------------------
1592 static void linkOptimization(sMainData
*pDat
)
1594 pDat
->nb
->gatherLinkData(pDat
, pDat
->nb
);
1596 selectOptimalWidthAndFrequency(pDat
);
1597 hammerSublinkFixup(pDat
);
1598 pDat
->nb
->setLinkData(pDat
, pDat
->nb
);
1602 /*----------------------------------------------------------------------------------------
1604 * trafficDistribution(sMainData *pDat)
1607 * In the case of a two node system with both sublinks used, enable the traffic
1608 * distribution feature.
1611 * @param[in] sMainData* pDat = our global state, port list data
1612 * ---------------------------------------------------------------------------------------
1614 static void trafficDistribution(sMainData
*pDat
)
1616 #ifndef HT_BUILD_NC_ONLY
1617 u32 links01
, links10
;
1621 /* Traffic Distribution is only used when there are exactly two nodes in the system */
1622 if (pDat
->NodesDiscovered
+1 != 2)
1628 for (i
= 0; i
< pDat
->TotalLinks
*2; i
+= 2)
1630 if ((pDat
->PortList
[i
].Type
== PORTLIST_TYPE_CPU
) && (pDat
->PortList
[i
+1].Type
== PORTLIST_TYPE_CPU
))
1632 links01
|= (u32
)1 << pDat
->PortList
[i
].Link
;
1633 links10
|= (u32
)1 << pDat
->PortList
[i
+1].Link
;
1637 ASSERT(linkCount
!= 0);
1639 return; /* Don't setup Traffic Distribution if only one link is being used */
1641 pDat
->nb
->writeTrafficDistribution(links01
, links10
, pDat
->nb
);
1642 #endif /* HT_BUILD_NC_ONLY */
1645 /*----------------------------------------------------------------------------------------
1647 * tuning(sMainData *pDat)
1650 * Handle system and performance tunings, such as traffic distribution, fifo and
1651 * buffer tuning, and special config tunings.
1654 * @param[in] sMainData* pDat = our global state, port list data
1655 * ---------------------------------------------------------------------------------------
1657 static void tuning(sMainData
*pDat
)
1661 /* See if traffic distribution can be done and do it if so
1662 * or allow system specific customization
1664 if ((pDat
->HtBlock
->AMD_CB_CustomizeTrafficDistribution
== NULL
)
1665 || !pDat
->HtBlock
->AMD_CB_CustomizeTrafficDistribution())
1667 trafficDistribution(pDat
);
1670 /* For each node, invoke northbridge specific buffer tunings or
1671 * system specific customizations.
1673 for (i
=0; i
< pDat
->NodesDiscovered
+ 1; i
++)
1675 if ((pDat
->HtBlock
->AMD_CB_CustomizeBuffers
== NULL
)
1676 || !pDat
->HtBlock
->AMD_CB_CustomizeBuffers(i
))
1678 pDat
->nb
->bufferOptimizations(i
, pDat
, pDat
->nb
);
1683 /*----------------------------------------------------------------------------------------
1688 * Perform any general sanity checks which should prevent HT from running if they fail.
1689 * Currently only the "Must run on BSP only" check.
1692 * @param[out] result BOOL = true if check is ok, false if it failed
1693 * ---------------------------------------------------------------------------------------
1695 static BOOL
isSanityCheckOk(void)
1699 AmdMSRRead(APIC_Base
, &qValue
);
1701 return ((qValue
.lo
& ((u32
)1 << APIC_Base_BSP
)) != 0);
1704 /***************************************************************************
1705 *** HT Initialize ***
1706 ***************************************************************************/
1708 /*----------------------------------------------------------------------------------------
1710 * htInitialize(AMD_HTBLOCK *pBlock)
1713 * This is the top level external interface for Hypertransport Initialization.
1714 * Create our initial internal state, initialize the coherent fabric,
1715 * initialize the non-coherent chains, and perform any required fabric tuning or
1719 * @param[in] AMD_HTBLOCK* pBlock = Our Initial State including possible
1720 * topologies and routings, non coherent bus
1721 * assignment info, and actual
1722 * wrapper or OEM call back routines.
1723 * ---------------------------------------------------------------------------------------
1725 void amdHtInitialize(AMD_HTBLOCK
*pBlock
)
1730 if (isSanityCheckOk())
1732 newNorthBridge(0, &nb
);
1734 pDat
.HtBlock
= pBlock
;
1736 pDat
.sysMpCap
= nb
.maxNodes
;
1737 nb
.isCapable(0, &pDat
, pDat
.nb
);
1738 coherentInit(&pDat
);
1740 pDat
.AutoBusCurrent
= pBlock
->AutoBusStart
;
1741 pDat
.UsedCfgMapEntires
= 0;
1743 linkOptimization(&pDat
);