Clean up comments, whitespace, and copyright date in the AMD HT code.
[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  * int
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 int 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         int 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(unsigned char *graph, int nodeA, int 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(unsigned char *graph, int nodeA, int nodeB)
223 {
224         int 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 = {sizeof(sHtEventCohFamilyFeud),
419                                                                 currentNode,
420                                                                 currentLink,
421                                                                 pDat->NodesDiscovered};
422
423                                         pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,
424                                                                         HT_EVENT_COH_FAMILY_FEUD,
425                                                                         (u8 *)&evt);
426                                 }
427
428                                 /* If node is not compatible, force boot to 1P
429                                  * If they are not compatible stop cHT init and:
430                                  *      1. Disable all cHT links on the BSP
431                                  *      2. Configure the BSP routing tables as a UP.
432                                  *      3. Notify main BIOS.
433                                  */
434                                 pDat->NodesDiscovered = 0;
435                                 currentNode = 0;
436                                 pDat->TotalLinks = 0;
437                                 /* Abandon our coherent link data structure.  At this point there may
438                                  * be coherent links on the BSP that are not yet in the portList, and
439                                  * we have to turn them off anyway.  So depend on the hardware to tell us.
440                                  */
441                                 for (currentLink = 0; currentLink < pDat->nb->maxLinks; currentLink++)
442                                 {
443                                         /* Stop all links which are connected, coherent, and ready */
444                                         if (pDat->nb->verifyLinkIsCoherent(currentNode, currentLink, pDat->nb))
445                                                 pDat->nb->stopLink(currentNode, currentLink, pDat->nb);
446                                 }
447
448                                 for (nodeToKill = 0; nodeToKill < pDat->nb->maxNodes; nodeToKill++)
449                                 {
450                                         pDat->nb->writeFullRoutingTable(0, nodeToKill, ROUTETOSELF, ROUTETOSELF, 0, pDat->nb);
451                                 }
452
453                                 /* End Coherent Discovery */
454                                 STOP_HERE;
455                                 break;
456                         }
457
458                         /* Read token from Current+1 */
459                         token = pDat->nb->readToken(currentNode+1, pDat->nb);
460                         ASSERT(token <= pDat->NodesDiscovered);
461                         if (token == 0)
462                         {
463                                 pDat->NodesDiscovered++;
464                                 ASSERT(pDat->NodesDiscovered < pDat->nb->maxNodes);
465                                 /* Check the capability of northbridges against the currently known configuration */
466                                 if (!pDat->nb->isCapable(currentNode+1, pDat, pDat->nb))
467                                 {
468                                         u8 nodeToKill;
469
470                                         /* Notify BIOS of event  */
471                                         if (pDat->HtBlock->AMD_CB_EventNotify)
472                                         {
473                                                 sHtEventCohMpCapMismatch evt = {sizeof(sHtEventCohMpCapMismatch),
474                                                                         currentNode,
475                                                                         currentLink,
476                                                                         pDat->sysMpCap,
477                                                                         pDat->NodesDiscovered};
478
479                                                 pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,
480                                                                         HT_EVENT_COH_MPCAP_MISMATCH,
481                                                                         (u8 *)&evt);
482                                         }
483
484                                         pDat->NodesDiscovered = 0;
485                                         currentNode = 0;
486                                         pDat->TotalLinks = 0;
487
488                                         for (nodeToKill = 0; nodeToKill < pDat->nb->maxNodes; nodeToKill++)
489                                         {
490                                                 pDat->nb->writeFullRoutingTable(0, nodeToKill, ROUTETOSELF, ROUTETOSELF, 0, pDat->nb);
491                                         }
492
493                                         /* End Coherent Discovery */
494                                         STOP_HERE;
495                                         break;
496                                 }
497
498                                 token = pDat->NodesDiscovered;
499                                 pDat->nb->writeToken(currentNode+1, token, pDat->nb);
500                                 /* Inform that we have discovered a node, so that logical id to
501                                  * socket mapping info can be recorded.
502                                  */
503                                 if (pDat->HtBlock->AMD_CB_EventNotify)
504                                 {
505                                         sHtEventCohNodeDiscovered evt = {sizeof(sHtEventCohNodeDiscovered),
506                                                                 currentNode,
507                                                                 currentLink,
508                                                                 token};
509
510                                         pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_INFO,
511                                                                 HT_EVENT_COH_NODE_DISCOVERED,
512                                                                 (u8 *)&evt);
513                                 }
514                         }
515
516                         if (pDat->TotalLinks == MAX_PLATFORM_LINKS)
517                         {
518                                 /*
519                                  * Exceeded our capacity to describe all coherent links found in the system.
520                                  * Error strategy:
521                                  * Auto recovery is not possible because data space is already all used.
522                                  * If the callback is not implemented or returns we will continue to initialize
523                                  * the fabric we are capable of representing, adding no more nodes or links.
524                                  * This should yield a bootable topology, but likely not the one intended.
525                                  * We cannot continue discovery, there may not be any way to route a new
526                                  * node back to the BSP if we can't add links to our representation of the system.
527                                  */
528                                 if (pDat->HtBlock->AMD_CB_EventNotify)
529                                 {
530                                         sHtEventCohLinkExceed evt = {sizeof(sHtEventCohLinkExceed),
531                                                                 currentNode,
532                                                                 currentLink,
533                                                                 token,
534                                                                 pDat->NodesDiscovered,
535                                                                 pDat->nb->maxLinks};
536
537                                         pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,
538                                                                         HT_EVENT_COH_LINK_EXCEED,
539                                                                         (u8 *)&evt);
540                                 }
541                                 /* Force link and node loops to halt */
542                                 STOP_HERE;
543                                 currentNode = pDat->NodesDiscovered;
544                                 break;
545                         }
546
547                         pDat->PortList[pDat->TotalLinks*2].Type = PORTLIST_TYPE_CPU;
548                         pDat->PortList[pDat->TotalLinks*2].Link = currentLink;
549                         pDat->PortList[pDat->TotalLinks*2].NodeID = currentNode;
550
551                         pDat->PortList[pDat->TotalLinks*2+1].Type = PORTLIST_TYPE_CPU;
552                         pDat->PortList[pDat->TotalLinks*2+1].Link = pDat->nb->readDefLnk(currentNode+1, pDat->nb);
553                         pDat->PortList[pDat->TotalLinks*2+1].NodeID = token;
554
555                         pDat->TotalLinks++;
556
557                         if ( !pDat->sysMatrix[currentNode][token] )
558                         {
559                                 pDat->sysDegree[currentNode]++;
560                                 pDat->sysDegree[token]++;
561                                 pDat->sysMatrix[currentNode][token] = TRUE;
562                                 pDat->sysMatrix[token][currentNode] = TRUE;
563                         }
564                 }
565                 currentNode++;
566         }
567 }
568
569
570 /***************************************************************************
571  ***            ISOMORPHISM BASED ROUTING TABLE GENERATION CODE         ***
572  ***************************************************************************/
573
574 /*----------------------------------------------------------------------------------------
575  * BOOL
576  * isoMorph(u8 i, sMainData *pDat)
577  *
578  *  Description:
579  *       Is graphA isomorphic to graphB?
580  *       if this function returns true, then Perm will contain the permutation
581  *       required to transform graphB into graphA.
582  *       We also use the degree of each node, that is the number of connections it has, to
583  *       speed up rejection of non-isomorphic graphs (if there is a node in graphA with n
584  *       connections, there must be at least one unmatched in graphB with n connections).
585  *
586  *  Parameters:
587  *      @param[in] u8 i   = the discovered node which we are trying to match
588  *                  with a permutation the topology
589  *      @param[in]/@param[out] sMainData* pDat  = our global state, degree and adjacency matrix,
590  *                                output a permutation if successful
591  *      @param[out] BOOL results = the graphs are (or are not) isomorphic
592  * ---------------------------------------------------------------------------------------
593  */
594 BOOL isoMorph(u8 i, sMainData *pDat)
595 {
596         u8 j, k;
597         u8 nodecnt;
598
599         /* We have only been called if nodecnt == pSelected->size ! */
600         nodecnt = pDat->NodesDiscovered+1;
601
602         if (i != nodecnt)
603         {
604                 /*  Keep building the permutation */
605                 for (j = 0; j < nodecnt; j++)
606                 {
607                         /*  Make sure the degree matches */
608                         if (pDat->sysDegree[i] != pDat->dbDegree[j])
609                                 continue;
610
611                         /*  Make sure that j hasn't been used yet (ought to use a "used" */
612                         /*  array instead, might be faster) */
613                         for (k = 0; k < i; k++)
614                         {
615                                 if (pDat->Perm[k] == j)
616                                         break;
617                         }
618                         if (k != i)
619                                 continue;
620                         pDat->Perm[i] = j;
621                         if (isoMorph(i+1, pDat))
622                                 return TRUE;
623                 }
624                 return FALSE;
625         } else {
626                 /*  Test to see if the permutation is isomorphic */
627                 for (j = 0; j < nodecnt; j++)
628                 {
629                         for (k = 0; k < nodecnt; k++)
630                         {
631                                 if ( pDat->sysMatrix[j][k] !=
632                                    pDat->dbMatrix[pDat->Perm[j]][pDat->Perm[k]] )
633                                         return FALSE;
634                         }
635                 }
636                 return TRUE;
637         }
638 }
639
640
641 /*----------------------------------------------------------------------------------------
642  * void
643  * lookupComputeAndLoadRoutingTables(sMainData *pDat)
644  *
645  *  Description:
646  *       Using the description of the fabric topology we discovered, try to find a match
647  *       among the supported topologies.  A supported topology description matches
648  *       the discovered fabric if the nodes can be matched in such a way that all the nodes connected
649  *       in one set are exactly the nodes connected in the other (formally, that the graphs are
650  *       isomorphic).  Which links are used is not really important to matching.  If the graphs
651  *       match, then there is a permutation of one that translates the node positions and linkages
652  *       to the other.
653  *
654  *       In order to make the isomorphism test efficient, we test for matched number of nodes
655  *       (a 4 node fabric is not isomorphic to a 2 node topology), and provide degrees of nodes
656  *       to the isomorphism test.
657  *
658  *       The generic routing table solution for any topology is predetermined and represented
659  *       as part of the topology.  The permutation we computed tells us how to interpret the
660  *       routing onto the fabric we discovered.  We do this working backward from the last
661  *       node discovered to the BSP, writing the routing tables as we go.
662  *
663  *  Parameters:
664  *      @param[in]    sMainData* pDat = our global state, the discovered fabric,
665  *      @param[out]                     degree matrix, permutation
666  * ---------------------------------------------------------------------------------------
667  */
668 void lookupComputeAndLoadRoutingTables(sMainData *pDat)
669 {
670         u8 **pTopologyList;
671         u8 *pSelected;
672
673         int i, j, k, size;
674
675         size = pDat->NodesDiscovered + 1;
676         /* Use the provided topology list or the internal, default one. */
677         pTopologyList = pDat->HtBlock->topolist;
678         if (pTopologyList == NULL)
679         {
680                 getAmdTopolist(&pTopologyList);
681         }
682
683         pSelected = *pTopologyList;
684         while (pSelected != NULL)
685         {
686                 if (graphHowManyNodes(pSelected) == size)
687                 {
688                         /*  Build Degree vector and Adjency Matrix for this entry */
689                         for (i = 0; i < size; i++)
690                         {
691                                 pDat->dbDegree[i] = 0;
692                                 for (j = 0; j < size; j++)
693                                 {
694                                         if (graphIsAdjacent(pSelected, i, j))
695                                         {
696                                                 pDat->dbMatrix[i][j] = 1;
697                                                 pDat->dbDegree[i]++;
698                                         }
699                                         else
700                                         {
701                                                 pDat->dbMatrix[i][j] = 0;
702                                         }
703                                 }
704                         }
705                         if (isoMorph(0, pDat))
706                                 break;  /*  A matching topology was found */
707                 }
708
709                 pTopologyList++;
710                 pSelected = *pTopologyList;
711         }
712
713         if (pSelected != NULL)
714         {
715                 /*  Compute the reverse Permutation */
716                 for (i = 0; i < size; i++)
717                 {
718                         pDat->ReversePerm[pDat->Perm[i]] = i;
719                 }
720
721                 /*  Start with the last discovered node, and move towards the BSP */
722                 for (i = size-1; i >= 0; i--)
723                 {
724                         for (j = 0; j < size; j++)
725                         {
726                                 u8 ReqTargetLink, RspTargetLink;
727                                 u8 ReqTargetNode, RspTargetNode;
728
729                                 u8 AbstractBcTargetNodes = graphGetBc(pSelected, pDat->Perm[i], pDat->Perm[j]);
730                                 u32 BcTargetLinks = 0;
731
732                                 for (k = 0; k < MAX_NODES; k++)
733                                 {
734                                         if (AbstractBcTargetNodes & ((u32)1<<k))
735                                         {
736                                                 BcTargetLinks |= (u32)1 << convertNodeToLink(i, pDat->ReversePerm[k], pDat);
737                                         }
738                                 }
739
740                                 if (i == j)
741                                 {
742                                         ReqTargetLink = ROUTETOSELF;
743                                         RspTargetLink = ROUTETOSELF;
744                                 }
745                                 else
746                                 {
747                                         ReqTargetNode = graphGetReq(pSelected, pDat->Perm[i], pDat->Perm[j]);
748                                         ReqTargetLink = convertNodeToLink(i, pDat->ReversePerm[ReqTargetNode], pDat);
749
750                                         RspTargetNode = graphGetRsp(pSelected, pDat->Perm[i], pDat->Perm[j]);
751                                         RspTargetLink = convertNodeToLink(i, pDat->ReversePerm[RspTargetNode], pDat);
752                                 }
753
754                                 pDat->nb->writeFullRoutingTable(i, j, ReqTargetLink, RspTargetLink, BcTargetLinks, pDat->nb);
755                         }
756                         /* Clean up discovery 'footprint' that otherwise remains in the routing table.  It didn't hurt
757                          * anything, but might cause confusion during debug and validation.  Do this by setting the
758                          * route back to all self routes. Since it's the node that would be one more than actually installed,
759                          * this only applies if less than maxNodes were found.
760                          */
761                         if (size < pDat->nb->maxNodes)
762                         {
763                                 pDat->nb->writeFullRoutingTable(i, size, ROUTETOSELF, ROUTETOSELF, 0, pDat->nb);
764                         }
765                 }
766
767         }
768         else
769         {
770                 /*
771                  * No Matching Topology was found
772                  * Error Strategy:
773                  * Auto recovery doesn't seem likely, Force boot as 1P.
774                  * For reporting, logging, provide number of nodes
775                  * If not implemented or returns, boot as BSP uniprocessor.
776                  */
777                 if (pDat->HtBlock->AMD_CB_EventNotify)
778                 {
779                         sHtEventCohNoTopology evt = {sizeof(sHtEventCohNoTopology),
780                                                         pDat->NodesDiscovered};
781
782                         pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,
783                                                 HT_EVENT_COH_NO_TOPOLOGY,
784                                                 (u8 *)&evt);
785                 }
786                 STOP_HERE;
787                 /* Force 1P */
788                 pDat->NodesDiscovered = 0;
789                 pDat->TotalLinks = 0;
790                 pDat->nb->enableRoutingTables(0, pDat->nb);
791         }
792 }
793 #endif /* HT_BUILD_NC_ONLY */
794
795
796 /*----------------------------------------------------------------------------------------
797  * void
798  * finializeCoherentInit(sMainData *pDat)
799  *
800  *  Description:
801  *       Find the total number of cores and update the number of nodes and cores in all cpus.
802  *       Limit cpu config access to installed cpus.
803  *
804  *  Parameters:
805  *      @param[in] sMainData* pDat = our global state, number of nodes discovered.
806  * ---------------------------------------------------------------------------------------
807  */
808 void finializeCoherentInit(sMainData *pDat)
809 {
810         u8 curNode;
811
812         u8 totalCores = 0;
813         for (curNode = 0; curNode < pDat->NodesDiscovered+1; curNode++)
814         {
815                 totalCores += pDat->nb->getNumCoresOnNode(curNode, pDat->nb);
816         }
817
818         for (curNode = 0; curNode < pDat->NodesDiscovered+1; curNode++)
819         {
820                 pDat->nb->setTotalNodesAndCores(curNode, pDat->NodesDiscovered+1, totalCores, pDat->nb);
821         }
822
823         for (curNode = 0; curNode < pDat->NodesDiscovered+1; curNode++)
824         {
825                 pDat->nb->limitNodes(curNode, pDat->nb);
826         }
827
828 }
829
830 /*----------------------------------------------------------------------------------------
831  * void
832  * coherentInit(sMainData *pDat)
833  *
834  *  Description:
835  *       Perform discovery and initialization of the coherent fabric.
836  *
837  *  Parameters:
838  *      @param[in] sMainData* pDat = our global state
839  * ---------------------------------------------------------------------------------------
840  */
841 void coherentInit(sMainData *pDat)
842 {
843         int i, j;
844
845 #ifdef HT_BUILD_NC_ONLY
846         /* Replace discovery process with:
847          * No other nodes, no coherent links
848          * Enable routing tables on currentNode, for power on self route
849          */
850         pDat->NodesDiscovered = 0;
851         pDat->TotalLinks = 0;
852         pDat->nb->enableRoutingTables(0, pDat->nb);
853 #else
854         pDat->NodesDiscovered = 0;
855         pDat->TotalLinks = 0;
856         for (i = 0; i < MAX_NODES; i++)
857         {
858                 pDat->sysDegree[i] = 0;
859                 for (j = 0; j < MAX_NODES; j++)
860                 {
861                         pDat->sysMatrix[i][j] = 0;
862                 }
863         }
864
865         htDiscoveryFloodFill(pDat);
866         lookupComputeAndLoadRoutingTables(pDat);
867 #endif
868         finializeCoherentInit(pDat);
869 }
870
871 /***************************************************************************
872  ***                        Non-coherent init code                        ***
873  ***                              Algorithms                              ***
874  ***************************************************************************/
875 /*----------------------------------------------------------------------------------------
876  * void
877  * processLink(u8 node, u8 link, sMainData *pDat)
878  *
879  *  Description:
880  *       Process a non-coherent link, enabling a range of bus numbers, and setting the device
881  *       ID for all devices found
882  *
883  *  Parameters:
884  *      @param[in] u8 node = Node on which to process nc init
885  *      @param[in] u8 link = The non-coherent link on that node
886  *      @param[in] sMainData* pDat = our global state
887  * ---------------------------------------------------------------------------------------
888  */
889 void processLink(u8 node, u8 link, sMainData *pDat)
890 {
891         u8 secBus, subBus;
892         u32 currentBUID;
893         u32 temp;
894         u32 unitIDcnt;
895         SBDFO currentPtr;
896         u8 depth;
897         u8 *pSwapPtr;
898
899         SBDFO lastSBDFO = ILLEGAL_SBDFO;
900         u8 lastLink = 0;
901
902         ASSERT(node < pDat->nb->maxNodes && link < pDat->nb->maxLinks);
903
904         if ((pDat->HtBlock->AMD_CB_OverrideBusNumbers == NULL)
905            || !pDat->HtBlock->AMD_CB_OverrideBusNumbers(node, link, &secBus, &subBus))
906         {
907                 /* Assign Bus numbers */
908                 if (pDat->AutoBusCurrent >= pDat->HtBlock->AutoBusMax)
909                 {
910                         /* If we run out of Bus Numbers notify, if call back unimplemented or if it
911                          * returns, skip this chain
912                          */
913                         if (pDat->HtBlock->AMD_CB_EventNotify)
914                         {
915                                 sHTEventNcohBusMaxExceed evt = {sizeof(sHTEventNcohBusMaxExceed), node, link, pDat->AutoBusCurrent};
916
917                                 pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,HT_EVENT_NCOH_BUS_MAX_EXCEED,(u8 *)&evt);
918                         }
919                         STOP_HERE;
920                         return;
921                 }
922
923                 if (pDat->UsedCfgMapEntires >= 4)
924                 {
925                         /* If we have used all the PCI Config maps we can't add another chain.
926                          * Notify and if call back is unimplemented or returns, skip this chain.
927                          */
928                         if (pDat->HtBlock->AMD_CB_EventNotify)
929                         {
930                                 sHtEventNcohCfgMapExceed evt = {sizeof(sHtEventNcohCfgMapExceed), node, link};
931
932                                 pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,
933                                                         HT_EVENT_NCOH_CFG_MAP_EXCEED,
934                                                         (u8 *)&evt);
935                         }
936                         STOP_HERE;
937                         return;
938                 }
939
940                 secBus = pDat->AutoBusCurrent;
941                 subBus = secBus + pDat->HtBlock->AutoBusIncrement-1;
942                 pDat->AutoBusCurrent += pDat->HtBlock->AutoBusIncrement;
943         }
944
945         pDat->nb->setCFGAddrMap(pDat->UsedCfgMapEntires, secBus, subBus, node, link, pDat, pDat->nb);
946         pDat->UsedCfgMapEntires++;
947
948         if ((pDat->HtBlock->AMD_CB_ManualBUIDSwapList != NULL)
949          && pDat->HtBlock->AMD_CB_ManualBUIDSwapList(node, link, &pSwapPtr))
950         {
951                 /* Manual non-coherent BUID assignment */
952
953                 /* Assign BUID's per manual override */
954                 while (*pSwapPtr != 0xFF)
955                 {
956                         currentPtr = MAKE_SBDFO(0, secBus, *pSwapPtr, 0, 0);
957                         pSwapPtr++;
958
959                         do
960                         {
961                                 AmdPCIFindNextCap(&currentPtr);
962                                 ASSERT(currentPtr != ILLEGAL_SBDFO);
963                                 AmdPCIRead(currentPtr, &temp);
964                         } while (!IS_HT_SLAVE_CAPABILITY(temp));
965
966                         currentBUID = *pSwapPtr;
967                         pSwapPtr++;
968                         AmdPCIWriteBits(currentPtr, 20, 16, &currentBUID);
969                 }
970
971                 /* Build chain of devices */
972                 depth = 0;
973                 pSwapPtr++;
974                 while (*pSwapPtr != 0xFF)
975                 {
976                         pDat->PortList[pDat->TotalLinks*2].NodeID = node;
977                         if (depth == 0)
978                         {
979                                 pDat->PortList[pDat->TotalLinks*2].Type = PORTLIST_TYPE_CPU;
980                                 pDat->PortList[pDat->TotalLinks*2].Link = link;
981                         }
982                         else
983                         {
984                                 pDat->PortList[pDat->TotalLinks*2].Type = PORTLIST_TYPE_IO;
985                                 pDat->PortList[pDat->TotalLinks*2].Link = 1-lastLink;
986                                 pDat->PortList[pDat->TotalLinks*2].HostLink = link;
987                                 pDat->PortList[pDat->TotalLinks*2].HostDepth = depth-1;
988                                 pDat->PortList[pDat->TotalLinks*2].Pointer = lastSBDFO;
989                         }
990
991                         pDat->PortList[pDat->TotalLinks*2+1].Type = PORTLIST_TYPE_IO;
992                         pDat->PortList[pDat->TotalLinks*2+1].NodeID = node;
993                         pDat->PortList[pDat->TotalLinks*2+1].HostLink = link;
994                         pDat->PortList[pDat->TotalLinks*2+1].HostDepth = depth;
995
996                         currentPtr = MAKE_SBDFO(0, secBus, (*pSwapPtr & 0x3F), 0, 0);
997                         do
998                         {
999                                 AmdPCIFindNextCap(&currentPtr);
1000                                 ASSERT(currentPtr != ILLEGAL_SBDFO);
1001                                 AmdPCIRead(currentPtr, &temp);
1002                         } while (!IS_HT_SLAVE_CAPABILITY(temp));
1003                         pDat->PortList[pDat->TotalLinks*2+1].Pointer = currentPtr;
1004                         lastSBDFO = currentPtr;
1005
1006                         /* Bit 6 indicates whether orientation override is desired.
1007                          * Bit 7 indicates the upstream link if overriding.
1008                          */
1009                         /* assert catches at least the one known incorrect setting */
1010                         ASSERT ((*pSwapPtr & 0x40) || (!(*pSwapPtr & 0x80)));
1011                         if (*pSwapPtr & 0x40)
1012                         {
1013                                 /* Override the device's orientation */
1014                                 lastLink = *pSwapPtr >> 7;
1015                         }
1016                         else
1017                         {
1018                                 /* Detect the device's orientation */
1019                                 AmdPCIReadBits(currentPtr, 26, 26, &temp);
1020                                 lastLink = (u8)temp;
1021                         }
1022                         pDat->PortList[pDat->TotalLinks*2+1].Link = lastLink;
1023
1024                         depth++;
1025                         pDat->TotalLinks++;
1026                         pSwapPtr++;
1027                 }
1028         }
1029         else
1030         {
1031                 /* Automatic non-coherent device detection */
1032                 depth = 0;
1033                 currentBUID = 1;
1034                 while (1)
1035                 {
1036                         currentPtr = MAKE_SBDFO(0, secBus, 0, 0, 0);
1037
1038                         AmdPCIRead(currentPtr, &temp);
1039                         if (temp == 0xFFFFFFFF)
1040                                 /* No device found at currentPtr */
1041                                 break;
1042
1043                         if (pDat->TotalLinks == MAX_PLATFORM_LINKS)
1044                         {
1045                                 /*
1046                                  * Exceeded our capacity to describe all non-coherent links found in the system.
1047                                  * Error strategy:
1048                                  * Auto recovery is not possible because data space is already all used.
1049                                  */
1050                                 if (pDat->HtBlock->AMD_CB_EventNotify)
1051                                 {
1052                                         sHtEventNcohLinkExceed evt = {sizeof(sHtEventNcohLinkExceed),
1053                                                         node,
1054                                                         link,
1055                                                         depth,
1056                                                         pDat->nb->maxLinks};
1057
1058                                         pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,
1059                                                                 HT_EVENT_NCOH_LINK_EXCEED,
1060                                                                 (u8 *)&evt);
1061                                 }
1062                                 /* Force link loop to halt */
1063                                 STOP_HERE;
1064                                 break;
1065                         }
1066
1067                         pDat->PortList[pDat->TotalLinks*2].NodeID = node;
1068                         if (depth == 0)
1069                         {
1070                                 pDat->PortList[pDat->TotalLinks*2].Type = PORTLIST_TYPE_CPU;
1071                                 pDat->PortList[pDat->TotalLinks*2].Link = link;
1072                         }
1073                         else
1074                         {
1075                                 pDat->PortList[pDat->TotalLinks*2].Type = PORTLIST_TYPE_IO;
1076                                 pDat->PortList[pDat->TotalLinks*2].Link = 1-lastLink;
1077                                 pDat->PortList[pDat->TotalLinks*2].HostLink = link;
1078                                 pDat->PortList[pDat->TotalLinks*2].HostDepth = depth-1;
1079                                 pDat->PortList[pDat->TotalLinks*2].Pointer = lastSBDFO;
1080                         }
1081
1082                         pDat->PortList[pDat->TotalLinks*2+1].Type = PORTLIST_TYPE_IO;
1083                         pDat->PortList[pDat->TotalLinks*2+1].NodeID = node;
1084                         pDat->PortList[pDat->TotalLinks*2+1].HostLink = link;
1085                         pDat->PortList[pDat->TotalLinks*2+1].HostDepth = depth;
1086
1087                         do
1088                         {
1089                                 AmdPCIFindNextCap(&currentPtr);
1090                                 ASSERT(currentPtr != ILLEGAL_SBDFO);
1091                                 AmdPCIRead(currentPtr, &temp);
1092                         } while (!IS_HT_SLAVE_CAPABILITY(temp));
1093
1094                         AmdPCIReadBits(currentPtr, 25, 21, &unitIDcnt);
1095                         if ((unitIDcnt + currentBUID > 31) || ((secBus == 0) && (unitIDcnt + currentBUID > 24)))
1096                         {
1097                                 /* An error handler for the case where we run out of BUID's on a chain */
1098                                 if (pDat->HtBlock->AMD_CB_EventNotify)
1099                                 {
1100                                         sHtEventNcohBuidExceed evt = {sizeof(sHtEventNcohBuidExceed),
1101                                                                 node, link, depth, (u8)currentBUID, (u8)unitIDcnt};
1102
1103                                         pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,HT_EVENT_NCOH_BUID_EXCEED,(u8 *)&evt);
1104                                 }
1105                                 STOP_HERE;
1106                                 break;
1107                         }
1108                         AmdPCIWriteBits(currentPtr, 20, 16, &currentBUID);
1109
1110
1111                         currentPtr += MAKE_SBDFO(0, 0, currentBUID, 0, 0);
1112                         AmdPCIReadBits(currentPtr, 20, 16, &temp);
1113                         if (temp != currentBUID)
1114                         {
1115                                 /* An error handler for this critical error */
1116                                 if (pDat->HtBlock->AMD_CB_EventNotify)
1117                                 {
1118                                         sHtEventNcohDeviceFailed evt = {sizeof(sHtEventNcohDeviceFailed),
1119                                                         node, link, depth, (u8)currentBUID};
1120
1121                                         pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,HT_EVENT_NCOH_DEVICE_FAILED,(u8 *)&evt);
1122                                 }
1123                                 STOP_HERE;
1124                                 break;
1125                         }
1126
1127                         AmdPCIReadBits(currentPtr, 26, 26, &temp);
1128                         pDat->PortList[pDat->TotalLinks*2+1].Link = (u8)temp;
1129                         pDat->PortList[pDat->TotalLinks*2+1].Pointer = currentPtr;
1130
1131                         lastLink = (u8)temp;
1132                         lastSBDFO = currentPtr;
1133
1134                         depth++;
1135                         pDat->TotalLinks++;
1136                         currentBUID += unitIDcnt;
1137                 }
1138                 if (pDat->HtBlock->AMD_CB_EventNotify)
1139                 {
1140                         /* Provide information on automatic device results */
1141                         sHtEventNcohAutoDepth evt = {sizeof(sHtEventNcohAutoDepth), node, link, (depth - 1)};
1142
1143                         pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_INFO,HT_EVENT_NCOH_AUTO_DEPTH,(u8 *)&evt);
1144                 }
1145         }
1146 }
1147
1148
1149 /*----------------------------------------------------------------------------------------
1150  * void
1151  * ncInit(sMainData *pDat)
1152  *
1153  *  Description:
1154  *       Initialize the non-coherent fabric. Begin with the compat link on the BSP, then
1155  *       find and initialize all other non-coherent chains.
1156  *
1157  *  Parameters:
1158  *      @param[in]  sMainData*  pDat = our global state
1159  * ---------------------------------------------------------------------------------------
1160  */
1161 void ncInit(sMainData *pDat)
1162 {
1163         u8 node, link;
1164         u8 compatLink;
1165
1166         compatLink = pDat->nb->readSbLink(pDat->nb);
1167         processLink(0, compatLink, pDat);
1168
1169         for (node = 0; node <= pDat->NodesDiscovered; node++)
1170         {
1171                 for (link = 0; link < pDat->nb->maxLinks; link++)
1172                 {
1173                         if (pDat->HtBlock->AMD_CB_IgnoreLink && pDat->HtBlock->AMD_CB_IgnoreLink(node, link))
1174                                 continue;   /*  Skip the link */
1175
1176                         if (node == 0 && link == compatLink)
1177                                 continue;
1178
1179                         if (pDat->nb->readTrueLinkFailStatus(node, link, pDat, pDat->nb))
1180                                 continue;
1181
1182                         if (pDat->nb->verifyLinkIsNonCoherent(node, link, pDat->nb))
1183                                 processLink(node, link, pDat);
1184                 }
1185         }
1186 }
1187
1188 /***************************************************************************
1189  ***                            Link Optimization                         ***
1190  ***************************************************************************/
1191
1192 /*----------------------------------------------------------------------------------------
1193  * void
1194  * regangLinks(sMainData *pDat)
1195  *
1196  *  Description:
1197  *       Test the sublinks of a link to see if they qualify to be reganged.  If they do,
1198  *       update the port list data to indicate that this should be done.  Note that no
1199  *       actual hardware state is changed in this routine.
1200  *
1201  *  Parameters:
1202  *      @param[in,out] sMainData*  pDat = our global state
1203  * ---------------------------------------------------------------------------------------
1204  */
1205 void regangLinks(sMainData *pDat)
1206 {
1207 #ifndef HT_BUILD_NC_ONLY
1208         u8 i, j;
1209         for (i = 0; i < pDat->TotalLinks*2; i += 2)
1210         {
1211                 ASSERT(pDat->PortList[i].Type < 2 && pDat->PortList[i].Link < pDat->nb->maxLinks);  /*  Data validation */
1212                 ASSERT(pDat->PortList[i+1].Type < 2 && pDat->PortList[i+1].Link < pDat->nb->maxLinks); /*  data validation */
1213                 ASSERT(!(pDat->PortList[i].Type == PORTLIST_TYPE_IO && pDat->PortList[i+1].Type == PORTLIST_TYPE_CPU));  /*  ensure src is closer to the bsp than dst */
1214
1215                 /* Regang is false unless we pass all conditions below */
1216                 pDat->PortList[i].SelRegang = FALSE;
1217                 pDat->PortList[i+1].SelRegang = FALSE;
1218
1219                 if ( (pDat->PortList[i].Type != PORTLIST_TYPE_CPU) || (pDat->PortList[i+1].Type != PORTLIST_TYPE_CPU))
1220                         continue;   /*  Only process cpu to cpu links */
1221
1222                 for (j = i+2; j < pDat->TotalLinks*2; j += 2)
1223                 {
1224                         if ( (pDat->PortList[j].Type != PORTLIST_TYPE_CPU) || (pDat->PortList[j+1].Type != PORTLIST_TYPE_CPU) )
1225                                 continue;   /*  Only process cpu to cpu links */
1226
1227                         if (pDat->PortList[i].NodeID != pDat->PortList[j].NodeID)
1228                                 continue;   /*  Links must be from the same source */
1229
1230                         if (pDat->PortList[i+1].NodeID != pDat->PortList[j+1].NodeID)
1231                                 continue;   /*  Link must be to the same target */
1232
1233                         if ((pDat->PortList[i].Link & 3) != (pDat->PortList[j].Link & 3))
1234                                 continue;   /*  Ensure same source base port */
1235
1236                         if ((pDat->PortList[i+1].Link & 3) != (pDat->PortList[j+1].Link & 3))
1237                                 continue;   /*  Ensure same destination base port */
1238
1239                         if ((pDat->PortList[i].Link & 4) != (pDat->PortList[i+1].Link & 4))
1240                                 continue;   /*  Ensure sublink0 routes to sublink0 */
1241
1242                         ASSERT((pDat->PortList[j].Link & 4) == (pDat->PortList[j+1].Link & 4)); /*  (therefore sublink1 routes to sublink1) */
1243
1244                         if (pDat->HtBlock->AMD_CB_SkipRegang &&
1245                                 pDat->HtBlock->AMD_CB_SkipRegang(pDat->PortList[i].NodeID,
1246                                                         pDat->PortList[i].Link & 0x03,
1247                                                         pDat->PortList[i+1].NodeID,
1248                                                         pDat->PortList[i+1].Link & 0x03))
1249                         {
1250                                 continue;   /*  Skip regang */
1251                         }
1252
1253
1254                         pDat->PortList[i].Link &= 0x03; /*  Force to point to sublink0 */
1255                         pDat->PortList[i+1].Link &= 0x03;
1256                         pDat->PortList[i].SelRegang = TRUE; /*  Enable link reganging */
1257                         pDat->PortList[i+1].SelRegang = TRUE;
1258                         pDat->PortList[i].PrvWidthOutCap = HT_WIDTH_16_BITS;
1259                         pDat->PortList[i+1].PrvWidthOutCap = HT_WIDTH_16_BITS;
1260                         pDat->PortList[i].PrvWidthInCap = HT_WIDTH_16_BITS;
1261                         pDat->PortList[i+1].PrvWidthInCap = HT_WIDTH_16_BITS;
1262
1263                         /*  Delete PortList[j, j+1], slow but easy to debug implementation */
1264                         pDat->TotalLinks--;
1265                         Amdmemcpy(&(pDat->PortList[j]), &(pDat->PortList[j+2]), sizeof(sPortDescriptor)*(pDat->TotalLinks*2-j));
1266                         Amdmemset(&(pDat->PortList[pDat->TotalLinks*2]), INVALID_LINK, sizeof(sPortDescriptor)*2);
1267
1268                         /* //High performance, but would make debuging harder due to 'shuffling' of the records */
1269                         /* //Amdmemcpy(PortList[TotalPorts-2], PortList[j], SIZEOF(sPortDescriptor)*2); */
1270                         /* //TotalPorts -=2; */
1271
1272                         break; /*  Exit loop, advance to PortList[i+2] */
1273                 }
1274         }
1275 #endif /* HT_BUILD_NC_ONLY */
1276 }
1277
1278 /*----------------------------------------------------------------------------------------
1279  * void
1280  * selectOptimalWidthAndFrequency(sMainData *pDat)
1281  *
1282  *  Description:
1283  *       For all links:
1284  *       Examine both sides of a link and determine the optimal frequency and width,
1285  *       taking into account externally provided limits and enforcing any other limit
1286  *       or matching rules as applicable except sublink balancing.   Update the port
1287  *       list date with the optimal settings.
1288  *       Note no hardware state changes in this routine.
1289  *
1290  *  Parameters:
1291  *      @param[in,out]  sMainData*  pDat = our global state, port list data
1292  * ---------------------------------------------------------------------------------------
1293  */
1294 void selectOptimalWidthAndFrequency(sMainData *pDat)
1295 {
1296         u8 i, j;
1297         u32 temp;
1298         u16 cbPCBFreqLimit;
1299         u8 cbPCBABDownstreamWidth;
1300         u8 cbPCBBAUpstreamWidth;
1301
1302         for (i = 0; i < pDat->TotalLinks*2; i += 2)
1303         {
1304                 cbPCBFreqLimit = 0xFFFF;
1305                 cbPCBABDownstreamWidth = 16;
1306                 cbPCBBAUpstreamWidth = 16;
1307
1308                 if ( (pDat->PortList[i].Type == PORTLIST_TYPE_CPU) && (pDat->PortList[i+1].Type == PORTLIST_TYPE_CPU))
1309                 {
1310                         if (pDat->HtBlock->AMD_CB_Cpu2CpuPCBLimits)
1311                         {
1312                                 pDat->HtBlock->AMD_CB_Cpu2CpuPCBLimits(
1313                                                 pDat->PortList[i].NodeID,
1314                                                 pDat->PortList[i].Link,
1315                                                 pDat->PortList[i+1].NodeID,
1316                                                 pDat->PortList[i+1].Link,
1317                                                 &cbPCBABDownstreamWidth,
1318                                                 &cbPCBBAUpstreamWidth, &cbPCBFreqLimit
1319                                                 );
1320                         }
1321                 }
1322                 else
1323                 {
1324                         if (pDat->HtBlock->AMD_CB_IOPCBLimits)
1325                         {
1326                                 pDat->HtBlock->AMD_CB_IOPCBLimits(
1327                                                 pDat->PortList[i+1].NodeID,
1328                                                 pDat->PortList[i+1].HostLink,
1329                                                 pDat->PortList[i+1].HostDepth,
1330                                                 &cbPCBABDownstreamWidth,
1331                                                  &cbPCBBAUpstreamWidth, &cbPCBFreqLimit
1332                                                 );
1333                         }
1334                 }
1335
1336
1337                 temp = pDat->PortList[i].PrvFrequencyCap;
1338                 temp &= pDat->PortList[i+1].PrvFrequencyCap;
1339                 temp &= cbPCBFreqLimit;
1340                 pDat->PortList[i].CompositeFrequencyCap = (u16)temp;
1341                 pDat->PortList[i+1].CompositeFrequencyCap = (u16)temp;
1342
1343                 ASSERT (temp != 0);
1344                 for (j = 15; ; j--)
1345                 {
1346                         if (temp & ((u32)1 << j))
1347                                 break;
1348                 }
1349
1350                 pDat->PortList[i].SelFrequency = j;
1351                 pDat->PortList[i+1].SelFrequency = j;
1352
1353                 temp = pDat->PortList[i].PrvWidthOutCap;
1354                 if (pDat->PortList[i+1].PrvWidthInCap < temp)
1355                         temp = pDat->PortList[i+1].PrvWidthInCap;
1356                 if (cbPCBABDownstreamWidth < temp)
1357                         temp = cbPCBABDownstreamWidth;
1358                 pDat->PortList[i].SelWidthOut = (u8)temp;
1359                 pDat->PortList[i+1].SelWidthIn = (u8)temp;
1360
1361                 temp = pDat->PortList[i].PrvWidthInCap;
1362                 if (pDat->PortList[i+1].PrvWidthOutCap < temp)
1363                         temp = pDat->PortList[i+1].PrvWidthOutCap;
1364                 if (cbPCBBAUpstreamWidth < temp)
1365                         temp = cbPCBBAUpstreamWidth;
1366                 pDat->PortList[i].SelWidthIn = (u8)temp;
1367                 pDat->PortList[i+1].SelWidthOut = (u8)temp;
1368
1369         }
1370 }
1371
1372 /*----------------------------------------------------------------------------------------
1373  * void
1374  * hammerSublinkFixup(sMainData *pDat)
1375  *
1376  *  Description:
1377  *       Iterate through all links, checking the frequency of each sublink pair.  Make the
1378  *       adjustment to the port list data so that the frequencies are at a valid ratio,
1379  *       reducing frequency as needed to achieve this. (All links support the minimum 200 MHz
1380  *       frequency.)  Repeat the above until no adjustments are needed.
1381  *       Note no hardware state changes in this routine.
1382  *
1383  *  Parameters:
1384  *      @param[in,out] sMainData* pDat = our global state, link state and port list
1385  * ---------------------------------------------------------------------------------------
1386  */
1387 void hammerSublinkFixup(sMainData *pDat)
1388 {
1389 #ifndef HT_BUILD_NC_ONLY
1390         u8 i, j, k;
1391         BOOL changes, downgrade;
1392
1393         u8 hiIndex;
1394         u8 hiFreq, loFreq;
1395
1396         u32 temp;
1397
1398         do
1399         {
1400                 changes = FALSE;
1401                 for (i = 0; i < pDat->TotalLinks*2; i++)
1402                 {
1403                         if (pDat->PortList[i].Type != PORTLIST_TYPE_CPU) /*  Must be a CPU link */
1404                                 continue;
1405                         if (pDat->PortList[i].Link < 4) /*  Only look for for sublink1's */
1406                                 continue;
1407
1408                         for (j = 0; j < pDat->TotalLinks*2; j++)
1409                         {
1410                                 /*  Step 1. Find the matching sublink0 */
1411                                 if (pDat->PortList[j].Type != PORTLIST_TYPE_CPU)
1412                                         continue;
1413                                 if (pDat->PortList[j].NodeID != pDat->PortList[i].NodeID)
1414                                         continue;
1415                                 if (pDat->PortList[j].Link != (pDat->PortList[i].Link & 0x03))
1416                                         continue;
1417
1418                                 /*  Step 2. Check for an illegal frequency ratio */
1419                                 if (pDat->PortList[i].SelFrequency >= pDat->PortList[j].SelFrequency)
1420                                 {
1421                                         hiIndex = i;
1422                                         hiFreq = pDat->PortList[i].SelFrequency;
1423                                         loFreq = pDat->PortList[j].SelFrequency;
1424                                 }
1425                                 else
1426                                 {
1427                                         hiIndex = j;
1428                                         hiFreq = pDat->PortList[j].SelFrequency;
1429                                         loFreq = pDat->PortList[i].SelFrequency;
1430                                 }
1431
1432                                 if (hiFreq == loFreq)
1433                                         break; /*  The frequencies are 1:1, no need to do anything */
1434
1435                                 downgrade = FALSE;
1436
1437                                 if (hiFreq == 13)
1438                                 {
1439                                         if ((loFreq != 7) &&  /* {13, 7} 2400MHz / 1200MHz 2:1 */
1440                                                 (loFreq != 4) &&  /* {13, 4} 2400MHz /  600MHz 4:1 */
1441                                                 (loFreq != 2) )   /* {13, 2} 2400MHz /  400MHz 6:1 */
1442                                                 downgrade = TRUE;
1443                                 }
1444                                 else if (hiFreq == 11)
1445                                 {
1446                                         if ((loFreq != 6))    /* {11, 6} 2000MHz / 1000MHz 2:1 */
1447                                                 downgrade = TRUE;
1448                                 }
1449                                 else if (hiFreq == 9)
1450                                 {
1451                                         if ((loFreq != 5) &&  /* { 9, 5} 1600MHz /  800MHz 2:1 */
1452                                                 (loFreq != 2) &&  /* { 9, 2} 1600MHz /  400MHz 4:1 */
1453                                                 (loFreq != 0) )   /* { 9, 0} 1600MHz /  200Mhz 8:1 */
1454                                                 downgrade = TRUE;
1455                                 }
1456                                 else if (hiFreq == 7)
1457                                 {
1458                                         if ((loFreq != 4) &&  /* { 7, 4} 1200MHz /  600MHz 2:1 */
1459                                                 (loFreq != 0) )   /* { 7, 0} 1200MHz /  200MHz 6:1 */
1460                                                 downgrade = TRUE;
1461                                 }
1462                                 else if (hiFreq == 5)
1463                                 {
1464                                         if ((loFreq != 2) &&  /* { 5, 2}  800MHz /  400MHz 2:1 */
1465                                                 (loFreq != 0) )   /* { 5, 0}  800MHz /  200MHz 4:1 */
1466                                                 downgrade = TRUE;
1467                                 }
1468                                 else if (hiFreq == 2)
1469                                 {
1470                                         if ((loFreq != 0))    /* { 2, 0}  400MHz /  200MHz 2:1 */
1471                                                 downgrade = TRUE;
1472                                 }
1473                                 else
1474                                 {
1475                                         downgrade = TRUE; /*  no legal ratios for hiFreq */
1476                                 }
1477
1478                                 /*  Step 3. Downgrade the higher of the two frequencies, and set nochanges to FALSE */
1479                                 if (downgrade)
1480                                 {
1481                                         /*  Although the problem was with the port specified by hiIndex, we need to */
1482                                         /*  downgrade both ends of the link. */
1483                                         hiIndex = hiIndex & 0xFE; /*  Select the 'upstream' (i.e. even) port */
1484
1485                                         temp = pDat->PortList[hiIndex].CompositeFrequencyCap;
1486
1487                                         /*  Remove hiFreq from the list of valid frequencies */
1488                                         temp = temp & ~((uint32)1 << hiFreq);
1489                                         ASSERT (temp != 0);
1490                                         pDat->PortList[hiIndex].CompositeFrequencyCap = (uint16)temp;
1491                                         pDat->PortList[hiIndex+1].CompositeFrequencyCap = (uint16)temp;
1492
1493                                         for (k = 15; ; k--)
1494                                         {
1495                                                 if (temp & ((u32)1 << k))
1496                                                         break;
1497                                         }
1498
1499                                         pDat->PortList[hiIndex].SelFrequency = k;
1500                                         pDat->PortList[hiIndex+1].SelFrequency = k;
1501
1502                                         changes = TRUE;
1503                                 }
1504                         }
1505                 }
1506         } while (changes); /*  Repeat until a valid configuration is reached */
1507 #endif /* HT_BUILD_NC_ONLY */
1508 }
1509
1510 /*----------------------------------------------------------------------------------------
1511  * void
1512  * linkOptimization(sMainData *pDat)
1513  *
1514  *  Description:
1515  *       Based on link capabilities, apply optimization rules to come up with the real best
1516  *       settings, including several external limit decision from call backs. This includes
1517  *       handling of sublinks.  Finally, after the port list data is updated, set the hardware
1518  *       state for all links.
1519  *
1520  *  Parameters:
1521  *      @param[in]  sMainData* pDat = our global state
1522  * ---------------------------------------------------------------------------------------
1523  */
1524 void linkOptimization(sMainData *pDat)
1525 {
1526         pDat->nb->gatherLinkData(pDat, pDat->nb);
1527         regangLinks(pDat);
1528         selectOptimalWidthAndFrequency(pDat);
1529         hammerSublinkFixup(pDat);
1530         pDat->nb->setLinkData(pDat, pDat->nb);
1531 }
1532
1533
1534 /*----------------------------------------------------------------------------------------
1535  * void
1536  * trafficDistribution(sMainData *pDat)
1537  *
1538  *  Description:
1539  *       In the case of a two node system with both sublinks used, enable the traffic
1540  *       distribution feature.
1541  *
1542  *  Parameters:
1543  *        @param[in]        sMainData*    pDat           = our global state, port list data
1544  * ---------------------------------------------------------------------------------------
1545  */
1546 void trafficDistribution(sMainData *pDat)
1547 {
1548 #ifndef HT_BUILD_NC_ONLY
1549         u32 links01, links10;
1550         u8 linkCount;
1551         u8 i;
1552
1553         /*  Traffic Distribution is only used when there are exactly two nodes in the system */
1554         if (pDat->NodesDiscovered+1 != 2)
1555                 return;
1556
1557         links01 = 0;
1558         links10 = 0;
1559         linkCount = 0;
1560         for (i = 0; i < pDat->TotalLinks*2; i += 2)
1561         {
1562                 if ((pDat->PortList[i].Type == PORTLIST_TYPE_CPU) && (pDat->PortList[i+1].Type == PORTLIST_TYPE_CPU))
1563                 {
1564                         links01 |= (u32)1 << pDat->PortList[i].Link;
1565                         links10 |= (u32)1 << pDat->PortList[i+1].Link;
1566                         linkCount++;
1567                 }
1568         }
1569         ASSERT(linkCount != 0);
1570         if (linkCount == 1)
1571                 return; /*  Don't setup Traffic Distribution if only one link is being used */
1572
1573         pDat->nb->writeTrafficDistribution(links01, links10, pDat->nb);
1574 #endif /* HT_BUILD_NC_ONLY */
1575 }
1576
1577 /*----------------------------------------------------------------------------------------
1578  * void
1579  * tuning(sMainData *pDat)
1580  *
1581  *  Description:
1582  *       Handle system and performance tunings, such as traffic distribution, fifo and
1583  *       buffer tuning, and special config tunings.
1584  *
1585  *  Parameters:
1586  *      @param[in] sMainData* pDat = our global state, port list data
1587  * ---------------------------------------------------------------------------------------
1588  */
1589 void tuning(sMainData *pDat)
1590 {
1591         u8 i;
1592
1593         /* See if traffic distribution can be done and do it if so
1594          * or allow system specific customization
1595          */
1596         if ((pDat->HtBlock->AMD_CB_CustomizeTrafficDistribution == NULL)
1597                 || !pDat->HtBlock->AMD_CB_CustomizeTrafficDistribution())
1598         {
1599                 trafficDistribution(pDat);
1600         }
1601
1602         /* For each node, invoke northbridge specific buffer tunings or
1603          * system specific customizations.
1604          */
1605         for (i=0; i < pDat->NodesDiscovered + 1; i++)
1606         {
1607                 if ((pDat->HtBlock->AMD_CB_CustomizeBuffers == NULL)
1608                    || !pDat->HtBlock->AMD_CB_CustomizeBuffers(i))
1609                 {
1610                         pDat->nb->bufferOptimizations(i, pDat, pDat->nb);
1611                 }
1612         }
1613 }
1614
1615 /*----------------------------------------------------------------------------------------
1616  * BOOL
1617  * isSanityCheckOk()
1618  *
1619  *  Description:
1620  *       Perform any general sanity checks which should prevent HT from running if they fail.
1621  *       Currently only the "Must run on BSP only" check.
1622  *
1623  *  Parameters:
1624  *      @param[out] result BOOL  = true if check is ok, false if it failed
1625  * ---------------------------------------------------------------------------------------
1626  */
1627 BOOL isSanityCheckOk()
1628 {
1629         uint64 qValue;
1630
1631         AmdMSRRead(APIC_Base, &qValue);
1632
1633         return ((qValue.lo & ((u32)1 << APIC_Base_BSP)) != 0);
1634 }
1635
1636 /***************************************************************************
1637  ***                             HT Initialize                             ***
1638  ***************************************************************************/
1639
1640 /*----------------------------------------------------------------------------------------
1641  * void
1642  * htInitialize(AMD_HTBLOCK *pBlock)
1643  *
1644  *  Description:
1645  *       This is the top level external interface for Hypertransport Initialization.
1646  *       Create our initial internal state, initialize the coherent fabric,
1647  *       initialize the non-coherent chains, and perform any required fabric tuning or
1648  *       optimization.
1649  *
1650  *  Parameters:
1651  *      @param[in] AMD_HTBLOCK*  pBlock = Our Initial State including possible
1652  *                                topologies and routings, non coherent bus
1653  *                                assignment info, and actual
1654  *                                wrapper or OEM call back routines.
1655  * ---------------------------------------------------------------------------------------
1656  */
1657 void amdHtInitialize(AMD_HTBLOCK *pBlock)
1658 {
1659         sMainData pDat;
1660         cNorthBridge nb;
1661
1662         if (isSanityCheckOk())
1663         {
1664                 newNorthBridge(0, &nb);
1665
1666                 pDat.HtBlock = pBlock;
1667                 pDat.nb = &nb;
1668                 pDat.sysMpCap = nb.maxNodes;
1669                 nb.isCapable(0, &pDat, pDat.nb);
1670                 coherentInit(&pDat);
1671
1672                 pDat.AutoBusCurrent = pBlock->AutoBusStart;
1673                 pDat.UsedCfgMapEntires = 0;
1674                 ncInit(&pDat);
1675                 linkOptimization(&pDat);
1676                 tuning(&pDat);
1677         }
1678 }