blob: 6a45f10306d2d3b44a433306444a7ca2e95b883b [file] [log] [blame]
Marc Jones8ae8c882007-12-19 01:32:08 +00001/*
Stefan Reinauer7e61e452008-01-18 10:35:56 +00002 * This file is part of the coreboot project.
Marc Jones8ae8c882007-12-19 01:32:08 +00003 *
Timothy Pearsonc139c422015-01-23 20:27:26 -06004 * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
Marc Jones8ae8c882007-12-19 01:32:08 +00005 * Copyright (C) 2007 Advanced Micro Devices, Inc.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 2 of the License.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
Marc Jones8ae8c882007-12-19 01:32:08 +000015 */
16
17/*
18 *----------------------------------------------------------------------------
19 * MODULES USED
20 *
21 *----------------------------------------------------------------------------
22 */
23
24#undef FILECODE
25#define FILECODE 0xF001
26
27#include "comlib.h"
28#include "h3finit.h"
29#include "h3ffeat.h"
30#include "h3ncmn.h"
31#include "h3gtopo.h"
32#include "AsPsNb.h"
33/* this is pre-ram so include the required C files here */
34#include "comlib.c"
35#include "AsPsNb.c"
36#include "h3ncmn.c"
37
38/*----------------------------------------------------------------------------
39 * DEFINITIONS AND MACROS
40 *
41 *----------------------------------------------------------------------------
42 */
43
44#undef FILECODE
45#define FILECODE 0xF001
46
47/* APIC defines from amdgesa.inc, which can't be included in to c code. */
48#define APIC_Base_BSP 8
49#define APIC_Base 0x1b
50
Timothy Pearson7c55f372015-08-02 21:36:24 -050051#define NVRAM_LIMIT_HT_SPEED_200 0x12
52#define NVRAM_LIMIT_HT_SPEED_300 0x11
53#define NVRAM_LIMIT_HT_SPEED_400 0x10
54#define NVRAM_LIMIT_HT_SPEED_500 0xf
55#define NVRAM_LIMIT_HT_SPEED_600 0xe
56#define NVRAM_LIMIT_HT_SPEED_800 0xd
57#define NVRAM_LIMIT_HT_SPEED_1000 0xc
58#define NVRAM_LIMIT_HT_SPEED_1200 0xb
59#define NVRAM_LIMIT_HT_SPEED_1400 0xa
60#define NVRAM_LIMIT_HT_SPEED_1600 0x9
61#define NVRAM_LIMIT_HT_SPEED_1800 0x8
62#define NVRAM_LIMIT_HT_SPEED_2000 0x7
63#define NVRAM_LIMIT_HT_SPEED_2200 0x6
64#define NVRAM_LIMIT_HT_SPEED_2400 0x5
65#define NVRAM_LIMIT_HT_SPEED_2600 0x4
66#define NVRAM_LIMIT_HT_SPEED_2800 0x3
67#define NVRAM_LIMIT_HT_SPEED_3000 0x2
68#define NVRAM_LIMIT_HT_SPEED_3200 0x1
Timothy Pearson03259a82015-02-15 17:11:14 -060069#define NVRAM_LIMIT_HT_SPEED_AUTO 0x0
70
Timothy Pearson7c55f372015-08-02 21:36:24 -050071static const uint32_t ht_speed_limit[20] =
72 {0xFFFFF, 0xFFFFF, 0x7FFFF, 0x3FFFF,
73 0x0FFFF, 0x07FFF, 0x03FFF, 0x01FFF,
74 0x00FFF, 0x007FF, 0x003FF, 0x001FF,
75 0x000FF, 0x0007F, 0x0003F, 0x0001F,
76 0x0000F, 0x00007, 0x00003, 0x00001};
Timothy Pearson03259a82015-02-15 17:11:14 -060077
Timothy Pearson586d6e22015-02-16 14:57:06 -060078static const struct ht_speed_limit_map_t {
79 uint16_t mhz;
Timothy Pearsonb812d5d2015-03-14 19:59:54 -050080 uint8_t nvram;
Timothy Pearson586d6e22015-02-16 14:57:06 -060081} ht_speed_limit_map[] = {
82 {0, NVRAM_LIMIT_HT_SPEED_AUTO},
83 {200, NVRAM_LIMIT_HT_SPEED_200},
84 {300, NVRAM_LIMIT_HT_SPEED_300},
85 {400, NVRAM_LIMIT_HT_SPEED_400},
86 {500, NVRAM_LIMIT_HT_SPEED_500},
87 {600, NVRAM_LIMIT_HT_SPEED_600},
88 {800, NVRAM_LIMIT_HT_SPEED_800},
89 {1000, NVRAM_LIMIT_HT_SPEED_1000},
90 {1200, NVRAM_LIMIT_HT_SPEED_1200},
91 {1400, NVRAM_LIMIT_HT_SPEED_1400},
92 {1600, NVRAM_LIMIT_HT_SPEED_1600},
93 {1800, NVRAM_LIMIT_HT_SPEED_1800},
94 {2000, NVRAM_LIMIT_HT_SPEED_2000},
95 {2200, NVRAM_LIMIT_HT_SPEED_2200},
96 {2400, NVRAM_LIMIT_HT_SPEED_2400},
97 {2600, NVRAM_LIMIT_HT_SPEED_2600},
Timothy Pearson7c55f372015-08-02 21:36:24 -050098 {2800, NVRAM_LIMIT_HT_SPEED_2800},
99 {3000, NVRAM_LIMIT_HT_SPEED_3000},
100 {3200, NVRAM_LIMIT_HT_SPEED_3200},
Timothy Pearson586d6e22015-02-16 14:57:06 -0600101};
102
Timothy Pearson7c55f372015-08-02 21:36:24 -0500103static const uint32_t ht_speed_mhz_to_hw(uint16_t mhz)
Timothy Pearson586d6e22015-02-16 14:57:06 -0600104{
105 size_t i;
106 for (i = 0; i < ARRAY_SIZE(ht_speed_limit_map); i++)
107 if (ht_speed_limit_map[i].mhz == mhz)
Timothy Pearsonb812d5d2015-03-14 19:59:54 -0500108 return ht_speed_limit[ht_speed_limit_map[i].nvram];
Timothy Pearson586d6e22015-02-16 14:57:06 -0600109
110 printk(BIOS_WARNING,
111 "WARNING: Invalid HT link limit frequency %d specified, ignoring...\n",
112 mhz);
113 return ht_speed_limit[NVRAM_LIMIT_HT_SPEED_AUTO];
114}
115
Marc Jones8ae8c882007-12-19 01:32:08 +0000116/*----------------------------------------------------------------------------
117 * TYPEDEFS AND STRUCTURES
118 *
119 *----------------------------------------------------------------------------
120 */
121
122/*----------------------------------------------------------------------------
123 * PROTOTYPES OF LOCAL FUNCTIONS
124 *
125 *----------------------------------------------------------------------------
126 */
127
128/*----------------------------------------------------------------------------
129 * EXPORTED FUNCTIONS
130 *
131 *----------------------------------------------------------------------------
132 */
133
134/*----------------------------------------------------------------------------
135 * LOCAL FUNCTIONS
136 *
137 *----------------------------------------------------------------------------
138 */
139#ifndef HT_BUILD_NC_ONLY
140/*
141 **************************************************************************
142 * Routing table decompressor
143 **************************************************************************
144 */
145
146/*
147 **************************************************************************
148 * Graph Support routines
149 * These routines provide support for dealing with the graph representation
150 * of the topologies, along with the routing table information for that topology.
151 * The routing information is compressed and these routines currently decompress
152 * 'on the fly'. A graph is represented as a set of routes. All the edges in the
153 * graph are routes; a direct route from node i to node j exists in the graph IFF
154 * there is an edge directly connecting node i to node j. All other routes designate
155 * the edge which the route to that node initially takes, by designating a node
156 * to which a direct connection exists. That is, the route to non-adjacent node j
157 * from node i specifies node k where node i directly connects to node k.
158 *
159 *
160 * pseudo definition of compressed graph:
161 * typedef struct
162 * {
163 * BIT broadcast[8];
164 * uint4 responseRoute;
165 * uint4 requestRoute;
166 * } sRoute;
167 * typedef struct
168 * {
169 * u8 size;
170 * sRoute graph[size][size];
171 * } sGraph;
172 *
173 **************************************************************************
174 */
175
176/*----------------------------------------------------------------------------------------
Marc Jones212486e2008-07-17 19:50:37 +0000177 * u8
Marc Jones8ae8c882007-12-19 01:32:08 +0000178 * graphHowManyNodes(u8 *graph)
179 *
180 * Description:
181 * Returns the number of nodes in the compressed graph
182 *
183 * Parameters:
Martin Rothf4cb4122015-01-06 10:27:39 -0700184 * @param[in] graph = a compressed graph
185 * @param[out] results = the number of nodes in the graph
Marc Jones8ae8c882007-12-19 01:32:08 +0000186 * ---------------------------------------------------------------------------------------
187 */
Myles Watson075fbe82010-04-15 05:19:29 +0000188static u8 graphHowManyNodes(u8 *graph)
Marc Jones8ae8c882007-12-19 01:32:08 +0000189{
190 return graph[0];
191}
192
193/*----------------------------------------------------------------------------------------
194 * BOOL
195 * graphIsAdjacent(u8 *graph, u8 nodeA, u8 nodeB)
196 *
197 * Description:
198 * Returns true if NodeA is directly connected to NodeB, false otherwise
199 * (if NodeA == NodeB also returns false)
200 * Relies on rule that directly connected nodes always route requests directly.
201 *
202 * Parameters:
Martin Rothf4cb4122015-01-06 10:27:39 -0700203 * @param[in] graph = the graph to examine
204 * @param[in] nodeA = the node number of the first node
205 * @param[in] nodeB = the node number of the second node
206 * @param[out] results = true if nodeA connects to nodeB false if not
Marc Jones8ae8c882007-12-19 01:32:08 +0000207 * ---------------------------------------------------------------------------------------
208 */
Myles Watson075fbe82010-04-15 05:19:29 +0000209static BOOL graphIsAdjacent(u8 *graph, u8 nodeA, u8 nodeB)
Marc Jones8ae8c882007-12-19 01:32:08 +0000210{
211 u8 size = graph[0];
212 ASSERT(size <= MAX_NODES);
213 ASSERT((nodeA < size) && (nodeB < size));
214 return (graph[1+(nodeA*size+nodeB)*2+1] & 0x0F) == nodeB;
215}
216
217/*----------------------------------------------------------------------------------------
218 * u8
219 * graphGetRsp(u8 *graph, u8 nodeA, u8 nodeB)
220 *
221 * Description:
222 * Returns the graph node used by nodeA to route responses targeted at nodeB.
223 * This will be a node directly connected to nodeA (possibly nodeB itself),
224 * or "Route to Self" if nodeA and nodeB are the same node.
225 * Note that all node numbers are abstract node numbers of the topology graph,
226 * it is the responsibility of the caller to apply any permutation needed.
227 *
228 * Parameters:
229 * @param[in] u8 graph = the graph to examine
230 * @param[in] u8 nodeA = the node number of the first node
231 * @param[in] u8 nodeB = the node number of the second node
232 * @param[out] u8 results = The response route node
233 * ---------------------------------------------------------------------------------------
234 */
Myles Watson075fbe82010-04-15 05:19:29 +0000235static u8 graphGetRsp(u8 *graph, u8 nodeA, u8 nodeB)
Marc Jones8ae8c882007-12-19 01:32:08 +0000236{
237 u8 size = graph[0];
238 ASSERT(size <= MAX_NODES);
239 ASSERT((nodeA < size) && (nodeB < size));
240 return (graph[1+(nodeA*size+nodeB)*2+1] & 0xF0)>>4;
241}
242
243/*----------------------------------------------------------------------------------------
244 * u8
245 * graphGetReq(u8 *graph, u8 nodeA, u8 nodeB)
246 *
247 * Description:
248 * Returns the graph node used by nodeA to route requests targeted at nodeB.
249 * This will be a node directly connected to nodeA (possibly nodeB itself),
250 * or "Route to Self" if nodeA and nodeB are the same node.
251 * Note that all node numbers are abstract node numbers of the topology graph,
252 * it is the responsibility of the caller to apply any permutation needed.
253 *
254 * Parameters:
Martin Rothf4cb4122015-01-06 10:27:39 -0700255 * @param[in] graph = the graph to examine
256 * @param[in] nodeA = the node number of the first node
257 * @param[in] nodeB = the node number of the second node
258 * @param[out] results = The request route node
Marc Jones8ae8c882007-12-19 01:32:08 +0000259 * ---------------------------------------------------------------------------------------
260 */
Myles Watson075fbe82010-04-15 05:19:29 +0000261static u8 graphGetReq(u8 *graph, u8 nodeA, u8 nodeB)
Marc Jones8ae8c882007-12-19 01:32:08 +0000262{
Marc Jones212486e2008-07-17 19:50:37 +0000263 u8 size = graph[0];
Marc Jones8ae8c882007-12-19 01:32:08 +0000264 ASSERT(size <= MAX_NODES);
265 ASSERT((nodeA < size) && (nodeB < size));
266 return (graph[1+(nodeA*size+nodeB)*2+1] & 0x0F);
267}
268
269/*----------------------------------------------------------------------------------------
270 * u8
Marc Jones212486e2008-07-17 19:50:37 +0000271 * graphGetBc(u8 *graph, u8 nodeA, u8 nodeB)
Marc Jones8ae8c882007-12-19 01:32:08 +0000272 *
273 * Description:
274 * Returns a bit vector of nodes that nodeA should forward a broadcast from
275 * nodeB towards
276 *
277 * Parameters:
Martin Rothf4cb4122015-01-06 10:27:39 -0700278 * @param[in] graph = the graph to examine
279 * @param[in] nodeA = the node number of the first node
280 * @param[in] nodeB = the node number of the second node
281 * OU results = the broadcast routes for nodeA from nodeB
Marc Jones8ae8c882007-12-19 01:32:08 +0000282 * ---------------------------------------------------------------------------------------
283 */
Myles Watson075fbe82010-04-15 05:19:29 +0000284static u8 graphGetBc(u8 *graph, u8 nodeA, u8 nodeB)
Marc Jones8ae8c882007-12-19 01:32:08 +0000285{
Marc Jones212486e2008-07-17 19:50:37 +0000286 u8 size = graph[0];
Marc Jones8ae8c882007-12-19 01:32:08 +0000287 ASSERT(size <= MAX_NODES);
288 ASSERT((nodeA < size) && (nodeB < size));
289 return graph[1+(nodeA*size+nodeB)*2];
290}
291
292
293/***************************************************************************
294 *** GENERIC HYPERTRANSPORT DISCOVERY CODE ***
295 ***************************************************************************/
296
297/*----------------------------------------------------------------------------------------
298 * void
299 * routeFromBSP(u8 targetNode, u8 actualTarget, sMainData *pDat)
300 *
301 * Description:
302 * Ensure a request / response route from target node to bsp. Since target node is
303 * always a predecessor of actual target node, each node gets a route to actual target
304 * on the link that goes to target. The routing produced by this routine is adequate
305 * for config access during discovery, but NOT for coherency.
306 *
307 * Parameters:
308 * @param[in] u8 targetNode = the path to actual target goes through target
309 * @param[in] u8 actualTarget = the ultimate target being routed to
310 * @param[in] sMainData* pDat = our global state, port config info
311 * ---------------------------------------------------------------------------------------
312 */
Myles Watson075fbe82010-04-15 05:19:29 +0000313static void routeFromBSP(u8 targetNode, u8 actualTarget, sMainData *pDat)
Marc Jones8ae8c882007-12-19 01:32:08 +0000314{
315 u8 predecessorNode, predecessorLink, currentPair;
316
317 if (targetNode == 0)
Marc Jonesaee07962008-07-16 21:09:31 +0000318 return; /* BSP has no predecessor, stop */
Marc Jones8ae8c882007-12-19 01:32:08 +0000319
Marc Jonesaee07962008-07-16 21:09:31 +0000320 /* Search for the link that connects targetNode to its predecessor */
Marc Jones8ae8c882007-12-19 01:32:08 +0000321 currentPair = 0;
322 while (pDat->PortList[currentPair*2+1].NodeID != targetNode)
323 {
324 currentPair++;
325 ASSERT(currentPair < pDat->TotalLinks);
326 }
327
328 predecessorNode = pDat->PortList[currentPair*2].NodeID;
329 predecessorLink = pDat->PortList[currentPair*2].Link;
330
Marc Jonesaee07962008-07-16 21:09:31 +0000331 /* Recursively call self to ensure the route from the BSP to the Predecessor */
332 /* Node is established */
Marc Jones8ae8c882007-12-19 01:32:08 +0000333 routeFromBSP(predecessorNode, actualTarget, pDat);
334
335 pDat->nb->writeRoutingTable(predecessorNode, actualTarget, predecessorLink, pDat->nb);
336}
337
Martin Rothf4cb4122015-01-06 10:27:39 -0700338/*---------------------------------------------------------------------------*/
339
340/**
341 * u8
Marc Jones8ae8c882007-12-19 01:32:08 +0000342 * convertNodeToLink(u8 srcNode, u8 targetNode, sMainData *pDat)
343 *
344 * Description:
345 * Return the link on source node which connects to target node
346 *
347 * Parameters:
Martin Rothf4cb4122015-01-06 10:27:39 -0700348 * @param[in] srcNode = the source node
349 * @param[in] targetNode = the target node to find the link to
350 * @param[in] pDat = our global state
351 * @return the link on source which connects to target
352 *
Marc Jones8ae8c882007-12-19 01:32:08 +0000353 */
Myles Watson075fbe82010-04-15 05:19:29 +0000354static u8 convertNodeToLink(u8 srcNode, u8 targetNode, sMainData *pDat)
Marc Jones8ae8c882007-12-19 01:32:08 +0000355{
356 u8 targetlink = INVALID_LINK;
357 u8 k;
358
359 for (k = 0; k < pDat->TotalLinks*2; k += 2)
360 {
361 if ((pDat->PortList[k+0].NodeID == srcNode) && (pDat->PortList[k+1].NodeID == targetNode))
362 {
363 targetlink = pDat->PortList[k+0].Link;
364 break;
365 }
366 else if ((pDat->PortList[k+1].NodeID == srcNode) && (pDat->PortList[k+0].NodeID == targetNode))
367 {
368 targetlink = pDat->PortList[k+1].Link;
369 break;
370 }
371 }
372 ASSERT(targetlink != INVALID_LINK);
373
374 return targetlink;
375}
376
377
378/*----------------------------------------------------------------------------------------
379 * void
380 * htDiscoveryFloodFill(sMainData *pDat)
381 *
382 * Description:
383 * Discover all coherent devices in the system, initializing some basics like node IDs
384 * and total nodes found in the process. As we go we also build a representation of the
385 * discovered system which we will use later to program the routing tables. During this
386 * step, the routing is via default link back to BSP and to each new node on the link it
387 * was discovered on (no coherency is active yet).
388 *
389 * Parameters:
390 * @param[in] sMainData* pDat = our global state
391 * ---------------------------------------------------------------------------------------
392 */
Myles Watson075fbe82010-04-15 05:19:29 +0000393static void htDiscoveryFloodFill(sMainData *pDat)
Marc Jones8ae8c882007-12-19 01:32:08 +0000394{
Timothy Pearson0122afb2015-07-30 14:07:15 -0500395 uint8_t currentNode = 0;
396 uint8_t currentLink;
397 uint8_t currentLinkID;
398
399 /* NOTE
400 * Each node inside a dual node (socket G34) processor must share
401 * an adjacent node ID. Alter the link scan order such that the
402 * other internal node is always scanned first...
403 */
404 uint8_t currentLinkScanOrder_Default[8] = {0, 1, 2, 3, 4, 5, 6, 7};
405 uint8_t currentLinkScanOrder_G34_Fam10[8] = {1, 0, 2, 3, 4, 5, 6, 7};
406 uint8_t currentLinkScanOrder_G34_Fam15[8] = {2, 0, 1, 3, 4, 5, 6, 7};
407
408 uint8_t fam15h = 0;
409 uint8_t rev_gte_d = 0;
410 uint8_t dual_node = 0;
411 uint32_t f3xe8;
412 uint32_t family;
413 uint32_t model;
414
415 f3xe8 = pci_read_config32(NODE_PCI(0, 3), 0xe8);
416
417 family = model = cpuid_eax(0x80000001);
418 model = ((model & 0xf0000) >> 12) | ((model & 0xf0) >> 4);
419 family = ((family & 0xf00000) >> 16) | ((family & 0xf00) >> 8);
420
421 if (family >= 0x6f) {
422 /* Family 15h or later */
423 fam15h = 1;
424 }
425
426 if ((model >= 0x8) || fam15h)
427 /* Revision D or later */
428 rev_gte_d = 1;
429
430 if (rev_gte_d)
431 /* Check for dual node capability */
432 if (f3xe8 & 0x20000000)
433 dual_node = 1;
Marc Jones8ae8c882007-12-19 01:32:08 +0000434
435 /* Entries are always added in pairs, the even indices are the 'source'
436 * side closest to the BSP, the odd indices are the 'destination' side
437 */
Marc Jones8ae8c882007-12-19 01:32:08 +0000438 while (currentNode <= pDat->NodesDiscovered)
439 {
440 u32 temp;
441
442 if (currentNode != 0)
443 {
444 /* Set path from BSP to currentNode */
445 routeFromBSP(currentNode, currentNode, pDat);
446
447 /* Set path from BSP to currentNode for currentNode+1 if
448 * currentNode+1 != MAX_NODES
449 */
450 if (currentNode+1 != MAX_NODES)
451 routeFromBSP(currentNode, currentNode+1, pDat);
452
453 /* Configure currentNode to route traffic to the BSP through its
454 * default link
455 */
456 pDat->nb->writeRoutingTable(currentNode, 0, pDat->nb->readDefLnk(currentNode, pDat->nb), pDat->nb);
457 }
458
459 /* Set currentNode's NodeID field to currentNode */
460 pDat->nb->writeNodeID(currentNode, currentNode, pDat->nb);
461
Timothy Pearson7c55f372015-08-02 21:36:24 -0500462 /* Enable routing tables on currentNode */
Marc Jones8ae8c882007-12-19 01:32:08 +0000463 pDat->nb->enableRoutingTables(currentNode, pDat->nb);
464
Timothy Pearson0122afb2015-07-30 14:07:15 -0500465 for (currentLinkID = 0; currentLinkID < pDat->nb->maxLinks; currentLinkID++)
Marc Jones8ae8c882007-12-19 01:32:08 +0000466 {
467 BOOL linkfound;
468 u8 token;
469
Timothy Pearson0122afb2015-07-30 14:07:15 -0500470 if (currentLinkID < 8) {
471 if (dual_node) {
472 if (fam15h)
473 currentLink = currentLinkScanOrder_G34_Fam15[currentLinkID];
474 else
475 currentLink = currentLinkScanOrder_G34_Fam10[currentLinkID];
476 } else {
477 currentLink = currentLinkScanOrder_Default[currentLinkID];
478 }
479 } else {
480 currentLink = currentLinkID;
481 }
482
Marc Jones8ae8c882007-12-19 01:32:08 +0000483 if (pDat->HtBlock->AMD_CB_IgnoreLink && pDat->HtBlock->AMD_CB_IgnoreLink(currentNode, currentLink))
484 continue;
485
486 if (pDat->nb->readTrueLinkFailStatus(currentNode, currentLink, pDat, pDat->nb))
487 continue;
488
489 /* Make sure that the link is connected, coherent, and ready */
490 if (!pDat->nb->verifyLinkIsCoherent(currentNode, currentLink, pDat->nb))
491 continue;
492
493
494 /* Test to see if the currentLink has already been explored */
495 linkfound = FALSE;
496 for (temp = 0; temp < pDat->TotalLinks; temp++)
497 {
498 if ((pDat->PortList[temp*2+1].NodeID == currentNode) &&
499 (pDat->PortList[temp*2+1].Link == currentLink))
500 {
501 linkfound = TRUE;
502 break;
503 }
504 }
505 if (linkfound)
506 {
507 /* We had already expored this link */
508 continue;
509 }
510
511 if (pDat->nb->handleSpecialLinkCase(currentNode, currentLink, pDat, pDat->nb))
512 {
513 continue;
514 }
515
516 /* Modify currentNode's routing table to use currentLink to send
517 * traffic to currentNode+1
518 */
519 pDat->nb->writeRoutingTable(currentNode, currentNode+1, currentLink, pDat->nb);
520
521 /* Check the northbridge of the node we just found, to make sure it is compatible
522 * before doing anything else to it.
523 */
524 if (!pDat->nb->isCompatible(currentNode+1, pDat->nb))
525 {
526 u8 nodeToKill;
527
528 /* Notify BIOS of event (while variables are still the same) */
529 if (pDat->HtBlock->AMD_CB_EventNotify)
530 {
Marc Jones212486e2008-07-17 19:50:37 +0000531 sHtEventCohFamilyFeud evt;
532 evt.eSize = sizeof(sHtEventCohFamilyFeud);
533 evt.node = currentNode;
534 evt.link = currentLink;
535 evt.totalNodes = pDat->NodesDiscovered;
Marc Jones8ae8c882007-12-19 01:32:08 +0000536
537 pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,
538 HT_EVENT_COH_FAMILY_FEUD,
539 (u8 *)&evt);
540 }
541
542 /* If node is not compatible, force boot to 1P
543 * If they are not compatible stop cHT init and:
544 * 1. Disable all cHT links on the BSP
545 * 2. Configure the BSP routing tables as a UP.
546 * 3. Notify main BIOS.
547 */
548 pDat->NodesDiscovered = 0;
549 currentNode = 0;
550 pDat->TotalLinks = 0;
551 /* Abandon our coherent link data structure. At this point there may
552 * be coherent links on the BSP that are not yet in the portList, and
553 * we have to turn them off anyway. So depend on the hardware to tell us.
554 */
555 for (currentLink = 0; currentLink < pDat->nb->maxLinks; currentLink++)
556 {
557 /* Stop all links which are connected, coherent, and ready */
558 if (pDat->nb->verifyLinkIsCoherent(currentNode, currentLink, pDat->nb))
559 pDat->nb->stopLink(currentNode, currentLink, pDat->nb);
560 }
561
562 for (nodeToKill = 0; nodeToKill < pDat->nb->maxNodes; nodeToKill++)
563 {
564 pDat->nb->writeFullRoutingTable(0, nodeToKill, ROUTETOSELF, ROUTETOSELF, 0, pDat->nb);
565 }
566
567 /* End Coherent Discovery */
568 STOP_HERE;
569 break;
570 }
571
572 /* Read token from Current+1 */
573 token = pDat->nb->readToken(currentNode+1, pDat->nb);
574 ASSERT(token <= pDat->NodesDiscovered);
575 if (token == 0)
576 {
577 pDat->NodesDiscovered++;
578 ASSERT(pDat->NodesDiscovered < pDat->nb->maxNodes);
579 /* Check the capability of northbridges against the currently known configuration */
580 if (!pDat->nb->isCapable(currentNode+1, pDat, pDat->nb))
581 {
582 u8 nodeToKill;
583
584 /* Notify BIOS of event */
585 if (pDat->HtBlock->AMD_CB_EventNotify)
586 {
Marc Jones212486e2008-07-17 19:50:37 +0000587 sHtEventCohMpCapMismatch evt;
588 evt.eSize = sizeof(sHtEventCohMpCapMismatch);
589 evt.node = currentNode;
590 evt.link = currentLink;
591 evt.sysMpCap = pDat->sysMpCap;
592 evt.totalNodes = pDat->NodesDiscovered;
Marc Jones8ae8c882007-12-19 01:32:08 +0000593
594 pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,
595 HT_EVENT_COH_MPCAP_MISMATCH,
596 (u8 *)&evt);
597 }
598
599 pDat->NodesDiscovered = 0;
600 currentNode = 0;
601 pDat->TotalLinks = 0;
602
603 for (nodeToKill = 0; nodeToKill < pDat->nb->maxNodes; nodeToKill++)
604 {
605 pDat->nb->writeFullRoutingTable(0, nodeToKill, ROUTETOSELF, ROUTETOSELF, 0, pDat->nb);
606 }
607
608 /* End Coherent Discovery */
609 STOP_HERE;
610 break;
611 }
612
613 token = pDat->NodesDiscovered;
614 pDat->nb->writeToken(currentNode+1, token, pDat->nb);
615 /* Inform that we have discovered a node, so that logical id to
616 * socket mapping info can be recorded.
617 */
618 if (pDat->HtBlock->AMD_CB_EventNotify)
619 {
Marc Jones212486e2008-07-17 19:50:37 +0000620 sHtEventCohNodeDiscovered evt;
621 evt.eSize = sizeof(sHtEventCohNodeDiscovered);
622 evt.node = currentNode;
623 evt.link = currentLink;
624 evt.newNode = token;
Marc Jones8ae8c882007-12-19 01:32:08 +0000625
626 pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_INFO,
627 HT_EVENT_COH_NODE_DISCOVERED,
628 (u8 *)&evt);
629 }
630 }
631
632 if (pDat->TotalLinks == MAX_PLATFORM_LINKS)
633 {
634 /*
635 * Exceeded our capacity to describe all coherent links found in the system.
636 * Error strategy:
637 * Auto recovery is not possible because data space is already all used.
638 * If the callback is not implemented or returns we will continue to initialize
639 * the fabric we are capable of representing, adding no more nodes or links.
640 * This should yield a bootable topology, but likely not the one intended.
641 * We cannot continue discovery, there may not be any way to route a new
642 * node back to the BSP if we can't add links to our representation of the system.
643 */
644 if (pDat->HtBlock->AMD_CB_EventNotify)
645 {
Marc Jones212486e2008-07-17 19:50:37 +0000646 sHtEventCohLinkExceed evt;
647 evt.eSize = sizeof(sHtEventCohLinkExceed);
648 evt.node = currentNode;
649 evt.link = currentLink;
650 evt.targetNode = token;
651 evt.totalNodes = pDat->NodesDiscovered;
652 evt.maxLinks = pDat->nb->maxLinks;
Marc Jones8ae8c882007-12-19 01:32:08 +0000653
654 pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,
655 HT_EVENT_COH_LINK_EXCEED,
656 (u8 *)&evt);
657 }
658 /* Force link and node loops to halt */
659 STOP_HERE;
660 currentNode = pDat->NodesDiscovered;
661 break;
662 }
663
664 pDat->PortList[pDat->TotalLinks*2].Type = PORTLIST_TYPE_CPU;
665 pDat->PortList[pDat->TotalLinks*2].Link = currentLink;
666 pDat->PortList[pDat->TotalLinks*2].NodeID = currentNode;
667
668 pDat->PortList[pDat->TotalLinks*2+1].Type = PORTLIST_TYPE_CPU;
669 pDat->PortList[pDat->TotalLinks*2+1].Link = pDat->nb->readDefLnk(currentNode+1, pDat->nb);
670 pDat->PortList[pDat->TotalLinks*2+1].NodeID = token;
671
672 pDat->TotalLinks++;
673
674 if ( !pDat->sysMatrix[currentNode][token] )
675 {
676 pDat->sysDegree[currentNode]++;
677 pDat->sysDegree[token]++;
678 pDat->sysMatrix[currentNode][token] = TRUE;
679 pDat->sysMatrix[token][currentNode] = TRUE;
680 }
681 }
682 currentNode++;
683 }
684}
685
686
687/***************************************************************************
688 *** ISOMORPHISM BASED ROUTING TABLE GENERATION CODE ***
689 ***************************************************************************/
690
691/*----------------------------------------------------------------------------------------
692 * BOOL
693 * isoMorph(u8 i, sMainData *pDat)
694 *
695 * Description:
696 * Is graphA isomorphic to graphB?
697 * if this function returns true, then Perm will contain the permutation
698 * required to transform graphB into graphA.
699 * We also use the degree of each node, that is the number of connections it has, to
700 * speed up rejection of non-isomorphic graphs (if there is a node in graphA with n
701 * connections, there must be at least one unmatched in graphB with n connections).
702 *
703 * Parameters:
704 * @param[in] u8 i = the discovered node which we are trying to match
705 * with a permutation the topology
706 * @param[in]/@param[out] sMainData* pDat = our global state, degree and adjacency matrix,
707 * output a permutation if successful
708 * @param[out] BOOL results = the graphs are (or are not) isomorphic
709 * ---------------------------------------------------------------------------------------
710 */
Myles Watson075fbe82010-04-15 05:19:29 +0000711static BOOL isoMorph(u8 i, sMainData *pDat)
Marc Jones8ae8c882007-12-19 01:32:08 +0000712{
713 u8 j, k;
714 u8 nodecnt;
715
716 /* We have only been called if nodecnt == pSelected->size ! */
717 nodecnt = pDat->NodesDiscovered+1;
718
719 if (i != nodecnt)
720 {
Marc Jonesaee07962008-07-16 21:09:31 +0000721 /* Keep building the permutation */
Marc Jones8ae8c882007-12-19 01:32:08 +0000722 for (j = 0; j < nodecnt; j++)
723 {
Marc Jonesaee07962008-07-16 21:09:31 +0000724 /* Make sure the degree matches */
Marc Jones8ae8c882007-12-19 01:32:08 +0000725 if (pDat->sysDegree[i] != pDat->dbDegree[j])
726 continue;
727
Marc Jonesaee07962008-07-16 21:09:31 +0000728 /* Make sure that j hasn't been used yet (ought to use a "used" */
729 /* array instead, might be faster) */
Marc Jones8ae8c882007-12-19 01:32:08 +0000730 for (k = 0; k < i; k++)
731 {
732 if (pDat->Perm[k] == j)
733 break;
734 }
735 if (k != i)
736 continue;
737 pDat->Perm[i] = j;
738 if (isoMorph(i+1, pDat))
739 return TRUE;
740 }
741 return FALSE;
742 } else {
Marc Jonesaee07962008-07-16 21:09:31 +0000743 /* Test to see if the permutation is isomorphic */
Marc Jones8ae8c882007-12-19 01:32:08 +0000744 for (j = 0; j < nodecnt; j++)
745 {
746 for (k = 0; k < nodecnt; k++)
747 {
748 if ( pDat->sysMatrix[j][k] !=
749 pDat->dbMatrix[pDat->Perm[j]][pDat->Perm[k]] )
750 return FALSE;
751 }
752 }
753 return TRUE;
754 }
755}
756
757
758/*----------------------------------------------------------------------------------------
759 * void
760 * lookupComputeAndLoadRoutingTables(sMainData *pDat)
761 *
762 * Description:
763 * Using the description of the fabric topology we discovered, try to find a match
764 * among the supported topologies. A supported topology description matches
765 * the discovered fabric if the nodes can be matched in such a way that all the nodes connected
766 * in one set are exactly the nodes connected in the other (formally, that the graphs are
767 * isomorphic). Which links are used is not really important to matching. If the graphs
768 * match, then there is a permutation of one that translates the node positions and linkages
769 * to the other.
770 *
771 * In order to make the isomorphism test efficient, we test for matched number of nodes
772 * (a 4 node fabric is not isomorphic to a 2 node topology), and provide degrees of nodes
773 * to the isomorphism test.
774 *
775 * The generic routing table solution for any topology is predetermined and represented
776 * as part of the topology. The permutation we computed tells us how to interpret the
777 * routing onto the fabric we discovered. We do this working backward from the last
778 * node discovered to the BSP, writing the routing tables as we go.
779 *
780 * Parameters:
781 * @param[in] sMainData* pDat = our global state, the discovered fabric,
782 * @param[out] degree matrix, permutation
783 * ---------------------------------------------------------------------------------------
784 */
Myles Watson075fbe82010-04-15 05:19:29 +0000785static void lookupComputeAndLoadRoutingTables(sMainData *pDat)
Marc Jones8ae8c882007-12-19 01:32:08 +0000786{
787 u8 **pTopologyList;
788 u8 *pSelected;
789
790 int i, j, k, size;
791
792 size = pDat->NodesDiscovered + 1;
793 /* Use the provided topology list or the internal, default one. */
794 pTopologyList = pDat->HtBlock->topolist;
795 if (pTopologyList == NULL)
796 {
797 getAmdTopolist(&pTopologyList);
798 }
799
800 pSelected = *pTopologyList;
801 while (pSelected != NULL)
802 {
803 if (graphHowManyNodes(pSelected) == size)
804 {
Marc Jonesaee07962008-07-16 21:09:31 +0000805 /* Build Degree vector and Adjency Matrix for this entry */
Marc Jones8ae8c882007-12-19 01:32:08 +0000806 for (i = 0; i < size; i++)
807 {
808 pDat->dbDegree[i] = 0;
809 for (j = 0; j < size; j++)
810 {
811 if (graphIsAdjacent(pSelected, i, j))
812 {
813 pDat->dbMatrix[i][j] = 1;
814 pDat->dbDegree[i]++;
815 }
816 else
817 {
818 pDat->dbMatrix[i][j] = 0;
819 }
820 }
821 }
822 if (isoMorph(0, pDat))
Marc Jonesaee07962008-07-16 21:09:31 +0000823 break; /* A matching topology was found */
Marc Jones8ae8c882007-12-19 01:32:08 +0000824 }
825
826 pTopologyList++;
827 pSelected = *pTopologyList;
828 }
829
830 if (pSelected != NULL)
831 {
Marc Jonesaee07962008-07-16 21:09:31 +0000832 /* Compute the reverse Permutation */
Marc Jones8ae8c882007-12-19 01:32:08 +0000833 for (i = 0; i < size; i++)
834 {
835 pDat->ReversePerm[pDat->Perm[i]] = i;
836 }
837
Marc Jonesaee07962008-07-16 21:09:31 +0000838 /* Start with the last discovered node, and move towards the BSP */
Marc Jones8ae8c882007-12-19 01:32:08 +0000839 for (i = size-1; i >= 0; i--)
840 {
841 for (j = 0; j < size; j++)
842 {
843 u8 ReqTargetLink, RspTargetLink;
844 u8 ReqTargetNode, RspTargetNode;
845
846 u8 AbstractBcTargetNodes = graphGetBc(pSelected, pDat->Perm[i], pDat->Perm[j]);
847 u32 BcTargetLinks = 0;
848
849 for (k = 0; k < MAX_NODES; k++)
850 {
851 if (AbstractBcTargetNodes & ((u32)1<<k))
852 {
853 BcTargetLinks |= (u32)1 << convertNodeToLink(i, pDat->ReversePerm[k], pDat);
854 }
855 }
856
857 if (i == j)
858 {
859 ReqTargetLink = ROUTETOSELF;
860 RspTargetLink = ROUTETOSELF;
861 }
862 else
863 {
864 ReqTargetNode = graphGetReq(pSelected, pDat->Perm[i], pDat->Perm[j]);
865 ReqTargetLink = convertNodeToLink(i, pDat->ReversePerm[ReqTargetNode], pDat);
866
867 RspTargetNode = graphGetRsp(pSelected, pDat->Perm[i], pDat->Perm[j]);
868 RspTargetLink = convertNodeToLink(i, pDat->ReversePerm[RspTargetNode], pDat);
869 }
870
871 pDat->nb->writeFullRoutingTable(i, j, ReqTargetLink, RspTargetLink, BcTargetLinks, pDat->nb);
872 }
873 /* Clean up discovery 'footprint' that otherwise remains in the routing table. It didn't hurt
874 * anything, but might cause confusion during debug and validation. Do this by setting the
875 * route back to all self routes. Since it's the node that would be one more than actually installed,
876 * this only applies if less than maxNodes were found.
877 */
878 if (size < pDat->nb->maxNodes)
879 {
880 pDat->nb->writeFullRoutingTable(i, size, ROUTETOSELF, ROUTETOSELF, 0, pDat->nb);
881 }
882 }
883
884 }
885 else
886 {
887 /*
888 * No Matching Topology was found
889 * Error Strategy:
890 * Auto recovery doesn't seem likely, Force boot as 1P.
891 * For reporting, logging, provide number of nodes
892 * If not implemented or returns, boot as BSP uniprocessor.
893 */
894 if (pDat->HtBlock->AMD_CB_EventNotify)
895 {
Marc Jones212486e2008-07-17 19:50:37 +0000896 sHtEventCohNoTopology evt;
897 evt.eSize = sizeof(sHtEventCohNoTopology);
898 evt.totalNodes = pDat->NodesDiscovered;
Marc Jones8ae8c882007-12-19 01:32:08 +0000899
900 pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,
901 HT_EVENT_COH_NO_TOPOLOGY,
902 (u8 *)&evt);
903 }
904 STOP_HERE;
905 /* Force 1P */
906 pDat->NodesDiscovered = 0;
907 pDat->TotalLinks = 0;
908 pDat->nb->enableRoutingTables(0, pDat->nb);
909 }
910}
911#endif /* HT_BUILD_NC_ONLY */
912
913
914/*----------------------------------------------------------------------------------------
915 * void
916 * finializeCoherentInit(sMainData *pDat)
917 *
918 * Description:
919 * Find the total number of cores and update the number of nodes and cores in all cpus.
920 * Limit cpu config access to installed cpus.
921 *
922 * Parameters:
923 * @param[in] sMainData* pDat = our global state, number of nodes discovered.
924 * ---------------------------------------------------------------------------------------
925 */
Myles Watson075fbe82010-04-15 05:19:29 +0000926static void finializeCoherentInit(sMainData *pDat)
Marc Jones8ae8c882007-12-19 01:32:08 +0000927{
928 u8 curNode;
929
930 u8 totalCores = 0;
931 for (curNode = 0; curNode < pDat->NodesDiscovered+1; curNode++)
932 {
933 totalCores += pDat->nb->getNumCoresOnNode(curNode, pDat->nb);
934 }
935
936 for (curNode = 0; curNode < pDat->NodesDiscovered+1; curNode++)
937 {
938 pDat->nb->setTotalNodesAndCores(curNode, pDat->NodesDiscovered+1, totalCores, pDat->nb);
939 }
940
941 for (curNode = 0; curNode < pDat->NodesDiscovered+1; curNode++)
942 {
943 pDat->nb->limitNodes(curNode, pDat->nb);
944 }
945
946}
947
948/*----------------------------------------------------------------------------------------
949 * void
950 * coherentInit(sMainData *pDat)
951 *
952 * Description:
953 * Perform discovery and initialization of the coherent fabric.
954 *
955 * Parameters:
956 * @param[in] sMainData* pDat = our global state
957 * ---------------------------------------------------------------------------------------
958 */
Myles Watson075fbe82010-04-15 05:19:29 +0000959static void coherentInit(sMainData *pDat)
Marc Jones8ae8c882007-12-19 01:32:08 +0000960{
Marc Jones8ae8c882007-12-19 01:32:08 +0000961#ifdef HT_BUILD_NC_ONLY
962 /* Replace discovery process with:
963 * No other nodes, no coherent links
964 * Enable routing tables on currentNode, for power on self route
965 */
966 pDat->NodesDiscovered = 0;
967 pDat->TotalLinks = 0;
968 pDat->nb->enableRoutingTables(0, pDat->nb);
969#else
Xavi Drudis Ferrane660f042010-08-17 06:12:59 +0000970 u8 i, j;
971
Marc Jones8ae8c882007-12-19 01:32:08 +0000972 pDat->NodesDiscovered = 0;
973 pDat->TotalLinks = 0;
974 for (i = 0; i < MAX_NODES; i++)
975 {
976 pDat->sysDegree[i] = 0;
977 for (j = 0; j < MAX_NODES; j++)
978 {
979 pDat->sysMatrix[i][j] = 0;
980 }
981 }
982
983 htDiscoveryFloodFill(pDat);
984 lookupComputeAndLoadRoutingTables(pDat);
985#endif
986 finializeCoherentInit(pDat);
987}
988
989/***************************************************************************
990 *** Non-coherent init code ***
991 *** Algorithms ***
992 ***************************************************************************/
993/*----------------------------------------------------------------------------------------
994 * void
995 * processLink(u8 node, u8 link, sMainData *pDat)
996 *
997 * Description:
998 * Process a non-coherent link, enabling a range of bus numbers, and setting the device
999 * ID for all devices found
1000 *
1001 * Parameters:
1002 * @param[in] u8 node = Node on which to process nc init
1003 * @param[in] u8 link = The non-coherent link on that node
1004 * @param[in] sMainData* pDat = our global state
1005 * ---------------------------------------------------------------------------------------
1006 */
Myles Watson075fbe82010-04-15 05:19:29 +00001007static void processLink(u8 node, u8 link, sMainData *pDat)
Marc Jones8ae8c882007-12-19 01:32:08 +00001008{
1009 u8 secBus, subBus;
1010 u32 currentBUID;
1011 u32 temp;
1012 u32 unitIDcnt;
1013 SBDFO currentPtr;
1014 u8 depth;
Patrick Georgi86224f62010-09-25 17:24:10 +00001015 const u8 *pSwapPtr;
Marc Jones8ae8c882007-12-19 01:32:08 +00001016
1017 SBDFO lastSBDFO = ILLEGAL_SBDFO;
1018 u8 lastLink = 0;
1019
1020 ASSERT(node < pDat->nb->maxNodes && link < pDat->nb->maxLinks);
1021
1022 if ((pDat->HtBlock->AMD_CB_OverrideBusNumbers == NULL)
1023 || !pDat->HtBlock->AMD_CB_OverrideBusNumbers(node, link, &secBus, &subBus))
1024 {
1025 /* Assign Bus numbers */
1026 if (pDat->AutoBusCurrent >= pDat->HtBlock->AutoBusMax)
1027 {
1028 /* If we run out of Bus Numbers notify, if call back unimplemented or if it
1029 * returns, skip this chain
1030 */
1031 if (pDat->HtBlock->AMD_CB_EventNotify)
1032 {
Marc Jones212486e2008-07-17 19:50:37 +00001033 sHTEventNcohBusMaxExceed evt;
1034 evt.eSize = sizeof(sHTEventNcohBusMaxExceed);
1035 evt.node = node;
1036 evt.link = link;
1037 evt.bus = pDat->AutoBusCurrent;
Marc Jones8ae8c882007-12-19 01:32:08 +00001038
1039 pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,HT_EVENT_NCOH_BUS_MAX_EXCEED,(u8 *)&evt);
1040 }
1041 STOP_HERE;
1042 return;
1043 }
1044
1045 if (pDat->UsedCfgMapEntires >= 4)
1046 {
1047 /* If we have used all the PCI Config maps we can't add another chain.
1048 * Notify and if call back is unimplemented or returns, skip this chain.
1049 */
1050 if (pDat->HtBlock->AMD_CB_EventNotify)
1051 {
Marc Jones212486e2008-07-17 19:50:37 +00001052 sHtEventNcohCfgMapExceed evt;
1053 evt.eSize = sizeof(sHtEventNcohCfgMapExceed);
1054 evt.node = node;
1055 evt.link = link;
Marc Jones8ae8c882007-12-19 01:32:08 +00001056
1057 pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,
1058 HT_EVENT_NCOH_CFG_MAP_EXCEED,
1059 (u8 *)&evt);
1060 }
1061 STOP_HERE;
1062 return;
1063 }
1064
1065 secBus = pDat->AutoBusCurrent;
1066 subBus = secBus + pDat->HtBlock->AutoBusIncrement-1;
1067 pDat->AutoBusCurrent += pDat->HtBlock->AutoBusIncrement;
1068 }
1069
1070 pDat->nb->setCFGAddrMap(pDat->UsedCfgMapEntires, secBus, subBus, node, link, pDat, pDat->nb);
1071 pDat->UsedCfgMapEntires++;
1072
1073 if ((pDat->HtBlock->AMD_CB_ManualBUIDSwapList != NULL)
1074 && pDat->HtBlock->AMD_CB_ManualBUIDSwapList(node, link, &pSwapPtr))
1075 {
1076 /* Manual non-coherent BUID assignment */
Timothy Pearsonc139c422015-01-23 20:27:26 -06001077 currentBUID = 1;
Marc Jones8ae8c882007-12-19 01:32:08 +00001078
1079 /* Assign BUID's per manual override */
1080 while (*pSwapPtr != 0xFF)
1081 {
1082 currentPtr = MAKE_SBDFO(0, secBus, *pSwapPtr, 0, 0);
1083 pSwapPtr++;
1084
1085 do
1086 {
1087 AmdPCIFindNextCap(&currentPtr);
1088 ASSERT(currentPtr != ILLEGAL_SBDFO);
1089 AmdPCIRead(currentPtr, &temp);
1090 } while (!IS_HT_SLAVE_CAPABILITY(temp));
1091
1092 currentBUID = *pSwapPtr;
1093 pSwapPtr++;
1094 AmdPCIWriteBits(currentPtr, 20, 16, &currentBUID);
1095 }
1096
1097 /* Build chain of devices */
1098 depth = 0;
1099 pSwapPtr++;
1100 while (*pSwapPtr != 0xFF)
1101 {
1102 pDat->PortList[pDat->TotalLinks*2].NodeID = node;
1103 if (depth == 0)
1104 {
1105 pDat->PortList[pDat->TotalLinks*2].Type = PORTLIST_TYPE_CPU;
1106 pDat->PortList[pDat->TotalLinks*2].Link = link;
1107 }
1108 else
1109 {
1110 pDat->PortList[pDat->TotalLinks*2].Type = PORTLIST_TYPE_IO;
1111 pDat->PortList[pDat->TotalLinks*2].Link = 1-lastLink;
1112 pDat->PortList[pDat->TotalLinks*2].HostLink = link;
1113 pDat->PortList[pDat->TotalLinks*2].HostDepth = depth-1;
1114 pDat->PortList[pDat->TotalLinks*2].Pointer = lastSBDFO;
1115 }
1116
1117 pDat->PortList[pDat->TotalLinks*2+1].Type = PORTLIST_TYPE_IO;
1118 pDat->PortList[pDat->TotalLinks*2+1].NodeID = node;
1119 pDat->PortList[pDat->TotalLinks*2+1].HostLink = link;
1120 pDat->PortList[pDat->TotalLinks*2+1].HostDepth = depth;
1121
1122 currentPtr = MAKE_SBDFO(0, secBus, (*pSwapPtr & 0x3F), 0, 0);
1123 do
1124 {
1125 AmdPCIFindNextCap(&currentPtr);
1126 ASSERT(currentPtr != ILLEGAL_SBDFO);
1127 AmdPCIRead(currentPtr, &temp);
1128 } while (!IS_HT_SLAVE_CAPABILITY(temp));
1129 pDat->PortList[pDat->TotalLinks*2+1].Pointer = currentPtr;
1130 lastSBDFO = currentPtr;
1131
1132 /* Bit 6 indicates whether orientation override is desired.
1133 * Bit 7 indicates the upstream link if overriding.
1134 */
1135 /* assert catches at least the one known incorrect setting */
1136 ASSERT ((*pSwapPtr & 0x40) || (!(*pSwapPtr & 0x80)));
1137 if (*pSwapPtr & 0x40)
1138 {
1139 /* Override the device's orientation */
1140 lastLink = *pSwapPtr >> 7;
1141 }
1142 else
1143 {
1144 /* Detect the device's orientation */
1145 AmdPCIReadBits(currentPtr, 26, 26, &temp);
1146 lastLink = (u8)temp;
1147 }
1148 pDat->PortList[pDat->TotalLinks*2+1].Link = lastLink;
1149
1150 depth++;
1151 pDat->TotalLinks++;
1152 pSwapPtr++;
1153 }
1154 }
1155 else
1156 {
1157 /* Automatic non-coherent device detection */
1158 depth = 0;
1159 currentBUID = 1;
1160 while (1)
1161 {
1162 currentPtr = MAKE_SBDFO(0, secBus, 0, 0, 0);
1163
1164 AmdPCIRead(currentPtr, &temp);
1165 if (temp == 0xFFFFFFFF)
1166 /* No device found at currentPtr */
1167 break;
1168
1169 if (pDat->TotalLinks == MAX_PLATFORM_LINKS)
1170 {
1171 /*
1172 * Exceeded our capacity to describe all non-coherent links found in the system.
1173 * Error strategy:
1174 * Auto recovery is not possible because data space is already all used.
1175 */
1176 if (pDat->HtBlock->AMD_CB_EventNotify)
1177 {
Marc Jones212486e2008-07-17 19:50:37 +00001178 sHtEventNcohLinkExceed evt;
1179 evt.eSize = sizeof(sHtEventNcohLinkExceed);
1180 evt.node = node;
1181 evt.link = link;
1182 evt.depth = depth;
1183 evt.maxLinks = pDat->nb->maxLinks;
Marc Jones8ae8c882007-12-19 01:32:08 +00001184
1185 pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,
1186 HT_EVENT_NCOH_LINK_EXCEED,
1187 (u8 *)&evt);
1188 }
1189 /* Force link loop to halt */
1190 STOP_HERE;
1191 break;
1192 }
1193
1194 pDat->PortList[pDat->TotalLinks*2].NodeID = node;
1195 if (depth == 0)
1196 {
1197 pDat->PortList[pDat->TotalLinks*2].Type = PORTLIST_TYPE_CPU;
1198 pDat->PortList[pDat->TotalLinks*2].Link = link;
1199 }
1200 else
1201 {
1202 pDat->PortList[pDat->TotalLinks*2].Type = PORTLIST_TYPE_IO;
1203 pDat->PortList[pDat->TotalLinks*2].Link = 1-lastLink;
1204 pDat->PortList[pDat->TotalLinks*2].HostLink = link;
1205 pDat->PortList[pDat->TotalLinks*2].HostDepth = depth-1;
1206 pDat->PortList[pDat->TotalLinks*2].Pointer = lastSBDFO;
1207 }
1208
1209 pDat->PortList[pDat->TotalLinks*2+1].Type = PORTLIST_TYPE_IO;
1210 pDat->PortList[pDat->TotalLinks*2+1].NodeID = node;
1211 pDat->PortList[pDat->TotalLinks*2+1].HostLink = link;
1212 pDat->PortList[pDat->TotalLinks*2+1].HostDepth = depth;
1213
1214 do
1215 {
1216 AmdPCIFindNextCap(&currentPtr);
1217 ASSERT(currentPtr != ILLEGAL_SBDFO);
1218 AmdPCIRead(currentPtr, &temp);
1219 } while (!IS_HT_SLAVE_CAPABILITY(temp));
1220
1221 AmdPCIReadBits(currentPtr, 25, 21, &unitIDcnt);
1222 if ((unitIDcnt + currentBUID > 31) || ((secBus == 0) && (unitIDcnt + currentBUID > 24)))
1223 {
1224 /* An error handler for the case where we run out of BUID's on a chain */
1225 if (pDat->HtBlock->AMD_CB_EventNotify)
1226 {
Marc Jones212486e2008-07-17 19:50:37 +00001227 sHtEventNcohBuidExceed evt;
1228 evt.eSize = sizeof(sHtEventNcohBuidExceed);
1229 evt.node = node;
1230 evt.link = link;
1231 evt.depth = depth;
1232 evt.currentBUID = (uint8)currentBUID;
1233 evt.unitCount = (uint8)unitIDcnt;
Marc Jones8ae8c882007-12-19 01:32:08 +00001234
1235 pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,HT_EVENT_NCOH_BUID_EXCEED,(u8 *)&evt);
1236 }
1237 STOP_HERE;
1238 break;
1239 }
1240 AmdPCIWriteBits(currentPtr, 20, 16, &currentBUID);
1241
1242
1243 currentPtr += MAKE_SBDFO(0, 0, currentBUID, 0, 0);
1244 AmdPCIReadBits(currentPtr, 20, 16, &temp);
1245 if (temp != currentBUID)
1246 {
1247 /* An error handler for this critical error */
1248 if (pDat->HtBlock->AMD_CB_EventNotify)
1249 {
Marc Jones212486e2008-07-17 19:50:37 +00001250 sHtEventNcohDeviceFailed evt;
1251 evt.eSize = sizeof(sHtEventNcohDeviceFailed);
1252 evt.node = node;
1253 evt.link = link;
1254 evt.depth = depth;
1255 evt.attemptedBUID = (uint8)currentBUID;
Marc Jones8ae8c882007-12-19 01:32:08 +00001256
1257 pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,HT_EVENT_NCOH_DEVICE_FAILED,(u8 *)&evt);
1258 }
1259 STOP_HERE;
1260 break;
1261 }
1262
1263 AmdPCIReadBits(currentPtr, 26, 26, &temp);
1264 pDat->PortList[pDat->TotalLinks*2+1].Link = (u8)temp;
1265 pDat->PortList[pDat->TotalLinks*2+1].Pointer = currentPtr;
1266
1267 lastLink = (u8)temp;
1268 lastSBDFO = currentPtr;
1269
1270 depth++;
1271 pDat->TotalLinks++;
1272 currentBUID += unitIDcnt;
1273 }
1274 if (pDat->HtBlock->AMD_CB_EventNotify)
1275 {
1276 /* Provide information on automatic device results */
Marc Jones212486e2008-07-17 19:50:37 +00001277 sHtEventNcohAutoDepth evt;
1278 evt.eSize = sizeof(sHtEventNcohAutoDepth);
1279 evt.node = node;
1280 evt.link = link;
1281 evt.depth = (depth - 1);
Marc Jones8ae8c882007-12-19 01:32:08 +00001282
1283 pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_INFO,HT_EVENT_NCOH_AUTO_DEPTH,(u8 *)&evt);
1284 }
1285 }
1286}
1287
1288
1289/*----------------------------------------------------------------------------------------
1290 * void
1291 * ncInit(sMainData *pDat)
1292 *
1293 * Description:
1294 * Initialize the non-coherent fabric. Begin with the compat link on the BSP, then
1295 * find and initialize all other non-coherent chains.
1296 *
1297 * Parameters:
1298 * @param[in] sMainData* pDat = our global state
1299 * ---------------------------------------------------------------------------------------
1300 */
Myles Watson075fbe82010-04-15 05:19:29 +00001301static void ncInit(sMainData *pDat)
Marc Jones8ae8c882007-12-19 01:32:08 +00001302{
1303 u8 node, link;
1304 u8 compatLink;
1305
1306 compatLink = pDat->nb->readSbLink(pDat->nb);
1307 processLink(0, compatLink, pDat);
1308
1309 for (node = 0; node <= pDat->NodesDiscovered; node++)
1310 {
1311 for (link = 0; link < pDat->nb->maxLinks; link++)
1312 {
1313 if (pDat->HtBlock->AMD_CB_IgnoreLink && pDat->HtBlock->AMD_CB_IgnoreLink(node, link))
Marc Jonesaee07962008-07-16 21:09:31 +00001314 continue; /* Skip the link */
Marc Jones8ae8c882007-12-19 01:32:08 +00001315
1316 if (node == 0 && link == compatLink)
1317 continue;
1318
1319 if (pDat->nb->readTrueLinkFailStatus(node, link, pDat, pDat->nb))
1320 continue;
1321
1322 if (pDat->nb->verifyLinkIsNonCoherent(node, link, pDat->nb))
1323 processLink(node, link, pDat);
1324 }
1325 }
1326}
1327
1328/***************************************************************************
1329 *** Link Optimization ***
1330 ***************************************************************************/
1331
1332/*----------------------------------------------------------------------------------------
1333 * void
1334 * regangLinks(sMainData *pDat)
1335 *
1336 * Description:
1337 * Test the sublinks of a link to see if they qualify to be reganged. If they do,
1338 * update the port list data to indicate that this should be done. Note that no
1339 * actual hardware state is changed in this routine.
1340 *
1341 * Parameters:
1342 * @param[in,out] sMainData* pDat = our global state
1343 * ---------------------------------------------------------------------------------------
1344 */
Myles Watson075fbe82010-04-15 05:19:29 +00001345static void regangLinks(sMainData *pDat)
Marc Jones8ae8c882007-12-19 01:32:08 +00001346{
1347#ifndef HT_BUILD_NC_ONLY
1348 u8 i, j;
1349 for (i = 0; i < pDat->TotalLinks*2; i += 2)
1350 {
Marc Jonesaee07962008-07-16 21:09:31 +00001351 ASSERT(pDat->PortList[i].Type < 2 && pDat->PortList[i].Link < pDat->nb->maxLinks); /* Data validation */
1352 ASSERT(pDat->PortList[i+1].Type < 2 && pDat->PortList[i+1].Link < pDat->nb->maxLinks); /* data validation */
1353 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 */
Marc Jones8ae8c882007-12-19 01:32:08 +00001354
1355 /* Regang is false unless we pass all conditions below */
1356 pDat->PortList[i].SelRegang = FALSE;
1357 pDat->PortList[i+1].SelRegang = FALSE;
1358
1359 if ( (pDat->PortList[i].Type != PORTLIST_TYPE_CPU) || (pDat->PortList[i+1].Type != PORTLIST_TYPE_CPU))
Marc Jonesaee07962008-07-16 21:09:31 +00001360 continue; /* Only process cpu to cpu links */
Marc Jones8ae8c882007-12-19 01:32:08 +00001361
1362 for (j = i+2; j < pDat->TotalLinks*2; j += 2)
1363 {
1364 if ( (pDat->PortList[j].Type != PORTLIST_TYPE_CPU) || (pDat->PortList[j+1].Type != PORTLIST_TYPE_CPU) )
Marc Jonesaee07962008-07-16 21:09:31 +00001365 continue; /* Only process cpu to cpu links */
Marc Jones8ae8c882007-12-19 01:32:08 +00001366
1367 if (pDat->PortList[i].NodeID != pDat->PortList[j].NodeID)
Marc Jonesaee07962008-07-16 21:09:31 +00001368 continue; /* Links must be from the same source */
Marc Jones8ae8c882007-12-19 01:32:08 +00001369
1370 if (pDat->PortList[i+1].NodeID != pDat->PortList[j+1].NodeID)
Marc Jonesaee07962008-07-16 21:09:31 +00001371 continue; /* Link must be to the same target */
Marc Jones8ae8c882007-12-19 01:32:08 +00001372
1373 if ((pDat->PortList[i].Link & 3) != (pDat->PortList[j].Link & 3))
Marc Jonesaee07962008-07-16 21:09:31 +00001374 continue; /* Ensure same source base port */
Marc Jones8ae8c882007-12-19 01:32:08 +00001375
1376 if ((pDat->PortList[i+1].Link & 3) != (pDat->PortList[j+1].Link & 3))
Marc Jonesaee07962008-07-16 21:09:31 +00001377 continue; /* Ensure same destination base port */
Marc Jones8ae8c882007-12-19 01:32:08 +00001378
1379 if ((pDat->PortList[i].Link & 4) != (pDat->PortList[i+1].Link & 4))
Marc Jonesaee07962008-07-16 21:09:31 +00001380 continue; /* Ensure sublink0 routes to sublink0 */
Marc Jones8ae8c882007-12-19 01:32:08 +00001381
Marc Jonesaee07962008-07-16 21:09:31 +00001382 ASSERT((pDat->PortList[j].Link & 4) == (pDat->PortList[j+1].Link & 4)); /* (therefore sublink1 routes to sublink1) */
Marc Jones8ae8c882007-12-19 01:32:08 +00001383
1384 if (pDat->HtBlock->AMD_CB_SkipRegang &&
Marc Jonesaee07962008-07-16 21:09:31 +00001385 pDat->HtBlock->AMD_CB_SkipRegang(pDat->PortList[i].NodeID,
Marc Jones8ae8c882007-12-19 01:32:08 +00001386 pDat->PortList[i].Link & 0x03,
1387 pDat->PortList[i+1].NodeID,
1388 pDat->PortList[i+1].Link & 0x03))
1389 {
Marc Jonesaee07962008-07-16 21:09:31 +00001390 continue; /* Skip regang */
Marc Jones8ae8c882007-12-19 01:32:08 +00001391 }
1392
1393
Marc Jonesaee07962008-07-16 21:09:31 +00001394 pDat->PortList[i].Link &= 0x03; /* Force to point to sublink0 */
Marc Jones8ae8c882007-12-19 01:32:08 +00001395 pDat->PortList[i+1].Link &= 0x03;
Marc Jonesaee07962008-07-16 21:09:31 +00001396 pDat->PortList[i].SelRegang = TRUE; /* Enable link reganging */
Marc Jones8ae8c882007-12-19 01:32:08 +00001397 pDat->PortList[i+1].SelRegang = TRUE;
1398 pDat->PortList[i].PrvWidthOutCap = HT_WIDTH_16_BITS;
1399 pDat->PortList[i+1].PrvWidthOutCap = HT_WIDTH_16_BITS;
1400 pDat->PortList[i].PrvWidthInCap = HT_WIDTH_16_BITS;
1401 pDat->PortList[i+1].PrvWidthInCap = HT_WIDTH_16_BITS;
1402
Marc Jonesaee07962008-07-16 21:09:31 +00001403 /* Delete PortList[j, j+1], slow but easy to debug implementation */
Marc Jones8ae8c882007-12-19 01:32:08 +00001404 pDat->TotalLinks--;
1405 Amdmemcpy(&(pDat->PortList[j]), &(pDat->PortList[j+2]), sizeof(sPortDescriptor)*(pDat->TotalLinks*2-j));
1406 Amdmemset(&(pDat->PortList[pDat->TotalLinks*2]), INVALID_LINK, sizeof(sPortDescriptor)*2);
1407
Marc Jonesaee07962008-07-16 21:09:31 +00001408 /* //High performance, but would make debuging harder due to 'shuffling' of the records */
1409 /* //Amdmemcpy(PortList[TotalPorts-2], PortList[j], SIZEOF(sPortDescriptor)*2); */
1410 /* //TotalPorts -=2; */
Marc Jones8ae8c882007-12-19 01:32:08 +00001411
Marc Jonesaee07962008-07-16 21:09:31 +00001412 break; /* Exit loop, advance to PortList[i+2] */
Marc Jones8ae8c882007-12-19 01:32:08 +00001413 }
1414 }
1415#endif /* HT_BUILD_NC_ONLY */
1416}
1417
Timothy Pearson50001b82015-08-11 17:47:48 -05001418static void detectIoLinkIsochronousCapable(sMainData *pDat)
1419{
1420 uint8_t i;
1421 unsigned char iommu;
1422 uint8_t isochronous_capable = 0;
1423
1424 iommu = 1;
1425 get_option(&iommu, "iommu");
1426
1427 for (i = 0; i < pDat->TotalLinks*2; i += 2) {
1428 if ((pDat->PortList[i].Type == PORTLIST_TYPE_CPU) && (pDat->PortList[i+1].Type == PORTLIST_TYPE_IO)) {
1429 if ((pDat->PortList[i].PrvFeatureCap & 0x1) && (pDat->PortList[i+1].PrvFeatureCap & 0x1)) {
1430 pDat->PortList[i].enable_isochronous_mode = 1;
1431 pDat->PortList[i+1].enable_isochronous_mode = 1;
1432 isochronous_capable = 1;
1433 } else {
1434 pDat->PortList[i].enable_isochronous_mode = 0;
1435 pDat->PortList[i+1].enable_isochronous_mode = 0;
1436 }
1437 }
1438 }
1439
1440 if (isochronous_capable && iommu) {
1441 printk(BIOS_DEBUG, "Forcing HT links to isochronous mode due to enabled IOMMU\n");
1442 /* Isochronous mode must be set on all links if the IOMMU is enabled */
1443 for (i = 0; i < pDat->TotalLinks*2; i += 2) {
1444 pDat->PortList[i].enable_isochronous_mode = 1;
1445 pDat->PortList[i+1].enable_isochronous_mode = 1;
1446 }
1447 }
1448}
1449
Marc Jones8ae8c882007-12-19 01:32:08 +00001450/*----------------------------------------------------------------------------------------
1451 * void
1452 * selectOptimalWidthAndFrequency(sMainData *pDat)
1453 *
1454 * Description:
1455 * For all links:
1456 * Examine both sides of a link and determine the optimal frequency and width,
1457 * taking into account externally provided limits and enforcing any other limit
1458 * or matching rules as applicable except sublink balancing. Update the port
1459 * list date with the optimal settings.
1460 * Note no hardware state changes in this routine.
1461 *
1462 * Parameters:
1463 * @param[in,out] sMainData* pDat = our global state, port list data
1464 * ---------------------------------------------------------------------------------------
1465 */
Myles Watson075fbe82010-04-15 05:19:29 +00001466static void selectOptimalWidthAndFrequency(sMainData *pDat)
Marc Jones8ae8c882007-12-19 01:32:08 +00001467{
1468 u8 i, j;
Timothy Pearson7c55f372015-08-02 21:36:24 -05001469 uint32_t temp;
1470 uint32_t cbPCBFreqLimit;
1471 uint32_t cbPCBFreqLimit_NVRAM;
Marc Jones8ae8c882007-12-19 01:32:08 +00001472 u8 cbPCBABDownstreamWidth;
1473 u8 cbPCBBAUpstreamWidth;
1474
Timothy Pearson7c55f372015-08-02 21:36:24 -05001475 cbPCBFreqLimit_NVRAM = 0xfffff;
Timothy Pearson03259a82015-02-15 17:11:14 -06001476 if (get_option(&temp, "hypertransport_speed_limit") == CB_SUCCESS)
1477 cbPCBFreqLimit_NVRAM = ht_speed_limit[temp & 0xf];
1478
Timothy Pearson7c55f372015-08-02 21:36:24 -05001479 if (!is_fam15h()) {
1480 /* FIXME
1481 * By default limit frequency to 2.6 GHz as there are residual
1482 * problems with HT v3.1 implementation on at least some Socket G34
1483 * mainboards / Fam10h CPUs.
1484 * Debug the issues and reenable this...
1485 */
1486 if (cbPCBFreqLimit_NVRAM > 0xffff)
1487 cbPCBFreqLimit_NVRAM = 0xffff;
1488 }
1489
Marc Jones8ae8c882007-12-19 01:32:08 +00001490 for (i = 0; i < pDat->TotalLinks*2; i += 2)
1491 {
Timothy Pearson7c55f372015-08-02 21:36:24 -05001492 cbPCBFreqLimit = 0xfffff; // Maximum allowed by autoconfiguration
Timothy Pearson586d6e22015-02-16 14:57:06 -06001493 if (pDat->HtBlock->ht_link_configuration)
1494 cbPCBFreqLimit = ht_speed_mhz_to_hw(pDat->HtBlock->ht_link_configuration->ht_speed_limit);
Timothy Pearson03259a82015-02-15 17:11:14 -06001495 cbPCBFreqLimit = min(cbPCBFreqLimit, cbPCBFreqLimit_NVRAM);
Timothy Pearson55cf7bc2010-03-01 10:30:08 +00001496
Alexandru Gagniucfdbc1af2015-08-26 10:11:02 -04001497#if CONFIG_LIMIT_HT_DOWN_WIDTH_8
Timothy Pearson55cf7bc2010-03-01 10:30:08 +00001498 cbPCBABDownstreamWidth = 8;
1499#else
Marc Jones8ae8c882007-12-19 01:32:08 +00001500 cbPCBABDownstreamWidth = 16;
Timothy Pearson55cf7bc2010-03-01 10:30:08 +00001501#endif
1502
Alexandru Gagniucfdbc1af2015-08-26 10:11:02 -04001503#if CONFIG_LIMIT_HT_UP_WIDTH_8
Timothy Pearson55cf7bc2010-03-01 10:30:08 +00001504 cbPCBBAUpstreamWidth = 8;
1505#else
Marc Jones8ae8c882007-12-19 01:32:08 +00001506 cbPCBBAUpstreamWidth = 16;
Timothy Pearson55cf7bc2010-03-01 10:30:08 +00001507#endif
Marc Jones8ae8c882007-12-19 01:32:08 +00001508
1509 if ( (pDat->PortList[i].Type == PORTLIST_TYPE_CPU) && (pDat->PortList[i+1].Type == PORTLIST_TYPE_CPU))
1510 {
1511 if (pDat->HtBlock->AMD_CB_Cpu2CpuPCBLimits)
1512 {
1513 pDat->HtBlock->AMD_CB_Cpu2CpuPCBLimits(
1514 pDat->PortList[i].NodeID,
1515 pDat->PortList[i].Link,
1516 pDat->PortList[i+1].NodeID,
1517 pDat->PortList[i+1].Link,
1518 &cbPCBABDownstreamWidth,
1519 &cbPCBBAUpstreamWidth, &cbPCBFreqLimit
1520 );
1521 }
1522 }
1523 else
1524 {
1525 if (pDat->HtBlock->AMD_CB_IOPCBLimits)
1526 {
1527 pDat->HtBlock->AMD_CB_IOPCBLimits(
1528 pDat->PortList[i+1].NodeID,
1529 pDat->PortList[i+1].HostLink,
1530 pDat->PortList[i+1].HostDepth,
1531 &cbPCBABDownstreamWidth,
1532 &cbPCBBAUpstreamWidth, &cbPCBFreqLimit
1533 );
1534 }
1535 }
1536
Marc Jones8ae8c882007-12-19 01:32:08 +00001537 temp = pDat->PortList[i].PrvFrequencyCap;
1538 temp &= pDat->PortList[i+1].PrvFrequencyCap;
1539 temp &= cbPCBFreqLimit;
Timothy Pearson7c55f372015-08-02 21:36:24 -05001540 pDat->PortList[i].CompositeFrequencyCap = temp;
1541 pDat->PortList[i+1].CompositeFrequencyCap = temp;
Marc Jones8ae8c882007-12-19 01:32:08 +00001542
1543 ASSERT (temp != 0);
Timothy Pearson7c55f372015-08-02 21:36:24 -05001544 for (j = 19; ; j--)
Marc Jones8ae8c882007-12-19 01:32:08 +00001545 {
Timothy Pearson7c55f372015-08-02 21:36:24 -05001546 if ((j == 16) || (j == 15))
1547 continue;
1548 if (temp & ((uint32_t)1 << j))
Marc Jones8ae8c882007-12-19 01:32:08 +00001549 break;
1550 }
1551
1552 pDat->PortList[i].SelFrequency = j;
1553 pDat->PortList[i+1].SelFrequency = j;
1554
1555 temp = pDat->PortList[i].PrvWidthOutCap;
1556 if (pDat->PortList[i+1].PrvWidthInCap < temp)
1557 temp = pDat->PortList[i+1].PrvWidthInCap;
1558 if (cbPCBABDownstreamWidth < temp)
1559 temp = cbPCBABDownstreamWidth;
1560 pDat->PortList[i].SelWidthOut = (u8)temp;
1561 pDat->PortList[i+1].SelWidthIn = (u8)temp;
1562
1563 temp = pDat->PortList[i].PrvWidthInCap;
1564 if (pDat->PortList[i+1].PrvWidthOutCap < temp)
1565 temp = pDat->PortList[i+1].PrvWidthOutCap;
1566 if (cbPCBBAUpstreamWidth < temp)
1567 temp = cbPCBBAUpstreamWidth;
1568 pDat->PortList[i].SelWidthIn = (u8)temp;
1569 pDat->PortList[i+1].SelWidthOut = (u8)temp;
Marc Jones8ae8c882007-12-19 01:32:08 +00001570 }
1571}
1572
1573/*----------------------------------------------------------------------------------------
1574 * void
1575 * hammerSublinkFixup(sMainData *pDat)
1576 *
1577 * Description:
1578 * Iterate through all links, checking the frequency of each sublink pair. Make the
1579 * adjustment to the port list data so that the frequencies are at a valid ratio,
1580 * reducing frequency as needed to achieve this. (All links support the minimum 200 MHz
1581 * frequency.) Repeat the above until no adjustments are needed.
1582 * Note no hardware state changes in this routine.
1583 *
1584 * Parameters:
1585 * @param[in,out] sMainData* pDat = our global state, link state and port list
1586 * ---------------------------------------------------------------------------------------
1587 */
Myles Watson075fbe82010-04-15 05:19:29 +00001588static void hammerSublinkFixup(sMainData *pDat)
Marc Jones8ae8c882007-12-19 01:32:08 +00001589{
1590#ifndef HT_BUILD_NC_ONLY
1591 u8 i, j, k;
1592 BOOL changes, downgrade;
1593
1594 u8 hiIndex;
1595 u8 hiFreq, loFreq;
1596
1597 u32 temp;
1598
1599 do
1600 {
1601 changes = FALSE;
1602 for (i = 0; i < pDat->TotalLinks*2; i++)
1603 {
Marc Jonesaee07962008-07-16 21:09:31 +00001604 if (pDat->PortList[i].Type != PORTLIST_TYPE_CPU) /* Must be a CPU link */
Marc Jones8ae8c882007-12-19 01:32:08 +00001605 continue;
Marc Jonesaee07962008-07-16 21:09:31 +00001606 if (pDat->PortList[i].Link < 4) /* Only look for for sublink1's */
Marc Jones8ae8c882007-12-19 01:32:08 +00001607 continue;
1608
1609 for (j = 0; j < pDat->TotalLinks*2; j++)
1610 {
Marc Jonesaee07962008-07-16 21:09:31 +00001611 /* Step 1. Find the matching sublink0 */
Marc Jones8ae8c882007-12-19 01:32:08 +00001612 if (pDat->PortList[j].Type != PORTLIST_TYPE_CPU)
1613 continue;
1614 if (pDat->PortList[j].NodeID != pDat->PortList[i].NodeID)
1615 continue;
1616 if (pDat->PortList[j].Link != (pDat->PortList[i].Link & 0x03))
1617 continue;
1618
Marc Jonesaee07962008-07-16 21:09:31 +00001619 /* Step 2. Check for an illegal frequency ratio */
Marc Jones8ae8c882007-12-19 01:32:08 +00001620 if (pDat->PortList[i].SelFrequency >= pDat->PortList[j].SelFrequency)
1621 {
1622 hiIndex = i;
1623 hiFreq = pDat->PortList[i].SelFrequency;
1624 loFreq = pDat->PortList[j].SelFrequency;
1625 }
1626 else
1627 {
1628 hiIndex = j;
1629 hiFreq = pDat->PortList[j].SelFrequency;
1630 loFreq = pDat->PortList[i].SelFrequency;
1631 }
1632
1633 if (hiFreq == loFreq)
Marc Jonesaee07962008-07-16 21:09:31 +00001634 break; /* The frequencies are 1:1, no need to do anything */
Marc Jones8ae8c882007-12-19 01:32:08 +00001635
1636 downgrade = FALSE;
1637
1638 if (hiFreq == 13)
1639 {
Marc Jonesaee07962008-07-16 21:09:31 +00001640 if ((loFreq != 7) && /* {13, 7} 2400MHz / 1200MHz 2:1 */
1641 (loFreq != 4) && /* {13, 4} 2400MHz / 600MHz 4:1 */
1642 (loFreq != 2) ) /* {13, 2} 2400MHz / 400MHz 6:1 */
Marc Jones8ae8c882007-12-19 01:32:08 +00001643 downgrade = TRUE;
1644 }
1645 else if (hiFreq == 11)
1646 {
Marc Jonesaee07962008-07-16 21:09:31 +00001647 if ((loFreq != 6)) /* {11, 6} 2000MHz / 1000MHz 2:1 */
Marc Jones8ae8c882007-12-19 01:32:08 +00001648 downgrade = TRUE;
1649 }
1650 else if (hiFreq == 9)
1651 {
Marc Jonesaee07962008-07-16 21:09:31 +00001652 if ((loFreq != 5) && /* { 9, 5} 1600MHz / 800MHz 2:1 */
1653 (loFreq != 2) && /* { 9, 2} 1600MHz / 400MHz 4:1 */
Elyes HAOUAS0f92f632014-07-27 19:37:31 +02001654 (loFreq != 0) ) /* { 9, 0} 1600MHz / 200MHz 8:1 */
Marc Jones8ae8c882007-12-19 01:32:08 +00001655 downgrade = TRUE;
1656 }
1657 else if (hiFreq == 7)
1658 {
Marc Jonesaee07962008-07-16 21:09:31 +00001659 if ((loFreq != 4) && /* { 7, 4} 1200MHz / 600MHz 2:1 */
1660 (loFreq != 0) ) /* { 7, 0} 1200MHz / 200MHz 6:1 */
Marc Jones8ae8c882007-12-19 01:32:08 +00001661 downgrade = TRUE;
1662 }
1663 else if (hiFreq == 5)
1664 {
Marc Jonesaee07962008-07-16 21:09:31 +00001665 if ((loFreq != 2) && /* { 5, 2} 800MHz / 400MHz 2:1 */
1666 (loFreq != 0) ) /* { 5, 0} 800MHz / 200MHz 4:1 */
Marc Jones8ae8c882007-12-19 01:32:08 +00001667 downgrade = TRUE;
1668 }
1669 else if (hiFreq == 2)
1670 {
Marc Jonesaee07962008-07-16 21:09:31 +00001671 if ((loFreq != 0)) /* { 2, 0} 400MHz / 200MHz 2:1 */
Marc Jones8ae8c882007-12-19 01:32:08 +00001672 downgrade = TRUE;
1673 }
1674 else
1675 {
Marc Jonesaee07962008-07-16 21:09:31 +00001676 downgrade = TRUE; /* no legal ratios for hiFreq */
Marc Jones8ae8c882007-12-19 01:32:08 +00001677 }
1678
Marc Jonesaee07962008-07-16 21:09:31 +00001679 /* Step 3. Downgrade the higher of the two frequencies, and set nochanges to FALSE */
Marc Jones8ae8c882007-12-19 01:32:08 +00001680 if (downgrade)
1681 {
Marc Jonesaee07962008-07-16 21:09:31 +00001682 /* Although the problem was with the port specified by hiIndex, we need to */
1683 /* downgrade both ends of the link. */
1684 hiIndex = hiIndex & 0xFE; /* Select the 'upstream' (i.e. even) port */
Marc Jones8ae8c882007-12-19 01:32:08 +00001685
1686 temp = pDat->PortList[hiIndex].CompositeFrequencyCap;
1687
Marc Jonesaee07962008-07-16 21:09:31 +00001688 /* Remove hiFreq from the list of valid frequencies */
1689 temp = temp & ~((uint32)1 << hiFreq);
Marc Jones8ae8c882007-12-19 01:32:08 +00001690 ASSERT (temp != 0);
Timothy Pearson7c55f372015-08-02 21:36:24 -05001691 pDat->PortList[hiIndex].CompositeFrequencyCap = temp;
1692 pDat->PortList[hiIndex+1].CompositeFrequencyCap = temp;
Marc Jones8ae8c882007-12-19 01:32:08 +00001693
Timothy Pearson7c55f372015-08-02 21:36:24 -05001694 for (k = 19; ; k--)
Marc Jones8ae8c882007-12-19 01:32:08 +00001695 {
Timothy Pearson7c55f372015-08-02 21:36:24 -05001696 if ((j == 16) || (j == 15))
1697 continue;
1698 if (temp & ((uint32_t)1 << k))
Marc Jones8ae8c882007-12-19 01:32:08 +00001699 break;
1700 }
1701
1702 pDat->PortList[hiIndex].SelFrequency = k;
1703 pDat->PortList[hiIndex+1].SelFrequency = k;
1704
1705 changes = TRUE;
1706 }
1707 }
1708 }
Marc Jonesaee07962008-07-16 21:09:31 +00001709 } while (changes); /* Repeat until a valid configuration is reached */
Marc Jones8ae8c882007-12-19 01:32:08 +00001710#endif /* HT_BUILD_NC_ONLY */
1711}
1712
1713/*----------------------------------------------------------------------------------------
1714 * void
1715 * linkOptimization(sMainData *pDat)
1716 *
1717 * Description:
1718 * Based on link capabilities, apply optimization rules to come up with the real best
1719 * settings, including several external limit decision from call backs. This includes
1720 * handling of sublinks. Finally, after the port list data is updated, set the hardware
1721 * state for all links.
1722 *
1723 * Parameters:
1724 * @param[in] sMainData* pDat = our global state
1725 * ---------------------------------------------------------------------------------------
1726 */
Myles Watson075fbe82010-04-15 05:19:29 +00001727static void linkOptimization(sMainData *pDat)
Marc Jones8ae8c882007-12-19 01:32:08 +00001728{
1729 pDat->nb->gatherLinkData(pDat, pDat->nb);
1730 regangLinks(pDat);
Timothy Pearson50001b82015-08-11 17:47:48 -05001731 if (is_fam15h())
1732 detectIoLinkIsochronousCapable(pDat);
Marc Jones8ae8c882007-12-19 01:32:08 +00001733 selectOptimalWidthAndFrequency(pDat);
1734 hammerSublinkFixup(pDat);
1735 pDat->nb->setLinkData(pDat, pDat->nb);
1736}
1737
1738
1739/*----------------------------------------------------------------------------------------
1740 * void
1741 * trafficDistribution(sMainData *pDat)
1742 *
1743 * Description:
1744 * In the case of a two node system with both sublinks used, enable the traffic
1745 * distribution feature.
1746 *
1747 * Parameters:
1748 * @param[in] sMainData* pDat = our global state, port list data
1749 * ---------------------------------------------------------------------------------------
1750 */
Myles Watson075fbe82010-04-15 05:19:29 +00001751static void trafficDistribution(sMainData *pDat)
Marc Jones8ae8c882007-12-19 01:32:08 +00001752{
1753#ifndef HT_BUILD_NC_ONLY
1754 u32 links01, links10;
1755 u8 linkCount;
1756 u8 i;
1757
Marc Jonesaee07962008-07-16 21:09:31 +00001758 /* Traffic Distribution is only used when there are exactly two nodes in the system */
Marc Jones8ae8c882007-12-19 01:32:08 +00001759 if (pDat->NodesDiscovered+1 != 2)
1760 return;
1761
1762 links01 = 0;
1763 links10 = 0;
1764 linkCount = 0;
1765 for (i = 0; i < pDat->TotalLinks*2; i += 2)
1766 {
1767 if ((pDat->PortList[i].Type == PORTLIST_TYPE_CPU) && (pDat->PortList[i+1].Type == PORTLIST_TYPE_CPU))
1768 {
1769 links01 |= (u32)1 << pDat->PortList[i].Link;
1770 links10 |= (u32)1 << pDat->PortList[i+1].Link;
1771 linkCount++;
1772 }
1773 }
1774 ASSERT(linkCount != 0);
1775 if (linkCount == 1)
Marc Jonesaee07962008-07-16 21:09:31 +00001776 return; /* Don't setup Traffic Distribution if only one link is being used */
Marc Jones8ae8c882007-12-19 01:32:08 +00001777
1778 pDat->nb->writeTrafficDistribution(links01, links10, pDat->nb);
1779#endif /* HT_BUILD_NC_ONLY */
1780}
1781
1782/*----------------------------------------------------------------------------------------
1783 * void
1784 * tuning(sMainData *pDat)
1785 *
1786 * Description:
1787 * Handle system and performance tunings, such as traffic distribution, fifo and
1788 * buffer tuning, and special config tunings.
1789 *
1790 * Parameters:
1791 * @param[in] sMainData* pDat = our global state, port list data
1792 * ---------------------------------------------------------------------------------------
1793 */
Myles Watson075fbe82010-04-15 05:19:29 +00001794static void tuning(sMainData *pDat)
Marc Jones8ae8c882007-12-19 01:32:08 +00001795{
1796 u8 i;
1797
1798 /* See if traffic distribution can be done and do it if so
1799 * or allow system specific customization
1800 */
1801 if ((pDat->HtBlock->AMD_CB_CustomizeTrafficDistribution == NULL)
1802 || !pDat->HtBlock->AMD_CB_CustomizeTrafficDistribution())
1803 {
1804 trafficDistribution(pDat);
1805 }
1806
1807 /* For each node, invoke northbridge specific buffer tunings or
1808 * system specific customizations.
1809 */
1810 for (i=0; i < pDat->NodesDiscovered + 1; i++)
1811 {
1812 if ((pDat->HtBlock->AMD_CB_CustomizeBuffers == NULL)
1813 || !pDat->HtBlock->AMD_CB_CustomizeBuffers(i))
1814 {
1815 pDat->nb->bufferOptimizations(i, pDat, pDat->nb);
1816 }
1817 }
1818}
1819
1820/*----------------------------------------------------------------------------------------
1821 * BOOL
1822 * isSanityCheckOk()
1823 *
1824 * Description:
1825 * Perform any general sanity checks which should prevent HT from running if they fail.
1826 * Currently only the "Must run on BSP only" check.
1827 *
1828 * Parameters:
1829 * @param[out] result BOOL = true if check is ok, false if it failed
1830 * ---------------------------------------------------------------------------------------
1831 */
Myles Watson075fbe82010-04-15 05:19:29 +00001832static BOOL isSanityCheckOk(void)
Marc Jones8ae8c882007-12-19 01:32:08 +00001833{
1834 uint64 qValue;
1835
1836 AmdMSRRead(APIC_Base, &qValue);
1837
1838 return ((qValue.lo & ((u32)1 << APIC_Base_BSP)) != 0);
1839}
1840
1841/***************************************************************************
1842 *** HT Initialize ***
1843 ***************************************************************************/
1844
1845/*----------------------------------------------------------------------------------------
1846 * void
1847 * htInitialize(AMD_HTBLOCK *pBlock)
1848 *
1849 * Description:
1850 * This is the top level external interface for Hypertransport Initialization.
1851 * Create our initial internal state, initialize the coherent fabric,
1852 * initialize the non-coherent chains, and perform any required fabric tuning or
1853 * optimization.
1854 *
1855 * Parameters:
1856 * @param[in] AMD_HTBLOCK* pBlock = Our Initial State including possible
1857 * topologies and routings, non coherent bus
1858 * assignment info, and actual
1859 * wrapper or OEM call back routines.
1860 * ---------------------------------------------------------------------------------------
1861 */
1862void amdHtInitialize(AMD_HTBLOCK *pBlock)
1863{
1864 sMainData pDat;
1865 cNorthBridge nb;
1866
1867 if (isSanityCheckOk())
1868 {
1869 newNorthBridge(0, &nb);
1870
1871 pDat.HtBlock = pBlock;
1872 pDat.nb = &nb;
1873 pDat.sysMpCap = nb.maxNodes;
1874 nb.isCapable(0, &pDat, pDat.nb);
1875 coherentInit(&pDat);
1876
1877 pDat.AutoBusCurrent = pBlock->AutoBusStart;
1878 pDat.UsedCfgMapEntires = 0;
1879 ncInit(&pDat);
1880 linkOptimization(&pDat);
1881 tuning(&pDat);
1882 }
1883}