Clean up AMD FAM10 HT variable initialization. The structure init is cleaner, avoid...
[coreboot.git] / src / northbridge / amd / amdht / h3finit.c
1 /*
2  * This file is part of the coreboot project.
3  *
4  * Copyright (C) 2007 Advanced Micro Devices, Inc.
5  *
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.
9  *
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.
14  *
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
18  */
19
20 /*
21  *----------------------------------------------------------------------------
22  *                              MODULES USED
23  *
24  *----------------------------------------------------------------------------
25  */
26
27 #undef FILECODE
28 #define FILECODE 0xF001
29
30 #include "comlib.h"
31 #include "h3finit.h"
32 #include "h3ffeat.h"
33 #include "h3ncmn.h"
34 #include "h3gtopo.h"
35 #include "AsPsNb.h"
36 /* this is pre-ram so include the required C files here */
37 #include "comlib.c"
38 #include "AsPsNb.c"
39 #include "h3ncmn.c"
40
41 /*----------------------------------------------------------------------------
42  *                      DEFINITIONS AND MACROS
43  *
44  *----------------------------------------------------------------------------
45  */
46
47 #undef FILECODE
48 #define FILECODE 0xF001
49
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
53
54 /*----------------------------------------------------------------------------
55  *                      TYPEDEFS AND STRUCTURES
56  *
57  *----------------------------------------------------------------------------
58  */
59
60 /*----------------------------------------------------------------------------
61  *                      PROTOTYPES OF LOCAL FUNCTIONS
62  *
63  *----------------------------------------------------------------------------
64  */
65
66 /*----------------------------------------------------------------------------
67  *                      EXPORTED FUNCTIONS
68  *
69  *----------------------------------------------------------------------------
70  */
71
72 /*----------------------------------------------------------------------------
73  *                      LOCAL FUNCTIONS
74  *
75  *----------------------------------------------------------------------------
76  */
77 #ifndef HT_BUILD_NC_ONLY
78 /*
79  **************************************************************************
80  *                      Routing table decompressor
81  **************************************************************************
82  */
83
84 /*
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.
96  *
97  *
98  * pseudo definition of compressed graph:
99  * typedef struct
100  * {
101  *      BIT  broadcast[8];
102  *      uint4 responseRoute;
103  *      uint4 requestRoute;
104  * } sRoute;
105  * typedef struct
106  * {
107  *      u8 size;
108  *      sRoute graph[size][size];
109  * } sGraph;
110  *
111  **************************************************************************
112  */
113
114 /*----------------------------------------------------------------------------------------
115  * u8
116  * graphHowManyNodes(u8 *graph)
117  *
118  *  Description:
119  *       Returns the number of nodes in the compressed graph
120  *
121  *  Parameters:
122  *      @param[in] u8 graph = a compressed graph
123  *      @param[out] u8 results = the number of nodes in the graph
124  * ---------------------------------------------------------------------------------------
125  */
126 u8 graphHowManyNodes(u8 *graph)
127 {
128         return graph[0];
129 }
130
131 /*----------------------------------------------------------------------------------------
132  * BOOL
133  * graphIsAdjacent(u8 *graph, u8 nodeA, u8 nodeB)
134  *
135  *  Description:
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.
139  *
140  *  Parameters:
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  * ---------------------------------------------------------------------------------------
146  */
147 BOOL graphIsAdjacent(u8 *graph, u8 nodeA, u8 nodeB)
148 {
149         u8 size = graph[0];
150         ASSERT(size <= MAX_NODES);
151         ASSERT((nodeA < size) && (nodeB < size));
152         return (graph[1+(nodeA*size+nodeB)*2+1] & 0x0F) == nodeB;
153 }
154
155 /*----------------------------------------------------------------------------------------
156  * u8
157  * graphGetRsp(u8 *graph, u8 nodeA, u8 nodeB)
158  *
159  *  Description:
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.
165  *
166  *  Parameters:
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  * ---------------------------------------------------------------------------------------
172  */
173 u8 graphGetRsp(u8 *graph, u8 nodeA, u8 nodeB)
174 {
175         u8 size = graph[0];
176         ASSERT(size <= MAX_NODES);
177         ASSERT((nodeA < size) && (nodeB < size));
178         return (graph[1+(nodeA*size+nodeB)*2+1] & 0xF0)>>4;
179 }
180
181 /*----------------------------------------------------------------------------------------
182  * u8
183  * graphGetReq(u8 *graph, u8 nodeA, u8 nodeB)
184  *
185  *  Description:
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.
191  *
192  *  Parameters:
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  * ---------------------------------------------------------------------------------------
198  */
199 u8 graphGetReq(u8 *graph, u8 nodeA, u8 nodeB)
200 {
201         u8 size = graph[0];
202         ASSERT(size <= MAX_NODES);
203         ASSERT((nodeA < size) && (nodeB < size));
204         return (graph[1+(nodeA*size+nodeB)*2+1] & 0x0F);
205 }
206
207 /*----------------------------------------------------------------------------------------
208  * u8
209  * graphGetBc(u8 *graph, u8 nodeA, u8 nodeB)
210  *
211  *  Description:
212  *       Returns a bit vector of nodes that nodeA should forward a broadcast from
213  *       nodeB towards
214  *
215  *  Parameters:
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  * ---------------------------------------------------------------------------------------
221  */
222 u8 graphGetBc(u8 *graph, u8 nodeA, u8 nodeB)
223 {
224         u8 size = graph[0];
225         ASSERT(size <= MAX_NODES);
226         ASSERT((nodeA < size) && (nodeB < size));
227         return graph[1+(nodeA*size+nodeB)*2];
228 }
229
230
231 /***************************************************************************
232  ***            GENERIC HYPERTRANSPORT DISCOVERY CODE                   ***
233  ***************************************************************************/
234
235 /*----------------------------------------------------------------------------------------
236  * void
237  * routeFromBSP(u8 targetNode, u8 actualTarget, sMainData *pDat)
238  *
239  *  Description:
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.
244  *
245  *  Parameters:
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  * ---------------------------------------------------------------------------------------
250  */
251 void routeFromBSP(u8 targetNode, u8 actualTarget, sMainData *pDat)
252 {
253         u8 predecessorNode, predecessorLink, currentPair;
254
255         if (targetNode == 0)
256                 return;  /*  BSP has no predecessor, stop */
257
258         /*  Search for the link that connects targetNode to its predecessor */
259         currentPair = 0;
260         while (pDat->PortList[currentPair*2+1].NodeID != targetNode)
261         {
262                 currentPair++;
263                 ASSERT(currentPair < pDat->TotalLinks);
264         }
265
266         predecessorNode = pDat->PortList[currentPair*2].NodeID;
267         predecessorLink = pDat->PortList[currentPair*2].Link;
268
269         /*  Recursively call self to ensure the route from the BSP to the Predecessor */
270         /*  Node is established */
271         routeFromBSP(predecessorNode, actualTarget, pDat);
272
273         pDat->nb->writeRoutingTable(predecessorNode, actualTarget, predecessorLink, pDat->nb);
274 }
275
276 /*----------------------------------------------------------------------------------------
277  * u8
278  * convertNodeToLink(u8 srcNode, u8 targetNode, sMainData *pDat)
279  *
280  *  Description:
281  *       Return the link on source node which connects to target node
282  *
283  *  Parameters:
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  * ---------------------------------------------------------------------------------------
289  */
290 u8 convertNodeToLink(u8 srcNode, u8 targetNode, sMainData *pDat)
291 {
292         u8 targetlink = INVALID_LINK;
293         u8 k;
294
295         for (k = 0; k < pDat->TotalLinks*2; k += 2)
296         {
297                 if ((pDat->PortList[k+0].NodeID == srcNode) && (pDat->PortList[k+1].NodeID == targetNode))
298                 {
299                         targetlink = pDat->PortList[k+0].Link;
300                         break;
301                 }
302                 else if ((pDat->PortList[k+1].NodeID == srcNode) && (pDat->PortList[k+0].NodeID == targetNode))
303                 {
304                         targetlink = pDat->PortList[k+1].Link;
305                         break;
306                 }
307         }
308         ASSERT(targetlink != INVALID_LINK);
309
310         return targetlink;
311 }
312
313
314 /*----------------------------------------------------------------------------------------
315  * void
316  * htDiscoveryFloodFill(sMainData *pDat)
317  *
318  *  Description:
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).
324  *
325  *  Parameters:
326  *      @param[in]    sMainData*  pDat = our global state
327  * ---------------------------------------------------------------------------------------
328  */
329 void htDiscoveryFloodFill(sMainData *pDat)
330 {
331         u8 currentNode = 0;
332         u8 currentLink;
333
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
336          */
337
338         while (currentNode <= pDat->NodesDiscovered)
339         {
340                 u32 temp;
341
342                 if (currentNode != 0)
343                 {
344                         /* Set path from BSP to currentNode */
345                         routeFromBSP(currentNode, currentNode, pDat);
346
347                         /* Set path from BSP to currentNode for currentNode+1 if
348                          * currentNode+1 != MAX_NODES
349                          */
350                         if (currentNode+1 != MAX_NODES)
351                                 routeFromBSP(currentNode, currentNode+1, pDat);
352
353                         /* Configure currentNode to route traffic to the BSP through its
354                          * default link
355                          */
356                         pDat->nb->writeRoutingTable(currentNode, 0, pDat->nb->readDefLnk(currentNode, pDat->nb), pDat->nb);
357                 }
358
359                 /* Set currentNode's NodeID field to currentNode */
360                 pDat->nb->writeNodeID(currentNode, currentNode, pDat->nb);
361
362                 /* Enable routing tables on currentNode*/
363                 pDat->nb->enableRoutingTables(currentNode, pDat->nb);
364
365                 for (currentLink = 0; currentLink < pDat->nb->maxLinks; currentLink++)
366                 {
367                         BOOL linkfound;
368                         u8 token;
369
370                         if (pDat->HtBlock->AMD_CB_IgnoreLink && pDat->HtBlock->AMD_CB_IgnoreLink(currentNode, currentLink))
371                                 continue;
372
373                         if (pDat->nb->readTrueLinkFailStatus(currentNode, currentLink, pDat, pDat->nb))
374                                 continue;
375
376                         /* Make sure that the link is connected, coherent, and ready */
377                         if (!pDat->nb->verifyLinkIsCoherent(currentNode, currentLink, pDat->nb))
378                                 continue;
379
380
381                         /* Test to see if the currentLink has already been explored */
382                         linkfound = FALSE;
383                         for (temp = 0; temp < pDat->TotalLinks; temp++)
384                         {
385                                 if ((pDat->PortList[temp*2+1].NodeID == currentNode) &&
386                                    (pDat->PortList[temp*2+1].Link == currentLink))
387                                 {
388                                         linkfound = TRUE;
389                                         break;
390                                 }
391                         }
392                         if (linkfound)
393                         {
394                                 /* We had already expored this link */
395                                 continue;
396                         }
397
398                         if (pDat->nb->handleSpecialLinkCase(currentNode, currentLink, pDat, pDat->nb))
399                         {
400                                 continue;
401                         }
402
403                         /* Modify currentNode's routing table to use currentLink to send
404                          * traffic to currentNode+1
405                          */
406                         pDat->nb->writeRoutingTable(currentNode, currentNode+1, currentLink, pDat->nb);
407
408                         /* Check the northbridge of the node we just found, to make sure it is compatible
409                          * before doing anything else to it.
410                          */
411                         if (!pDat->nb->isCompatible(currentNode+1, pDat->nb))
412                         {
413                                 u8 nodeToKill;
414
415                                 /* Notify BIOS of event (while variables are still the same) */
416                                 if (pDat->HtBlock->AMD_CB_EventNotify)
417                                 {
418                                         sHtEventCohFamilyFeud evt;
419                                         evt.eSize = sizeof(sHtEventCohFamilyFeud);
420                                         evt.node = currentNode;
421                                         evt.link = currentLink;
422                                         evt.totalNodes = pDat->NodesDiscovered;
423
424                                         pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,
425                                                                         HT_EVENT_COH_FAMILY_FEUD,
426                                                                         (u8 *)&evt);
427                                 }
428
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.
434                                  */
435                                 pDat->NodesDiscovered = 0;
436                                 currentNode = 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.
441                                  */
442                                 for (currentLink = 0; currentLink < pDat->nb->maxLinks; currentLink++)
443                                 {
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);
447                                 }
448
449                                 for (nodeToKill = 0; nodeToKill < pDat->nb->maxNodes; nodeToKill++)
450                                 {
451                                         pDat->nb->writeFullRoutingTable(0, nodeToKill, ROUTETOSELF, ROUTETOSELF, 0, pDat->nb);
452                                 }
453
454                                 /* End Coherent Discovery */
455                                 STOP_HERE;
456                                 break;
457                         }
458
459                         /* Read token from Current+1 */
460                         token = pDat->nb->readToken(currentNode+1, pDat->nb);
461                         ASSERT(token <= pDat->NodesDiscovered);
462                         if (token == 0)
463                         {
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))
468                                 {
469                                         u8 nodeToKill;
470
471                                         /* Notify BIOS of event  */
472                                         if (pDat->HtBlock->AMD_CB_EventNotify)
473                                         {
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;
480
481                                                 pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,
482                                                                         HT_EVENT_COH_MPCAP_MISMATCH,
483                                                                         (u8 *)&evt);
484                                         }
485
486                                         pDat->NodesDiscovered = 0;
487                                         currentNode = 0;
488                                         pDat->TotalLinks = 0;
489
490                                         for (nodeToKill = 0; nodeToKill < pDat->nb->maxNodes; nodeToKill++)
491                                         {
492                                                 pDat->nb->writeFullRoutingTable(0, nodeToKill, ROUTETOSELF, ROUTETOSELF, 0, pDat->nb);
493                                         }
494
495                                         /* End Coherent Discovery */
496                                         STOP_HERE;
497                                         break;
498                                 }
499
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.
504                                  */
505                                 if (pDat->HtBlock->AMD_CB_EventNotify)
506                                 {
507                                         sHtEventCohNodeDiscovered evt;
508                                         evt.eSize = sizeof(sHtEventCohNodeDiscovered);
509                                         evt.node = currentNode;
510                                         evt.link = currentLink;
511                                         evt.newNode = token;
512
513                                         pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_INFO,
514                                                                 HT_EVENT_COH_NODE_DISCOVERED,
515                                                                 (u8 *)&evt);
516                                 }
517                         }
518
519                         if (pDat->TotalLinks == MAX_PLATFORM_LINKS)
520                         {
521                                 /*
522                                  * Exceeded our capacity to describe all coherent links found in the system.
523                                  * Error strategy:
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.
530                                  */
531                                 if (pDat->HtBlock->AMD_CB_EventNotify)
532                                 {
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;
540
541                                         pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,
542                                                                         HT_EVENT_COH_LINK_EXCEED,
543                                                                         (u8 *)&evt);
544                                 }
545                                 /* Force link and node loops to halt */
546                                 STOP_HERE;
547                                 currentNode = pDat->NodesDiscovered;
548                                 break;
549                         }
550
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;
554
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;
558
559                         pDat->TotalLinks++;
560
561                         if ( !pDat->sysMatrix[currentNode][token] )
562                         {
563                                 pDat->sysDegree[currentNode]++;
564                                 pDat->sysDegree[token]++;
565                                 pDat->sysMatrix[currentNode][token] = TRUE;
566                                 pDat->sysMatrix[token][currentNode] = TRUE;
567                         }
568                 }
569                 currentNode++;
570         }
571 }
572
573
574 /***************************************************************************
575  ***            ISOMORPHISM BASED ROUTING TABLE GENERATION CODE         ***
576  ***************************************************************************/
577
578 /*----------------------------------------------------------------------------------------
579  * BOOL
580  * isoMorph(u8 i, sMainData *pDat)
581  *
582  *  Description:
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).
589  *
590  *  Parameters:
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  * ---------------------------------------------------------------------------------------
597  */
598 BOOL isoMorph(u8 i, sMainData *pDat)
599 {
600         u8 j, k;
601         u8 nodecnt;
602
603         /* We have only been called if nodecnt == pSelected->size ! */
604         nodecnt = pDat->NodesDiscovered+1;
605
606         if (i != nodecnt)
607         {
608                 /*  Keep building the permutation */
609                 for (j = 0; j < nodecnt; j++)
610                 {
611                         /*  Make sure the degree matches */
612                         if (pDat->sysDegree[i] != pDat->dbDegree[j])
613                                 continue;
614
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++)
618                         {
619                                 if (pDat->Perm[k] == j)
620                                         break;
621                         }
622                         if (k != i)
623                                 continue;
624                         pDat->Perm[i] = j;
625                         if (isoMorph(i+1, pDat))
626                                 return TRUE;
627                 }
628                 return FALSE;
629         } else {
630                 /*  Test to see if the permutation is isomorphic */
631                 for (j = 0; j < nodecnt; j++)
632                 {
633                         for (k = 0; k < nodecnt; k++)
634                         {
635                                 if ( pDat->sysMatrix[j][k] !=
636                                    pDat->dbMatrix[pDat->Perm[j]][pDat->Perm[k]] )
637                                         return FALSE;
638                         }
639                 }
640                 return TRUE;
641         }
642 }
643
644
645 /*----------------------------------------------------------------------------------------
646  * void
647  * lookupComputeAndLoadRoutingTables(sMainData *pDat)
648  *
649  *  Description:
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
656  *       to the other.
657  *
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.
661  *
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.
666  *
667  *  Parameters:
668  *      @param[in]    sMainData* pDat = our global state, the discovered fabric,
669  *      @param[out]                     degree matrix, permutation
670  * ---------------------------------------------------------------------------------------
671  */
672 void lookupComputeAndLoadRoutingTables(sMainData *pDat)
673 {
674         u8 **pTopologyList;
675         u8 *pSelected;
676
677         int i, j, k, size;
678
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)
683         {
684                 getAmdTopolist(&pTopologyList);
685         }
686
687         pSelected = *pTopologyList;
688         while (pSelected != NULL)
689         {
690                 if (graphHowManyNodes(pSelected) == size)
691                 {
692                         /*  Build Degree vector and Adjency Matrix for this entry */
693                         for (i = 0; i < size; i++)
694                         {
695                                 pDat->dbDegree[i] = 0;
696                                 for (j = 0; j < size; j++)
697                                 {
698                                         if (graphIsAdjacent(pSelected, i, j))
699                                         {
700                                                 pDat->dbMatrix[i][j] = 1;
701                                                 pDat->dbDegree[i]++;
702                                         }
703                                         else
704                                         {
705                                                 pDat->dbMatrix[i][j] = 0;
706                                         }
707                                 }
708                         }
709                         if (isoMorph(0, pDat))
710                                 break;  /*  A matching topology was found */
711                 }
712
713                 pTopologyList++;
714                 pSelected = *pTopologyList;
715         }
716
717         if (pSelected != NULL)
718         {
719                 /*  Compute the reverse Permutation */
720                 for (i = 0; i < size; i++)
721                 {
722                         pDat->ReversePerm[pDat->Perm[i]] = i;
723                 }
724
725                 /*  Start with the last discovered node, and move towards the BSP */
726                 for (i = size-1; i >= 0; i--)
727                 {
728                         for (j = 0; j < size; j++)
729                         {
730                                 u8 ReqTargetLink, RspTargetLink;
731                                 u8 ReqTargetNode, RspTargetNode;
732
733                                 u8 AbstractBcTargetNodes = graphGetBc(pSelected, pDat->Perm[i], pDat->Perm[j]);
734                                 u32 BcTargetLinks = 0;
735
736                                 for (k = 0; k < MAX_NODES; k++)
737                                 {
738                                         if (AbstractBcTargetNodes & ((u32)1<<k))
739                                         {
740                                                 BcTargetLinks |= (u32)1 << convertNodeToLink(i, pDat->ReversePerm[k], pDat);
741                                         }
742                                 }
743
744                                 if (i == j)
745                                 {
746                                         ReqTargetLink = ROUTETOSELF;
747                                         RspTargetLink = ROUTETOSELF;
748                                 }
749                                 else
750                                 {
751                                         ReqTargetNode = graphGetReq(pSelected, pDat->Perm[i], pDat->Perm[j]);
752                                         ReqTargetLink = convertNodeToLink(i, pDat->ReversePerm[ReqTargetNode], pDat);
753
754                                         RspTargetNode = graphGetRsp(pSelected, pDat->Perm[i], pDat->Perm[j]);
755                                         RspTargetLink = convertNodeToLink(i, pDat->ReversePerm[RspTargetNode], pDat);
756                                 }
757
758                                 pDat->nb->writeFullRoutingTable(i, j, ReqTargetLink, RspTargetLink, BcTargetLinks, pDat->nb);
759                         }
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.
764                          */
765                         if (size < pDat->nb->maxNodes)
766                         {
767                                 pDat->nb->writeFullRoutingTable(i, size, ROUTETOSELF, ROUTETOSELF, 0, pDat->nb);
768                         }
769                 }
770
771         }
772         else
773         {
774                 /*
775                  * No Matching Topology was found
776                  * Error Strategy:
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.
780                  */
781                 if (pDat->HtBlock->AMD_CB_EventNotify)
782                 {
783                         sHtEventCohNoTopology evt;
784                         evt.eSize = sizeof(sHtEventCohNoTopology);
785                         evt.totalNodes = pDat->NodesDiscovered;
786
787                         pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,
788                                                 HT_EVENT_COH_NO_TOPOLOGY,
789                                                 (u8 *)&evt);
790                 }
791                 STOP_HERE;
792                 /* Force 1P */
793                 pDat->NodesDiscovered = 0;
794                 pDat->TotalLinks = 0;
795                 pDat->nb->enableRoutingTables(0, pDat->nb);
796         }
797 }
798 #endif /* HT_BUILD_NC_ONLY */
799
800
801 /*----------------------------------------------------------------------------------------
802  * void
803  * finializeCoherentInit(sMainData *pDat)
804  *
805  *  Description:
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.
808  *
809  *  Parameters:
810  *      @param[in] sMainData* pDat = our global state, number of nodes discovered.
811  * ---------------------------------------------------------------------------------------
812  */
813 void finializeCoherentInit(sMainData *pDat)
814 {
815         u8 curNode;
816
817         u8 totalCores = 0;
818         for (curNode = 0; curNode < pDat->NodesDiscovered+1; curNode++)
819         {
820                 totalCores += pDat->nb->getNumCoresOnNode(curNode, pDat->nb);
821         }
822
823         for (curNode = 0; curNode < pDat->NodesDiscovered+1; curNode++)
824         {
825                 pDat->nb->setTotalNodesAndCores(curNode, pDat->NodesDiscovered+1, totalCores, pDat->nb);
826         }
827
828         for (curNode = 0; curNode < pDat->NodesDiscovered+1; curNode++)
829         {
830                 pDat->nb->limitNodes(curNode, pDat->nb);
831         }
832
833 }
834
835 /*----------------------------------------------------------------------------------------
836  * void
837  * coherentInit(sMainData *pDat)
838  *
839  *  Description:
840  *       Perform discovery and initialization of the coherent fabric.
841  *
842  *  Parameters:
843  *      @param[in] sMainData* pDat = our global state
844  * ---------------------------------------------------------------------------------------
845  */
846 void coherentInit(sMainData *pDat)
847 {
848         u8 i, j;
849
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
854          */
855         pDat->NodesDiscovered = 0;
856         pDat->TotalLinks = 0;
857         pDat->nb->enableRoutingTables(0, pDat->nb);
858 #else
859         pDat->NodesDiscovered = 0;
860         pDat->TotalLinks = 0;
861         for (i = 0; i < MAX_NODES; i++)
862         {
863                 pDat->sysDegree[i] = 0;
864                 for (j = 0; j < MAX_NODES; j++)
865                 {
866                         pDat->sysMatrix[i][j] = 0;
867                 }
868         }
869
870         htDiscoveryFloodFill(pDat);
871         lookupComputeAndLoadRoutingTables(pDat);
872 #endif
873         finializeCoherentInit(pDat);
874 }
875
876 /***************************************************************************
877  ***                        Non-coherent init code                        ***
878  ***                              Algorithms                              ***
879  ***************************************************************************/
880 /*----------------------------------------------------------------------------------------
881  * void
882  * processLink(u8 node, u8 link, sMainData *pDat)
883  *
884  *  Description:
885  *       Process a non-coherent link, enabling a range of bus numbers, and setting the device
886  *       ID for all devices found
887  *
888  *  Parameters:
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  * ---------------------------------------------------------------------------------------
893  */
894 void processLink(u8 node, u8 link, sMainData *pDat)
895 {
896         u8 secBus, subBus;
897         u32 currentBUID;
898         u32 temp;
899         u32 unitIDcnt;
900         SBDFO currentPtr;
901         u8 depth;
902         u8 *pSwapPtr;
903
904         SBDFO lastSBDFO = ILLEGAL_SBDFO;
905         u8 lastLink = 0;
906
907         ASSERT(node < pDat->nb->maxNodes && link < pDat->nb->maxLinks);
908
909         if ((pDat->HtBlock->AMD_CB_OverrideBusNumbers == NULL)
910            || !pDat->HtBlock->AMD_CB_OverrideBusNumbers(node, link, &secBus, &subBus))
911         {
912                 /* Assign Bus numbers */
913                 if (pDat->AutoBusCurrent >= pDat->HtBlock->AutoBusMax)
914                 {
915                         /* If we run out of Bus Numbers notify, if call back unimplemented or if it
916                          * returns, skip this chain
917                          */
918                         if (pDat->HtBlock->AMD_CB_EventNotify)
919                         {
920                                 sHTEventNcohBusMaxExceed evt;
921                                 evt.eSize = sizeof(sHTEventNcohBusMaxExceed);
922                                 evt.node = node;
923                                 evt.link = link;
924                                 evt.bus = pDat->AutoBusCurrent;
925
926                                 pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,HT_EVENT_NCOH_BUS_MAX_EXCEED,(u8 *)&evt);
927                         }
928                         STOP_HERE;
929                         return;
930                 }
931
932                 if (pDat->UsedCfgMapEntires >= 4)
933                 {
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.
936                          */
937                         if (pDat->HtBlock->AMD_CB_EventNotify)
938                         {
939                                 sHtEventNcohCfgMapExceed evt;
940                                 evt.eSize = sizeof(sHtEventNcohCfgMapExceed);
941                                 evt.node = node;
942                                 evt.link = link;
943
944                                 pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,
945                                                         HT_EVENT_NCOH_CFG_MAP_EXCEED,
946                                                         (u8 *)&evt);
947                         }
948                         STOP_HERE;
949                         return;
950                 }
951
952                 secBus = pDat->AutoBusCurrent;
953                 subBus = secBus + pDat->HtBlock->AutoBusIncrement-1;
954                 pDat->AutoBusCurrent += pDat->HtBlock->AutoBusIncrement;
955         }
956
957         pDat->nb->setCFGAddrMap(pDat->UsedCfgMapEntires, secBus, subBus, node, link, pDat, pDat->nb);
958         pDat->UsedCfgMapEntires++;
959
960         if ((pDat->HtBlock->AMD_CB_ManualBUIDSwapList != NULL)
961          && pDat->HtBlock->AMD_CB_ManualBUIDSwapList(node, link, &pSwapPtr))
962         {
963                 /* Manual non-coherent BUID assignment */
964
965                 /* Assign BUID's per manual override */
966                 while (*pSwapPtr != 0xFF)
967                 {
968                         currentPtr = MAKE_SBDFO(0, secBus, *pSwapPtr, 0, 0);
969                         pSwapPtr++;
970
971                         do
972                         {
973                                 AmdPCIFindNextCap(&currentPtr);
974                                 ASSERT(currentPtr != ILLEGAL_SBDFO);
975                                 AmdPCIRead(currentPtr, &temp);
976                         } while (!IS_HT_SLAVE_CAPABILITY(temp));
977
978                         currentBUID = *pSwapPtr;
979                         pSwapPtr++;
980                         AmdPCIWriteBits(currentPtr, 20, 16, &currentBUID);
981                 }
982
983                 /* Build chain of devices */
984                 depth = 0;
985                 pSwapPtr++;
986                 while (*pSwapPtr != 0xFF)
987                 {
988                         pDat->PortList[pDat->TotalLinks*2].NodeID = node;
989                         if (depth == 0)
990                         {
991                                 pDat->PortList[pDat->TotalLinks*2].Type = PORTLIST_TYPE_CPU;
992                                 pDat->PortList[pDat->TotalLinks*2].Link = link;
993                         }
994                         else
995                         {
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;
1001                         }
1002
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;
1007
1008                         currentPtr = MAKE_SBDFO(0, secBus, (*pSwapPtr & 0x3F), 0, 0);
1009                         do
1010                         {
1011                                 AmdPCIFindNextCap(&currentPtr);
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;
1017
1018                         /* Bit 6 indicates whether orientation override is desired.
1019                          * Bit 7 indicates the upstream link if overriding.
1020                          */
1021                         /* assert catches at least the one known incorrect setting */
1022                         ASSERT ((*pSwapPtr & 0x40) || (!(*pSwapPtr & 0x80)));
1023                         if (*pSwapPtr & 0x40)
1024                         {
1025                                 /* Override the device's orientation */
1026                                 lastLink = *pSwapPtr >> 7;
1027                         }
1028                         else
1029                         {
1030                                 /* Detect the device's orientation */
1031                                 AmdPCIReadBits(currentPtr, 26, 26, &temp);
1032                                 lastLink = (u8)temp;
1033                         }
1034                         pDat->PortList[pDat->TotalLinks*2+1].Link = lastLink;
1035
1036                         depth++;
1037                         pDat->TotalLinks++;
1038                         pSwapPtr++;
1039                 }
1040         }
1041         else
1042         {
1043                 /* Automatic non-coherent device detection */
1044                 depth = 0;
1045                 currentBUID = 1;
1046                 while (1)
1047                 {
1048                         currentPtr = MAKE_SBDFO(0, secBus, 0, 0, 0);
1049
1050                         AmdPCIRead(currentPtr, &temp);
1051                         if (temp == 0xFFFFFFFF)
1052                                 /* No device found at currentPtr */
1053                                 break;
1054
1055                         if (pDat->TotalLinks == MAX_PLATFORM_LINKS)
1056                         {
1057                                 /*
1058                                  * Exceeded our capacity to describe all non-coherent links found in the system.
1059                                  * Error strategy:
1060                                  * Auto recovery is not possible because data space is already all used.
1061                                  */
1062                                 if (pDat->HtBlock->AMD_CB_EventNotify)
1063                                 {
1064                                         sHtEventNcohLinkExceed evt;
1065                                         evt.eSize = sizeof(sHtEventNcohLinkExceed);
1066                                         evt.node = node;
1067                                         evt.link = link;
1068                                         evt.depth = depth;
1069                                         evt.maxLinks = pDat->nb->maxLinks;
1070
1071                                         pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,
1072                                                                 HT_EVENT_NCOH_LINK_EXCEED,
1073                                                                 (u8 *)&evt);
1074                                 }
1075                                 /* Force link loop to halt */
1076                                 STOP_HERE;
1077                                 break;
1078                         }
1079
1080                         pDat->PortList[pDat->TotalLinks*2].NodeID = node;
1081                         if (depth == 0)
1082                         {
1083                                 pDat->PortList[pDat->TotalLinks*2].Type = PORTLIST_TYPE_CPU;
1084                                 pDat->PortList[pDat->TotalLinks*2].Link = link;
1085                         }
1086                         else
1087                         {
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;
1093                         }
1094
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;
1099
1100                         do
1101                         {
1102                                 AmdPCIFindNextCap(&currentPtr);
1103                                 ASSERT(currentPtr != ILLEGAL_SBDFO);
1104                                 AmdPCIRead(currentPtr, &temp);
1105                         } while (!IS_HT_SLAVE_CAPABILITY(temp));
1106
1107                         AmdPCIReadBits(currentPtr, 25, 21, &unitIDcnt);
1108                         if ((unitIDcnt + currentBUID > 31) || ((secBus == 0) && (unitIDcnt + currentBUID > 24)))
1109                         {
1110                                 /* An error handler for the case where we run out of BUID's on a chain */
1111                                 if (pDat->HtBlock->AMD_CB_EventNotify)
1112                                 {
1113                                         sHtEventNcohBuidExceed evt;
1114                                         evt.eSize = sizeof(sHtEventNcohBuidExceed);
1115                                         evt.node = node;
1116                                         evt.link = link;
1117                                         evt.depth = depth;
1118                                         evt.currentBUID = (uint8)currentBUID;
1119                                         evt.unitCount = (uint8)unitIDcnt;
1120
1121                                         pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,HT_EVENT_NCOH_BUID_EXCEED,(u8 *)&evt);
1122                                 }
1123                                 STOP_HERE;
1124                                 break;
1125                         }
1126                         AmdPCIWriteBits(currentPtr, 20, 16, &currentBUID);
1127
1128
1129                         currentPtr += MAKE_SBDFO(0, 0, currentBUID, 0, 0);
1130                         AmdPCIReadBits(currentPtr, 20, 16, &temp);
1131                         if (temp != currentBUID)
1132                         {
1133                                 /* An error handler for this critical error */
1134                                 if (pDat->HtBlock->AMD_CB_EventNotify)
1135                                 {
1136                                         sHtEventNcohDeviceFailed evt;
1137                                         evt.eSize = sizeof(sHtEventNcohDeviceFailed);
1138                                         evt.node = node;
1139                                         evt.link = link;
1140                                         evt.depth = depth;
1141                                         evt.attemptedBUID = (uint8)currentBUID;
1142
1143                                         pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,HT_EVENT_NCOH_DEVICE_FAILED,(u8 *)&evt);
1144                                 }
1145                                 STOP_HERE;
1146                                 break;
1147                         }
1148
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;
1152
1153                         lastLink = (u8)temp;
1154                         lastSBDFO = currentPtr;
1155
1156                         depth++;
1157                         pDat->TotalLinks++;
1158                         currentBUID += unitIDcnt;
1159                 }
1160                 if (pDat->HtBlock->AMD_CB_EventNotify)
1161                 {
1162                         /* Provide information on automatic device results */
1163                         sHtEventNcohAutoDepth evt;
1164                         evt.eSize = sizeof(sHtEventNcohAutoDepth);
1165                         evt.node = node;
1166                         evt.link = link;
1167                         evt.depth = (depth - 1);
1168
1169                         pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_INFO,HT_EVENT_NCOH_AUTO_DEPTH,(u8 *)&evt);
1170                 }
1171         }
1172 }
1173
1174
1175 /*----------------------------------------------------------------------------------------
1176  * void
1177  * ncInit(sMainData *pDat)
1178  *
1179  *  Description:
1180  *       Initialize the non-coherent fabric. Begin with the compat link on the BSP, then
1181  *       find and initialize all other non-coherent chains.
1182  *
1183  *  Parameters:
1184  *      @param[in]  sMainData*  pDat = our global state
1185  * ---------------------------------------------------------------------------------------
1186  */
1187 void ncInit(sMainData *pDat)
1188 {
1189         u8 node, link;
1190         u8 compatLink;
1191
1192         compatLink = pDat->nb->readSbLink(pDat->nb);
1193         processLink(0, compatLink, pDat);
1194
1195         for (node = 0; node <= pDat->NodesDiscovered; node++)
1196         {
1197                 for (link = 0; link < pDat->nb->maxLinks; link++)
1198                 {
1199                         if (pDat->HtBlock->AMD_CB_IgnoreLink && pDat->HtBlock->AMD_CB_IgnoreLink(node, link))
1200                                 continue;   /*  Skip the link */
1201
1202                         if (node == 0 && link == compatLink)
1203                                 continue;
1204
1205                         if (pDat->nb->readTrueLinkFailStatus(node, link, pDat, pDat->nb))
1206                                 continue;
1207
1208                         if (pDat->nb->verifyLinkIsNonCoherent(node, link, pDat->nb))
1209                                 processLink(node, link, pDat);
1210                 }
1211         }
1212 }
1213
1214 /***************************************************************************
1215  ***                            Link Optimization                         ***
1216  ***************************************************************************/
1217
1218 /*----------------------------------------------------------------------------------------
1219  * void
1220  * regangLinks(sMainData *pDat)
1221  *
1222  *  Description:
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.
1226  *
1227  *  Parameters:
1228  *      @param[in,out] sMainData*  pDat = our global state
1229  * ---------------------------------------------------------------------------------------
1230  */
1231 void regangLinks(sMainData *pDat)
1232 {
1233 #ifndef HT_BUILD_NC_ONLY
1234         u8 i, j;
1235         for (i = 0; i < pDat->TotalLinks*2; i += 2)
1236         {
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 */
1240
1241                 /* Regang is false unless we pass all conditions below */
1242                 pDat->PortList[i].SelRegang = FALSE;
1243                 pDat->PortList[i+1].SelRegang = FALSE;
1244
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 */
1247
1248                 for (j = i+2; j < pDat->TotalLinks*2; j += 2)
1249                 {
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 */
1252
1253                         if (pDat->PortList[i].NodeID != pDat->PortList[j].NodeID)
1254                                 continue;   /*  Links must be from the same source */
1255
1256                         if (pDat->PortList[i+1].NodeID != pDat->PortList[j+1].NodeID)
1257                                 continue;   /*  Link must be to the same target */
1258
1259                         if ((pDat->PortList[i].Link & 3) != (pDat->PortList[j].Link & 3))
1260                                 continue;   /*  Ensure same source base port */
1261
1262                         if ((pDat->PortList[i+1].Link & 3) != (pDat->PortList[j+1].Link & 3))
1263                                 continue;   /*  Ensure same destination base port */
1264
1265                         if ((pDat->PortList[i].Link & 4) != (pDat->PortList[i+1].Link & 4))
1266                                 continue;   /*  Ensure sublink0 routes to sublink0 */
1267
1268                         ASSERT((pDat->PortList[j].Link & 4) == (pDat->PortList[j+1].Link & 4)); /*  (therefore sublink1 routes to sublink1) */
1269
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))
1275                         {
1276                                 continue;   /*  Skip regang */
1277                         }
1278
1279
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;
1288
1289                         /*  Delete PortList[j, j+1], slow but easy to debug implementation */
1290                         pDat->TotalLinks--;
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);
1293
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; */
1297
1298                         break; /*  Exit loop, advance to PortList[i+2] */
1299                 }
1300         }
1301 #endif /* HT_BUILD_NC_ONLY */
1302 }
1303
1304 /*----------------------------------------------------------------------------------------
1305  * void
1306  * selectOptimalWidthAndFrequency(sMainData *pDat)
1307  *
1308  *  Description:
1309  *       For all links:
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.
1315  *
1316  *  Parameters:
1317  *      @param[in,out]  sMainData*  pDat = our global state, port list data
1318  * ---------------------------------------------------------------------------------------
1319  */
1320 void selectOptimalWidthAndFrequency(sMainData *pDat)
1321 {
1322         u8 i, j;
1323         u32 temp;
1324         u16 cbPCBFreqLimit;
1325         u8 cbPCBABDownstreamWidth;
1326         u8 cbPCBBAUpstreamWidth;
1327
1328         for (i = 0; i < pDat->TotalLinks*2; i += 2)
1329         {
1330                 cbPCBFreqLimit = 0xFFFF;
1331                 cbPCBABDownstreamWidth = 16;
1332                 cbPCBBAUpstreamWidth = 16;
1333
1334                 if ( (pDat->PortList[i].Type == PORTLIST_TYPE_CPU) && (pDat->PortList[i+1].Type == PORTLIST_TYPE_CPU))
1335                 {
1336                         if (pDat->HtBlock->AMD_CB_Cpu2CpuPCBLimits)
1337                         {
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
1345                                                 );
1346                         }
1347                 }
1348                 else
1349                 {
1350                         if (pDat->HtBlock->AMD_CB_IOPCBLimits)
1351                         {
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
1358                                                 );
1359                         }
1360                 }
1361
1362
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;
1368
1369                 ASSERT (temp != 0);
1370                 for (j = 15; ; j--)
1371                 {
1372                         if (temp & ((u32)1 << j))
1373                                 break;
1374                 }
1375
1376                 pDat->PortList[i].SelFrequency = j;
1377                 pDat->PortList[i+1].SelFrequency = j;
1378
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;
1386
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;
1394
1395         }
1396 }
1397
1398 /*----------------------------------------------------------------------------------------
1399  * void
1400  * hammerSublinkFixup(sMainData *pDat)
1401  *
1402  *  Description:
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.
1408  *
1409  *  Parameters:
1410  *      @param[in,out] sMainData* pDat = our global state, link state and port list
1411  * ---------------------------------------------------------------------------------------
1412  */
1413 void hammerSublinkFixup(sMainData *pDat)
1414 {
1415 #ifndef HT_BUILD_NC_ONLY
1416         u8 i, j, k;
1417         BOOL changes, downgrade;
1418
1419         u8 hiIndex;
1420         u8 hiFreq, loFreq;
1421
1422         u32 temp;
1423
1424         do
1425         {
1426                 changes = FALSE;
1427                 for (i = 0; i < pDat->TotalLinks*2; i++)
1428                 {
1429                         if (pDat->PortList[i].Type != PORTLIST_TYPE_CPU) /*  Must be a CPU link */
1430                                 continue;
1431                         if (pDat->PortList[i].Link < 4) /*  Only look for for sublink1's */
1432                                 continue;
1433
1434                         for (j = 0; j < pDat->TotalLinks*2; j++)
1435                         {
1436                                 /*  Step 1. Find the matching sublink0 */
1437                                 if (pDat->PortList[j].Type != PORTLIST_TYPE_CPU)
1438                                         continue;
1439                                 if (pDat->PortList[j].NodeID != pDat->PortList[i].NodeID)
1440                                         continue;
1441                                 if (pDat->PortList[j].Link != (pDat->PortList[i].Link & 0x03))
1442                                         continue;
1443
1444                                 /*  Step 2. Check for an illegal frequency ratio */
1445                                 if (pDat->PortList[i].SelFrequency >= pDat->PortList[j].SelFrequency)
1446                                 {
1447                                         hiIndex = i;
1448                                         hiFreq = pDat->PortList[i].SelFrequency;
1449                                         loFreq = pDat->PortList[j].SelFrequency;
1450                                 }
1451                                 else
1452                                 {
1453                                         hiIndex = j;
1454                                         hiFreq = pDat->PortList[j].SelFrequency;
1455                                         loFreq = pDat->PortList[i].SelFrequency;
1456                                 }
1457
1458                                 if (hiFreq == loFreq)
1459                                         break; /*  The frequencies are 1:1, no need to do anything */
1460
1461                                 downgrade = FALSE;
1462
1463                                 if (hiFreq == 13)
1464                                 {
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 */
1468                                                 downgrade = TRUE;
1469                                 }
1470                                 else if (hiFreq == 11)
1471                                 {
1472                                         if ((loFreq != 6))    /* {11, 6} 2000MHz / 1000MHz 2:1 */
1473                                                 downgrade = TRUE;
1474                                 }
1475                                 else if (hiFreq == 9)
1476                                 {
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 */
1480                                                 downgrade = TRUE;
1481                                 }
1482                                 else if (hiFreq == 7)
1483                                 {
1484                                         if ((loFreq != 4) &&  /* { 7, 4} 1200MHz /  600MHz 2:1 */
1485                                                 (loFreq != 0) )   /* { 7, 0} 1200MHz /  200MHz 6:1 */
1486                                                 downgrade = TRUE;
1487                                 }
1488                                 else if (hiFreq == 5)
1489                                 {
1490                                         if ((loFreq != 2) &&  /* { 5, 2}  800MHz /  400MHz 2:1 */
1491                                                 (loFreq != 0) )   /* { 5, 0}  800MHz /  200MHz 4:1 */
1492                                                 downgrade = TRUE;
1493                                 }
1494                                 else if (hiFreq == 2)
1495                                 {
1496                                         if ((loFreq != 0))    /* { 2, 0}  400MHz /  200MHz 2:1 */
1497                                                 downgrade = TRUE;
1498                                 }
1499                                 else
1500                                 {
1501                                         downgrade = TRUE; /*  no legal ratios for hiFreq */
1502                                 }
1503
1504                                 /*  Step 3. Downgrade the higher of the two frequencies, and set nochanges to FALSE */
1505                                 if (downgrade)
1506                                 {
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 */
1510
1511                                         temp = pDat->PortList[hiIndex].CompositeFrequencyCap;
1512
1513                                         /*  Remove hiFreq from the list of valid frequencies */
1514                                         temp = temp & ~((uint32)1 << hiFreq);
1515                                         ASSERT (temp != 0);
1516                                         pDat->PortList[hiIndex].CompositeFrequencyCap = (uint16)temp;
1517                                         pDat->PortList[hiIndex+1].CompositeFrequencyCap = (uint16)temp;
1518
1519                                         for (k = 15; ; k--)
1520                                         {
1521                                                 if (temp & ((u32)1 << k))
1522                                                         break;
1523                                         }
1524
1525                                         pDat->PortList[hiIndex].SelFrequency = k;
1526                                         pDat->PortList[hiIndex+1].SelFrequency = k;
1527
1528                                         changes = TRUE;
1529                                 }
1530                         }
1531                 }
1532         } while (changes); /*  Repeat until a valid configuration is reached */
1533 #endif /* HT_BUILD_NC_ONLY */
1534 }
1535
1536 /*----------------------------------------------------------------------------------------
1537  * void
1538  * linkOptimization(sMainData *pDat)
1539  *
1540  *  Description:
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.
1545  *
1546  *  Parameters:
1547  *      @param[in]  sMainData* pDat = our global state
1548  * ---------------------------------------------------------------------------------------
1549  */
1550 void linkOptimization(sMainData *pDat)
1551 {
1552         pDat->nb->gatherLinkData(pDat, pDat->nb);
1553         regangLinks(pDat);
1554         selectOptimalWidthAndFrequency(pDat);
1555         hammerSublinkFixup(pDat);
1556         pDat->nb->setLinkData(pDat, pDat->nb);
1557 }
1558
1559
1560 /*----------------------------------------------------------------------------------------
1561  * void
1562  * trafficDistribution(sMainData *pDat)
1563  *
1564  *  Description:
1565  *       In the case of a two node system with both sublinks used, enable the traffic
1566  *       distribution feature.
1567  *
1568  *  Parameters:
1569  *        @param[in]        sMainData*    pDat           = our global state, port list data
1570  * ---------------------------------------------------------------------------------------
1571  */
1572 void trafficDistribution(sMainData *pDat)
1573 {
1574 #ifndef HT_BUILD_NC_ONLY
1575         u32 links01, links10;
1576         u8 linkCount;
1577         u8 i;
1578
1579         /*  Traffic Distribution is only used when there are exactly two nodes in the system */
1580         if (pDat->NodesDiscovered+1 != 2)
1581                 return;
1582
1583         links01 = 0;
1584         links10 = 0;
1585         linkCount = 0;
1586         for (i = 0; i < pDat->TotalLinks*2; i += 2)
1587         {
1588                 if ((pDat->PortList[i].Type == PORTLIST_TYPE_CPU) && (pDat->PortList[i+1].Type == PORTLIST_TYPE_CPU))
1589                 {
1590                         links01 |= (u32)1 << pDat->PortList[i].Link;
1591                         links10 |= (u32)1 << pDat->PortList[i+1].Link;
1592                         linkCount++;
1593                 }
1594         }
1595         ASSERT(linkCount != 0);
1596         if (linkCount == 1)
1597                 return; /*  Don't setup Traffic Distribution if only one link is being used */
1598
1599         pDat->nb->writeTrafficDistribution(links01, links10, pDat->nb);
1600 #endif /* HT_BUILD_NC_ONLY */
1601 }
1602
1603 /*----------------------------------------------------------------------------------------
1604  * void
1605  * tuning(sMainData *pDat)
1606  *
1607  *  Description:
1608  *       Handle system and performance tunings, such as traffic distribution, fifo and
1609  *       buffer tuning, and special config tunings.
1610  *
1611  *  Parameters:
1612  *      @param[in] sMainData* pDat = our global state, port list data
1613  * ---------------------------------------------------------------------------------------
1614  */
1615 void tuning(sMainData *pDat)
1616 {
1617         u8 i;
1618
1619         /* See if traffic distribution can be done and do it if so
1620          * or allow system specific customization
1621          */
1622         if ((pDat->HtBlock->AMD_CB_CustomizeTrafficDistribution == NULL)
1623                 || !pDat->HtBlock->AMD_CB_CustomizeTrafficDistribution())
1624         {
1625                 trafficDistribution(pDat);
1626         }
1627
1628         /* For each node, invoke northbridge specific buffer tunings or
1629          * system specific customizations.
1630          */
1631         for (i=0; i < pDat->NodesDiscovered + 1; i++)
1632         {
1633                 if ((pDat->HtBlock->AMD_CB_CustomizeBuffers == NULL)
1634                    || !pDat->HtBlock->AMD_CB_CustomizeBuffers(i))
1635                 {
1636                         pDat->nb->bufferOptimizations(i, pDat, pDat->nb);
1637                 }
1638         }
1639 }
1640
1641 /*----------------------------------------------------------------------------------------
1642  * BOOL
1643  * isSanityCheckOk()
1644  *
1645  *  Description:
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.
1648  *
1649  *  Parameters:
1650  *      @param[out] result BOOL  = true if check is ok, false if it failed
1651  * ---------------------------------------------------------------------------------------
1652  */
1653 BOOL isSanityCheckOk()
1654 {
1655         uint64 qValue;
1656
1657         AmdMSRRead(APIC_Base, &qValue);
1658
1659         return ((qValue.lo & ((u32)1 << APIC_Base_BSP)) != 0);
1660 }
1661
1662 /***************************************************************************
1663  ***                             HT Initialize                             ***
1664  ***************************************************************************/
1665
1666 /*----------------------------------------------------------------------------------------
1667  * void
1668  * htInitialize(AMD_HTBLOCK *pBlock)
1669  *
1670  *  Description:
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
1674  *       optimization.
1675  *
1676  *  Parameters:
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  * ---------------------------------------------------------------------------------------
1682  */
1683 void amdHtInitialize(AMD_HTBLOCK *pBlock)
1684 {
1685         sMainData pDat;
1686         cNorthBridge nb;
1687
1688         if (isSanityCheckOk())
1689         {
1690                 newNorthBridge(0, &nb);
1691
1692                 pDat.HtBlock = pBlock;
1693                 pDat.nb = &nb;
1694                 pDat.sysMpCap = nb.maxNodes;
1695                 nb.isCapable(0, &pDat, pDat.nb);
1696                 coherentInit(&pDat);
1697
1698                 pDat.AutoBusCurrent = pBlock->AutoBusStart;
1699                 pDat.UsedCfgMapEntires = 0;
1700                 ncInit(&pDat);
1701                 linkOptimization(&pDat);
1702                 tuning(&pDat);
1703         }
1704 }