blob: f91933536075299f5752dce840d844412993bcb0 [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
Marc Jones8ae8c882007-12-19 01:32:08 +000024#include "h3finit.h"
25#include "h3ffeat.h"
26#include "h3ncmn.h"
27#include "h3gtopo.h"
28#include "AsPsNb.h"
Damien Zammit75a3d1f2016-11-28 00:29:10 +110029
Elyes HAOUASd2b9ec12018-10-27 09:41:02 +020030#include <arch/cpu.h>
Damien Zammit75a3d1f2016-11-28 00:29:10 +110031#include <device/pci.h>
Kyösti Mälkkif1b58b72019-03-01 13:43:02 +020032#include <device/pci_ops.h>
Damien Zammit75a3d1f2016-11-28 00:29:10 +110033#include <console/console.h>
Elyes HAOUASa9473ec2018-10-24 15:55:53 +020034#include <cpu/x86/lapic_def.h>
Damien Zammit75a3d1f2016-11-28 00:29:10 +110035#include <cpu/amd/msr.h>
36#include <device/pci_def.h>
Damien Zammit75a3d1f2016-11-28 00:29:10 +110037#include <northbridge/amd/amdfam10/raminit.h>
38#include <northbridge/amd/amdfam10/amdfam10.h>
39
Marc Jones8ae8c882007-12-19 01:32:08 +000040/*----------------------------------------------------------------------------
41 * DEFINITIONS AND MACROS
42 *
43 *----------------------------------------------------------------------------
44 */
45
Timothy Pearson7c55f372015-08-02 21:36:24 -050046#define NVRAM_LIMIT_HT_SPEED_200 0x12
47#define NVRAM_LIMIT_HT_SPEED_300 0x11
48#define NVRAM_LIMIT_HT_SPEED_400 0x10
49#define NVRAM_LIMIT_HT_SPEED_500 0xf
50#define NVRAM_LIMIT_HT_SPEED_600 0xe
51#define NVRAM_LIMIT_HT_SPEED_800 0xd
52#define NVRAM_LIMIT_HT_SPEED_1000 0xc
53#define NVRAM_LIMIT_HT_SPEED_1200 0xb
54#define NVRAM_LIMIT_HT_SPEED_1400 0xa
55#define NVRAM_LIMIT_HT_SPEED_1600 0x9
56#define NVRAM_LIMIT_HT_SPEED_1800 0x8
57#define NVRAM_LIMIT_HT_SPEED_2000 0x7
58#define NVRAM_LIMIT_HT_SPEED_2200 0x6
59#define NVRAM_LIMIT_HT_SPEED_2400 0x5
60#define NVRAM_LIMIT_HT_SPEED_2600 0x4
61#define NVRAM_LIMIT_HT_SPEED_2800 0x3
62#define NVRAM_LIMIT_HT_SPEED_3000 0x2
63#define NVRAM_LIMIT_HT_SPEED_3200 0x1
Timothy Pearson03259a82015-02-15 17:11:14 -060064#define NVRAM_LIMIT_HT_SPEED_AUTO 0x0
65
Timothy Pearson7c55f372015-08-02 21:36:24 -050066static const uint32_t ht_speed_limit[20] =
67 {0xFFFFF, 0xFFFFF, 0x7FFFF, 0x3FFFF,
68 0x0FFFF, 0x07FFF, 0x03FFF, 0x01FFF,
69 0x00FFF, 0x007FF, 0x003FF, 0x001FF,
70 0x000FF, 0x0007F, 0x0003F, 0x0001F,
71 0x0000F, 0x00007, 0x00003, 0x00001};
Timothy Pearson03259a82015-02-15 17:11:14 -060072
Timothy Pearson586d6e22015-02-16 14:57:06 -060073static const struct ht_speed_limit_map_t {
74 uint16_t mhz;
Timothy Pearsonb812d5d2015-03-14 19:59:54 -050075 uint8_t nvram;
Timothy Pearson586d6e22015-02-16 14:57:06 -060076} ht_speed_limit_map[] = {
77 {0, NVRAM_LIMIT_HT_SPEED_AUTO},
78 {200, NVRAM_LIMIT_HT_SPEED_200},
79 {300, NVRAM_LIMIT_HT_SPEED_300},
80 {400, NVRAM_LIMIT_HT_SPEED_400},
81 {500, NVRAM_LIMIT_HT_SPEED_500},
82 {600, NVRAM_LIMIT_HT_SPEED_600},
83 {800, NVRAM_LIMIT_HT_SPEED_800},
84 {1000, NVRAM_LIMIT_HT_SPEED_1000},
85 {1200, NVRAM_LIMIT_HT_SPEED_1200},
86 {1400, NVRAM_LIMIT_HT_SPEED_1400},
87 {1600, NVRAM_LIMIT_HT_SPEED_1600},
88 {1800, NVRAM_LIMIT_HT_SPEED_1800},
89 {2000, NVRAM_LIMIT_HT_SPEED_2000},
90 {2200, NVRAM_LIMIT_HT_SPEED_2200},
91 {2400, NVRAM_LIMIT_HT_SPEED_2400},
92 {2600, NVRAM_LIMIT_HT_SPEED_2600},
Timothy Pearson7c55f372015-08-02 21:36:24 -050093 {2800, NVRAM_LIMIT_HT_SPEED_2800},
94 {3000, NVRAM_LIMIT_HT_SPEED_3000},
95 {3200, NVRAM_LIMIT_HT_SPEED_3200},
Timothy Pearson586d6e22015-02-16 14:57:06 -060096};
97
Timothy Pearson7c55f372015-08-02 21:36:24 -050098static const uint32_t ht_speed_mhz_to_hw(uint16_t mhz)
Timothy Pearson586d6e22015-02-16 14:57:06 -060099{
100 size_t i;
101 for (i = 0; i < ARRAY_SIZE(ht_speed_limit_map); i++)
102 if (ht_speed_limit_map[i].mhz == mhz)
Timothy Pearsonb812d5d2015-03-14 19:59:54 -0500103 return ht_speed_limit[ht_speed_limit_map[i].nvram];
Timothy Pearson586d6e22015-02-16 14:57:06 -0600104
105 printk(BIOS_WARNING,
106 "WARNING: Invalid HT link limit frequency %d specified, ignoring...\n",
107 mhz);
108 return ht_speed_limit[NVRAM_LIMIT_HT_SPEED_AUTO];
109}
110
Marc Jones8ae8c882007-12-19 01:32:08 +0000111/*----------------------------------------------------------------------------
112 * TYPEDEFS AND STRUCTURES
113 *
114 *----------------------------------------------------------------------------
115 */
116
117/*----------------------------------------------------------------------------
118 * PROTOTYPES OF LOCAL FUNCTIONS
119 *
120 *----------------------------------------------------------------------------
121 */
122
123/*----------------------------------------------------------------------------
124 * EXPORTED FUNCTIONS
125 *
126 *----------------------------------------------------------------------------
127 */
128
129/*----------------------------------------------------------------------------
130 * LOCAL FUNCTIONS
131 *
132 *----------------------------------------------------------------------------
133 */
134#ifndef HT_BUILD_NC_ONLY
135/*
136 **************************************************************************
137 * Routing table decompressor
138 **************************************************************************
139 */
140
141/*
142 **************************************************************************
143 * Graph Support routines
144 * These routines provide support for dealing with the graph representation
145 * of the topologies, along with the routing table information for that topology.
146 * The routing information is compressed and these routines currently decompress
147 * 'on the fly'. A graph is represented as a set of routes. All the edges in the
148 * graph are routes; a direct route from node i to node j exists in the graph IFF
149 * there is an edge directly connecting node i to node j. All other routes designate
150 * the edge which the route to that node initially takes, by designating a node
151 * to which a direct connection exists. That is, the route to non-adjacent node j
152 * from node i specifies node k where node i directly connects to node k.
153 *
154 *
155 * pseudo definition of compressed graph:
156 * typedef struct
157 * {
158 * BIT broadcast[8];
159 * uint4 responseRoute;
160 * uint4 requestRoute;
161 * } sRoute;
162 * typedef struct
163 * {
164 * u8 size;
165 * sRoute graph[size][size];
166 * } sGraph;
167 *
168 **************************************************************************
169 */
170
171/*----------------------------------------------------------------------------------------
Marc Jones212486e2008-07-17 19:50:37 +0000172 * u8
Marc Jones8ae8c882007-12-19 01:32:08 +0000173 * graphHowManyNodes(u8 *graph)
174 *
175 * Description:
176 * Returns the number of nodes in the compressed graph
177 *
178 * Parameters:
Martin Rothf4cb4122015-01-06 10:27:39 -0700179 * @param[in] graph = a compressed graph
180 * @param[out] results = the number of nodes in the graph
Marc Jones8ae8c882007-12-19 01:32:08 +0000181 * ---------------------------------------------------------------------------------------
182 */
Myles Watson075fbe82010-04-15 05:19:29 +0000183static u8 graphHowManyNodes(u8 *graph)
Marc Jones8ae8c882007-12-19 01:32:08 +0000184{
185 return graph[0];
186}
187
188/*----------------------------------------------------------------------------------------
189 * BOOL
190 * graphIsAdjacent(u8 *graph, u8 nodeA, u8 nodeB)
191 *
192 * Description:
193 * Returns true if NodeA is directly connected to NodeB, false otherwise
194 * (if NodeA == NodeB also returns false)
195 * Relies on rule that directly connected nodes always route requests directly.
196 *
197 * Parameters:
Martin Rothf4cb4122015-01-06 10:27:39 -0700198 * @param[in] graph = the graph to examine
199 * @param[in] nodeA = the node number of the first node
200 * @param[in] nodeB = the node number of the second node
201 * @param[out] results = true if nodeA connects to nodeB false if not
Marc Jones8ae8c882007-12-19 01:32:08 +0000202 * ---------------------------------------------------------------------------------------
203 */
Myles Watson075fbe82010-04-15 05:19:29 +0000204static BOOL graphIsAdjacent(u8 *graph, u8 nodeA, u8 nodeB)
Marc Jones8ae8c882007-12-19 01:32:08 +0000205{
206 u8 size = graph[0];
207 ASSERT(size <= MAX_NODES);
208 ASSERT((nodeA < size) && (nodeB < size));
209 return (graph[1+(nodeA*size+nodeB)*2+1] & 0x0F) == nodeB;
210}
211
212/*----------------------------------------------------------------------------------------
213 * u8
214 * graphGetRsp(u8 *graph, u8 nodeA, u8 nodeB)
215 *
216 * Description:
217 * Returns the graph node used by nodeA to route responses targeted at nodeB.
218 * This will be a node directly connected to nodeA (possibly nodeB itself),
219 * or "Route to Self" if nodeA and nodeB are the same node.
220 * Note that all node numbers are abstract node numbers of the topology graph,
221 * it is the responsibility of the caller to apply any permutation needed.
222 *
223 * Parameters:
224 * @param[in] u8 graph = the graph to examine
225 * @param[in] u8 nodeA = the node number of the first node
226 * @param[in] u8 nodeB = the node number of the second node
227 * @param[out] u8 results = The response route node
228 * ---------------------------------------------------------------------------------------
229 */
Myles Watson075fbe82010-04-15 05:19:29 +0000230static u8 graphGetRsp(u8 *graph, u8 nodeA, u8 nodeB)
Marc Jones8ae8c882007-12-19 01:32:08 +0000231{
232 u8 size = graph[0];
233 ASSERT(size <= MAX_NODES);
234 ASSERT((nodeA < size) && (nodeB < size));
235 return (graph[1+(nodeA*size+nodeB)*2+1] & 0xF0)>>4;
236}
237
238/*----------------------------------------------------------------------------------------
239 * u8
240 * graphGetReq(u8 *graph, u8 nodeA, u8 nodeB)
241 *
242 * Description:
243 * Returns the graph node used by nodeA to route requests targeted at nodeB.
244 * This will be a node directly connected to nodeA (possibly nodeB itself),
245 * or "Route to Self" if nodeA and nodeB are the same node.
246 * Note that all node numbers are abstract node numbers of the topology graph,
247 * it is the responsibility of the caller to apply any permutation needed.
248 *
249 * Parameters:
Martin Rothf4cb4122015-01-06 10:27:39 -0700250 * @param[in] graph = the graph to examine
251 * @param[in] nodeA = the node number of the first node
252 * @param[in] nodeB = the node number of the second node
253 * @param[out] results = The request route node
Marc Jones8ae8c882007-12-19 01:32:08 +0000254 * ---------------------------------------------------------------------------------------
255 */
Myles Watson075fbe82010-04-15 05:19:29 +0000256static u8 graphGetReq(u8 *graph, u8 nodeA, u8 nodeB)
Marc Jones8ae8c882007-12-19 01:32:08 +0000257{
Marc Jones212486e2008-07-17 19:50:37 +0000258 u8 size = graph[0];
Marc Jones8ae8c882007-12-19 01:32:08 +0000259 ASSERT(size <= MAX_NODES);
260 ASSERT((nodeA < size) && (nodeB < size));
261 return (graph[1+(nodeA*size+nodeB)*2+1] & 0x0F);
262}
263
264/*----------------------------------------------------------------------------------------
265 * u8
Marc Jones212486e2008-07-17 19:50:37 +0000266 * graphGetBc(u8 *graph, u8 nodeA, u8 nodeB)
Marc Jones8ae8c882007-12-19 01:32:08 +0000267 *
268 * Description:
269 * Returns a bit vector of nodes that nodeA should forward a broadcast from
270 * nodeB towards
271 *
272 * Parameters:
Martin Rothf4cb4122015-01-06 10:27:39 -0700273 * @param[in] graph = the graph to examine
274 * @param[in] nodeA = the node number of the first node
275 * @param[in] nodeB = the node number of the second node
276 * OU results = the broadcast routes for nodeA from nodeB
Marc Jones8ae8c882007-12-19 01:32:08 +0000277 * ---------------------------------------------------------------------------------------
278 */
Myles Watson075fbe82010-04-15 05:19:29 +0000279static u8 graphGetBc(u8 *graph, u8 nodeA, u8 nodeB)
Marc Jones8ae8c882007-12-19 01:32:08 +0000280{
Marc Jones212486e2008-07-17 19:50:37 +0000281 u8 size = graph[0];
Marc Jones8ae8c882007-12-19 01:32:08 +0000282 ASSERT(size <= MAX_NODES);
283 ASSERT((nodeA < size) && (nodeB < size));
284 return graph[1+(nodeA*size+nodeB)*2];
285}
286
287
288/***************************************************************************
289 *** GENERIC HYPERTRANSPORT DISCOVERY CODE ***
290 ***************************************************************************/
291
292/*----------------------------------------------------------------------------------------
293 * void
294 * routeFromBSP(u8 targetNode, u8 actualTarget, sMainData *pDat)
295 *
296 * Description:
297 * Ensure a request / response route from target node to bsp. Since target node is
298 * always a predecessor of actual target node, each node gets a route to actual target
299 * on the link that goes to target. The routing produced by this routine is adequate
300 * for config access during discovery, but NOT for coherency.
301 *
302 * Parameters:
303 * @param[in] u8 targetNode = the path to actual target goes through target
304 * @param[in] u8 actualTarget = the ultimate target being routed to
305 * @param[in] sMainData* pDat = our global state, port config info
306 * ---------------------------------------------------------------------------------------
307 */
Myles Watson075fbe82010-04-15 05:19:29 +0000308static void routeFromBSP(u8 targetNode, u8 actualTarget, sMainData *pDat)
Marc Jones8ae8c882007-12-19 01:32:08 +0000309{
310 u8 predecessorNode, predecessorLink, currentPair;
311
312 if (targetNode == 0)
Marc Jonesaee07962008-07-16 21:09:31 +0000313 return; /* BSP has no predecessor, stop */
Marc Jones8ae8c882007-12-19 01:32:08 +0000314
Marc Jonesaee07962008-07-16 21:09:31 +0000315 /* Search for the link that connects targetNode to its predecessor */
Marc Jones8ae8c882007-12-19 01:32:08 +0000316 currentPair = 0;
317 while (pDat->PortList[currentPair*2+1].NodeID != targetNode)
318 {
319 currentPair++;
320 ASSERT(currentPair < pDat->TotalLinks);
321 }
322
323 predecessorNode = pDat->PortList[currentPair*2].NodeID;
324 predecessorLink = pDat->PortList[currentPair*2].Link;
325
Marc Jonesaee07962008-07-16 21:09:31 +0000326 /* Recursively call self to ensure the route from the BSP to the Predecessor */
327 /* Node is established */
Marc Jones8ae8c882007-12-19 01:32:08 +0000328 routeFromBSP(predecessorNode, actualTarget, pDat);
329
330 pDat->nb->writeRoutingTable(predecessorNode, actualTarget, predecessorLink, pDat->nb);
331}
332
Martin Rothf4cb4122015-01-06 10:27:39 -0700333/*---------------------------------------------------------------------------*/
334
335/**
336 * u8
Marc Jones8ae8c882007-12-19 01:32:08 +0000337 * convertNodeToLink(u8 srcNode, u8 targetNode, sMainData *pDat)
338 *
339 * Description:
340 * Return the link on source node which connects to target node
341 *
342 * Parameters:
Martin Rothf4cb4122015-01-06 10:27:39 -0700343 * @param[in] srcNode = the source node
344 * @param[in] targetNode = the target node to find the link to
345 * @param[in] pDat = our global state
346 * @return the link on source which connects to target
347 *
Marc Jones8ae8c882007-12-19 01:32:08 +0000348 */
Myles Watson075fbe82010-04-15 05:19:29 +0000349static u8 convertNodeToLink(u8 srcNode, u8 targetNode, sMainData *pDat)
Marc Jones8ae8c882007-12-19 01:32:08 +0000350{
351 u8 targetlink = INVALID_LINK;
352 u8 k;
353
354 for (k = 0; k < pDat->TotalLinks*2; k += 2)
355 {
356 if ((pDat->PortList[k+0].NodeID == srcNode) && (pDat->PortList[k+1].NodeID == targetNode))
357 {
358 targetlink = pDat->PortList[k+0].Link;
359 break;
360 }
361 else if ((pDat->PortList[k+1].NodeID == srcNode) && (pDat->PortList[k+0].NodeID == targetNode))
362 {
363 targetlink = pDat->PortList[k+1].Link;
364 break;
365 }
366 }
367 ASSERT(targetlink != INVALID_LINK);
368
369 return targetlink;
370}
371
372
373/*----------------------------------------------------------------------------------------
374 * void
375 * htDiscoveryFloodFill(sMainData *pDat)
376 *
377 * Description:
378 * Discover all coherent devices in the system, initializing some basics like node IDs
379 * and total nodes found in the process. As we go we also build a representation of the
380 * discovered system which we will use later to program the routing tables. During this
381 * step, the routing is via default link back to BSP and to each new node on the link it
382 * was discovered on (no coherency is active yet).
383 *
384 * Parameters:
385 * @param[in] sMainData* pDat = our global state
386 * ---------------------------------------------------------------------------------------
387 */
Myles Watson075fbe82010-04-15 05:19:29 +0000388static void htDiscoveryFloodFill(sMainData *pDat)
Marc Jones8ae8c882007-12-19 01:32:08 +0000389{
Timothy Pearson0122afb2015-07-30 14:07:15 -0500390 uint8_t currentNode = 0;
391 uint8_t currentLink;
392 uint8_t currentLinkID;
393
394 /* NOTE
395 * Each node inside a dual node (socket G34) processor must share
396 * an adjacent node ID. Alter the link scan order such that the
397 * other internal node is always scanned first...
398 */
399 uint8_t currentLinkScanOrder_Default[8] = {0, 1, 2, 3, 4, 5, 6, 7};
400 uint8_t currentLinkScanOrder_G34_Fam10[8] = {1, 0, 2, 3, 4, 5, 6, 7};
401 uint8_t currentLinkScanOrder_G34_Fam15[8] = {2, 0, 1, 3, 4, 5, 6, 7};
402
403 uint8_t fam15h = 0;
404 uint8_t rev_gte_d = 0;
405 uint8_t dual_node = 0;
406 uint32_t f3xe8;
407 uint32_t family;
408 uint32_t model;
409
410 f3xe8 = pci_read_config32(NODE_PCI(0, 3), 0xe8);
411
412 family = model = cpuid_eax(0x80000001);
413 model = ((model & 0xf0000) >> 12) | ((model & 0xf0) >> 4);
414 family = ((family & 0xf00000) >> 16) | ((family & 0xf00) >> 8);
415
416 if (family >= 0x6f) {
417 /* Family 15h or later */
418 fam15h = 1;
419 }
420
421 if ((model >= 0x8) || fam15h)
422 /* Revision D or later */
423 rev_gte_d = 1;
424
425 if (rev_gte_d)
426 /* Check for dual node capability */
427 if (f3xe8 & 0x20000000)
428 dual_node = 1;
Marc Jones8ae8c882007-12-19 01:32:08 +0000429
430 /* Entries are always added in pairs, the even indices are the 'source'
431 * side closest to the BSP, the odd indices are the 'destination' side
432 */
Marc Jones8ae8c882007-12-19 01:32:08 +0000433 while (currentNode <= pDat->NodesDiscovered)
434 {
435 u32 temp;
436
437 if (currentNode != 0)
438 {
439 /* Set path from BSP to currentNode */
440 routeFromBSP(currentNode, currentNode, pDat);
441
442 /* Set path from BSP to currentNode for currentNode+1 if
443 * currentNode+1 != MAX_NODES
444 */
445 if (currentNode+1 != MAX_NODES)
446 routeFromBSP(currentNode, currentNode+1, pDat);
447
448 /* Configure currentNode to route traffic to the BSP through its
449 * default link
450 */
451 pDat->nb->writeRoutingTable(currentNode, 0, pDat->nb->readDefLnk(currentNode, pDat->nb), pDat->nb);
452 }
453
454 /* Set currentNode's NodeID field to currentNode */
455 pDat->nb->writeNodeID(currentNode, currentNode, pDat->nb);
456
Timothy Pearson7c55f372015-08-02 21:36:24 -0500457 /* Enable routing tables on currentNode */
Marc Jones8ae8c882007-12-19 01:32:08 +0000458 pDat->nb->enableRoutingTables(currentNode, pDat->nb);
459
Timothy Pearson0122afb2015-07-30 14:07:15 -0500460 for (currentLinkID = 0; currentLinkID < pDat->nb->maxLinks; currentLinkID++)
Marc Jones8ae8c882007-12-19 01:32:08 +0000461 {
462 BOOL linkfound;
463 u8 token;
464
Timothy Pearson0122afb2015-07-30 14:07:15 -0500465 if (currentLinkID < 8) {
466 if (dual_node) {
467 if (fam15h)
468 currentLink = currentLinkScanOrder_G34_Fam15[currentLinkID];
469 else
470 currentLink = currentLinkScanOrder_G34_Fam10[currentLinkID];
471 } else {
472 currentLink = currentLinkScanOrder_Default[currentLinkID];
473 }
474 } else {
475 currentLink = currentLinkID;
476 }
477
Marc Jones8ae8c882007-12-19 01:32:08 +0000478 if (pDat->HtBlock->AMD_CB_IgnoreLink && pDat->HtBlock->AMD_CB_IgnoreLink(currentNode, currentLink))
479 continue;
480
481 if (pDat->nb->readTrueLinkFailStatus(currentNode, currentLink, pDat, pDat->nb))
482 continue;
483
484 /* Make sure that the link is connected, coherent, and ready */
485 if (!pDat->nb->verifyLinkIsCoherent(currentNode, currentLink, pDat->nb))
486 continue;
487
488
489 /* Test to see if the currentLink has already been explored */
490 linkfound = FALSE;
491 for (temp = 0; temp < pDat->TotalLinks; temp++)
492 {
493 if ((pDat->PortList[temp*2+1].NodeID == currentNode) &&
494 (pDat->PortList[temp*2+1].Link == currentLink))
495 {
496 linkfound = TRUE;
497 break;
498 }
499 }
500 if (linkfound)
501 {
502 /* We had already expored this link */
503 continue;
504 }
505
506 if (pDat->nb->handleSpecialLinkCase(currentNode, currentLink, pDat, pDat->nb))
507 {
508 continue;
509 }
510
511 /* Modify currentNode's routing table to use currentLink to send
512 * traffic to currentNode+1
513 */
514 pDat->nb->writeRoutingTable(currentNode, currentNode+1, currentLink, pDat->nb);
515
516 /* Check the northbridge of the node we just found, to make sure it is compatible
517 * before doing anything else to it.
518 */
519 if (!pDat->nb->isCompatible(currentNode+1, pDat->nb))
520 {
521 u8 nodeToKill;
522
523 /* Notify BIOS of event (while variables are still the same) */
524 if (pDat->HtBlock->AMD_CB_EventNotify)
525 {
Marc Jones212486e2008-07-17 19:50:37 +0000526 sHtEventCohFamilyFeud evt;
527 evt.eSize = sizeof(sHtEventCohFamilyFeud);
528 evt.node = currentNode;
529 evt.link = currentLink;
530 evt.totalNodes = pDat->NodesDiscovered;
Marc Jones8ae8c882007-12-19 01:32:08 +0000531
532 pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,
533 HT_EVENT_COH_FAMILY_FEUD,
534 (u8 *)&evt);
535 }
536
537 /* If node is not compatible, force boot to 1P
538 * If they are not compatible stop cHT init and:
539 * 1. Disable all cHT links on the BSP
540 * 2. Configure the BSP routing tables as a UP.
541 * 3. Notify main BIOS.
542 */
543 pDat->NodesDiscovered = 0;
544 currentNode = 0;
545 pDat->TotalLinks = 0;
546 /* Abandon our coherent link data structure. At this point there may
547 * be coherent links on the BSP that are not yet in the portList, and
548 * we have to turn them off anyway. So depend on the hardware to tell us.
549 */
550 for (currentLink = 0; currentLink < pDat->nb->maxLinks; currentLink++)
551 {
552 /* Stop all links which are connected, coherent, and ready */
553 if (pDat->nb->verifyLinkIsCoherent(currentNode, currentLink, pDat->nb))
554 pDat->nb->stopLink(currentNode, currentLink, pDat->nb);
555 }
556
557 for (nodeToKill = 0; nodeToKill < pDat->nb->maxNodes; nodeToKill++)
558 {
559 pDat->nb->writeFullRoutingTable(0, nodeToKill, ROUTETOSELF, ROUTETOSELF, 0, pDat->nb);
560 }
561
562 /* End Coherent Discovery */
563 STOP_HERE;
564 break;
565 }
566
567 /* Read token from Current+1 */
568 token = pDat->nb->readToken(currentNode+1, pDat->nb);
569 ASSERT(token <= pDat->NodesDiscovered);
570 if (token == 0)
571 {
572 pDat->NodesDiscovered++;
573 ASSERT(pDat->NodesDiscovered < pDat->nb->maxNodes);
574 /* Check the capability of northbridges against the currently known configuration */
575 if (!pDat->nb->isCapable(currentNode+1, pDat, pDat->nb))
576 {
577 u8 nodeToKill;
578
579 /* Notify BIOS of event */
580 if (pDat->HtBlock->AMD_CB_EventNotify)
581 {
Marc Jones212486e2008-07-17 19:50:37 +0000582 sHtEventCohMpCapMismatch evt;
583 evt.eSize = sizeof(sHtEventCohMpCapMismatch);
584 evt.node = currentNode;
585 evt.link = currentLink;
586 evt.sysMpCap = pDat->sysMpCap;
587 evt.totalNodes = pDat->NodesDiscovered;
Marc Jones8ae8c882007-12-19 01:32:08 +0000588
589 pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,
590 HT_EVENT_COH_MPCAP_MISMATCH,
591 (u8 *)&evt);
592 }
593
594 pDat->NodesDiscovered = 0;
595 currentNode = 0;
596 pDat->TotalLinks = 0;
597
598 for (nodeToKill = 0; nodeToKill < pDat->nb->maxNodes; nodeToKill++)
599 {
600 pDat->nb->writeFullRoutingTable(0, nodeToKill, ROUTETOSELF, ROUTETOSELF, 0, pDat->nb);
601 }
602
603 /* End Coherent Discovery */
604 STOP_HERE;
605 break;
606 }
607
608 token = pDat->NodesDiscovered;
609 pDat->nb->writeToken(currentNode+1, token, pDat->nb);
610 /* Inform that we have discovered a node, so that logical id to
611 * socket mapping info can be recorded.
612 */
613 if (pDat->HtBlock->AMD_CB_EventNotify)
614 {
Marc Jones212486e2008-07-17 19:50:37 +0000615 sHtEventCohNodeDiscovered evt;
616 evt.eSize = sizeof(sHtEventCohNodeDiscovered);
617 evt.node = currentNode;
618 evt.link = currentLink;
619 evt.newNode = token;
Marc Jones8ae8c882007-12-19 01:32:08 +0000620
621 pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_INFO,
622 HT_EVENT_COH_NODE_DISCOVERED,
623 (u8 *)&evt);
624 }
625 }
626
627 if (pDat->TotalLinks == MAX_PLATFORM_LINKS)
628 {
629 /*
630 * Exceeded our capacity to describe all coherent links found in the system.
631 * Error strategy:
632 * Auto recovery is not possible because data space is already all used.
633 * If the callback is not implemented or returns we will continue to initialize
634 * the fabric we are capable of representing, adding no more nodes or links.
635 * This should yield a bootable topology, but likely not the one intended.
636 * We cannot continue discovery, there may not be any way to route a new
637 * node back to the BSP if we can't add links to our representation of the system.
638 */
639 if (pDat->HtBlock->AMD_CB_EventNotify)
640 {
Marc Jones212486e2008-07-17 19:50:37 +0000641 sHtEventCohLinkExceed evt;
642 evt.eSize = sizeof(sHtEventCohLinkExceed);
643 evt.node = currentNode;
644 evt.link = currentLink;
645 evt.targetNode = token;
646 evt.totalNodes = pDat->NodesDiscovered;
647 evt.maxLinks = pDat->nb->maxLinks;
Marc Jones8ae8c882007-12-19 01:32:08 +0000648
649 pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,
650 HT_EVENT_COH_LINK_EXCEED,
651 (u8 *)&evt);
652 }
653 /* Force link and node loops to halt */
654 STOP_HERE;
655 currentNode = pDat->NodesDiscovered;
656 break;
657 }
658
659 pDat->PortList[pDat->TotalLinks*2].Type = PORTLIST_TYPE_CPU;
660 pDat->PortList[pDat->TotalLinks*2].Link = currentLink;
661 pDat->PortList[pDat->TotalLinks*2].NodeID = currentNode;
662
663 pDat->PortList[pDat->TotalLinks*2+1].Type = PORTLIST_TYPE_CPU;
664 pDat->PortList[pDat->TotalLinks*2+1].Link = pDat->nb->readDefLnk(currentNode+1, pDat->nb);
665 pDat->PortList[pDat->TotalLinks*2+1].NodeID = token;
666
667 pDat->TotalLinks++;
668
Elyes HAOUASa8131602016-09-19 10:27:57 -0600669 if (!pDat->sysMatrix[currentNode][token])
Marc Jones8ae8c882007-12-19 01:32:08 +0000670 {
671 pDat->sysDegree[currentNode]++;
672 pDat->sysDegree[token]++;
673 pDat->sysMatrix[currentNode][token] = TRUE;
674 pDat->sysMatrix[token][currentNode] = TRUE;
675 }
676 }
677 currentNode++;
678 }
679}
680
681
682/***************************************************************************
683 *** ISOMORPHISM BASED ROUTING TABLE GENERATION CODE ***
684 ***************************************************************************/
685
686/*----------------------------------------------------------------------------------------
687 * BOOL
688 * isoMorph(u8 i, sMainData *pDat)
689 *
690 * Description:
691 * Is graphA isomorphic to graphB?
692 * if this function returns true, then Perm will contain the permutation
693 * required to transform graphB into graphA.
694 * We also use the degree of each node, that is the number of connections it has, to
695 * speed up rejection of non-isomorphic graphs (if there is a node in graphA with n
696 * connections, there must be at least one unmatched in graphB with n connections).
697 *
698 * Parameters:
699 * @param[in] u8 i = the discovered node which we are trying to match
700 * with a permutation the topology
701 * @param[in]/@param[out] sMainData* pDat = our global state, degree and adjacency matrix,
702 * output a permutation if successful
703 * @param[out] BOOL results = the graphs are (or are not) isomorphic
704 * ---------------------------------------------------------------------------------------
705 */
Myles Watson075fbe82010-04-15 05:19:29 +0000706static BOOL isoMorph(u8 i, sMainData *pDat)
Marc Jones8ae8c882007-12-19 01:32:08 +0000707{
708 u8 j, k;
709 u8 nodecnt;
710
711 /* We have only been called if nodecnt == pSelected->size ! */
712 nodecnt = pDat->NodesDiscovered+1;
713
714 if (i != nodecnt)
715 {
Marc Jonesaee07962008-07-16 21:09:31 +0000716 /* Keep building the permutation */
Marc Jones8ae8c882007-12-19 01:32:08 +0000717 for (j = 0; j < nodecnt; j++)
718 {
Marc Jonesaee07962008-07-16 21:09:31 +0000719 /* Make sure the degree matches */
Marc Jones8ae8c882007-12-19 01:32:08 +0000720 if (pDat->sysDegree[i] != pDat->dbDegree[j])
721 continue;
722
Marc Jonesaee07962008-07-16 21:09:31 +0000723 /* Make sure that j hasn't been used yet (ought to use a "used" */
724 /* array instead, might be faster) */
Marc Jones8ae8c882007-12-19 01:32:08 +0000725 for (k = 0; k < i; k++)
726 {
727 if (pDat->Perm[k] == j)
728 break;
729 }
730 if (k != i)
731 continue;
732 pDat->Perm[i] = j;
733 if (isoMorph(i+1, pDat))
734 return TRUE;
735 }
736 return FALSE;
737 } else {
Marc Jonesaee07962008-07-16 21:09:31 +0000738 /* Test to see if the permutation is isomorphic */
Marc Jones8ae8c882007-12-19 01:32:08 +0000739 for (j = 0; j < nodecnt; j++)
740 {
741 for (k = 0; k < nodecnt; k++)
742 {
Elyes HAOUASa8131602016-09-19 10:27:57 -0600743 if (pDat->sysMatrix[j][k] !=
744 pDat->dbMatrix[pDat->Perm[j]][pDat->Perm[k]])
Marc Jones8ae8c882007-12-19 01:32:08 +0000745 return FALSE;
746 }
747 }
748 return TRUE;
749 }
750}
751
752
753/*----------------------------------------------------------------------------------------
754 * void
755 * lookupComputeAndLoadRoutingTables(sMainData *pDat)
756 *
757 * Description:
758 * Using the description of the fabric topology we discovered, try to find a match
759 * among the supported topologies. A supported topology description matches
760 * the discovered fabric if the nodes can be matched in such a way that all the nodes connected
761 * in one set are exactly the nodes connected in the other (formally, that the graphs are
762 * isomorphic). Which links are used is not really important to matching. If the graphs
763 * match, then there is a permutation of one that translates the node positions and linkages
764 * to the other.
765 *
766 * In order to make the isomorphism test efficient, we test for matched number of nodes
767 * (a 4 node fabric is not isomorphic to a 2 node topology), and provide degrees of nodes
768 * to the isomorphism test.
769 *
770 * The generic routing table solution for any topology is predetermined and represented
771 * as part of the topology. The permutation we computed tells us how to interpret the
772 * routing onto the fabric we discovered. We do this working backward from the last
773 * node discovered to the BSP, writing the routing tables as we go.
774 *
775 * Parameters:
776 * @param[in] sMainData* pDat = our global state, the discovered fabric,
777 * @param[out] degree matrix, permutation
778 * ---------------------------------------------------------------------------------------
779 */
Myles Watson075fbe82010-04-15 05:19:29 +0000780static void lookupComputeAndLoadRoutingTables(sMainData *pDat)
Marc Jones8ae8c882007-12-19 01:32:08 +0000781{
782 u8 **pTopologyList;
783 u8 *pSelected;
784
785 int i, j, k, size;
786
787 size = pDat->NodesDiscovered + 1;
788 /* Use the provided topology list or the internal, default one. */
789 pTopologyList = pDat->HtBlock->topolist;
790 if (pTopologyList == NULL)
791 {
792 getAmdTopolist(&pTopologyList);
793 }
794
795 pSelected = *pTopologyList;
796 while (pSelected != NULL)
797 {
798 if (graphHowManyNodes(pSelected) == size)
799 {
Marc Jonesaee07962008-07-16 21:09:31 +0000800 /* Build Degree vector and Adjency Matrix for this entry */
Marc Jones8ae8c882007-12-19 01:32:08 +0000801 for (i = 0; i < size; i++)
802 {
803 pDat->dbDegree[i] = 0;
804 for (j = 0; j < size; j++)
805 {
806 if (graphIsAdjacent(pSelected, i, j))
807 {
808 pDat->dbMatrix[i][j] = 1;
809 pDat->dbDegree[i]++;
810 }
811 else
812 {
813 pDat->dbMatrix[i][j] = 0;
814 }
815 }
816 }
817 if (isoMorph(0, pDat))
Marc Jonesaee07962008-07-16 21:09:31 +0000818 break; /* A matching topology was found */
Marc Jones8ae8c882007-12-19 01:32:08 +0000819 }
820
821 pTopologyList++;
822 pSelected = *pTopologyList;
823 }
824
825 if (pSelected != NULL)
826 {
Marc Jonesaee07962008-07-16 21:09:31 +0000827 /* Compute the reverse Permutation */
Marc Jones8ae8c882007-12-19 01:32:08 +0000828 for (i = 0; i < size; i++)
829 {
830 pDat->ReversePerm[pDat->Perm[i]] = i;
831 }
832
Marc Jonesaee07962008-07-16 21:09:31 +0000833 /* Start with the last discovered node, and move towards the BSP */
Marc Jones8ae8c882007-12-19 01:32:08 +0000834 for (i = size-1; i >= 0; i--)
835 {
836 for (j = 0; j < size; j++)
837 {
838 u8 ReqTargetLink, RspTargetLink;
839 u8 ReqTargetNode, RspTargetNode;
840
841 u8 AbstractBcTargetNodes = graphGetBc(pSelected, pDat->Perm[i], pDat->Perm[j]);
842 u32 BcTargetLinks = 0;
843
844 for (k = 0; k < MAX_NODES; k++)
845 {
846 if (AbstractBcTargetNodes & ((u32)1<<k))
847 {
848 BcTargetLinks |= (u32)1 << convertNodeToLink(i, pDat->ReversePerm[k], pDat);
849 }
850 }
851
852 if (i == j)
853 {
854 ReqTargetLink = ROUTETOSELF;
855 RspTargetLink = ROUTETOSELF;
856 }
857 else
858 {
859 ReqTargetNode = graphGetReq(pSelected, pDat->Perm[i], pDat->Perm[j]);
860 ReqTargetLink = convertNodeToLink(i, pDat->ReversePerm[ReqTargetNode], pDat);
861
862 RspTargetNode = graphGetRsp(pSelected, pDat->Perm[i], pDat->Perm[j]);
863 RspTargetLink = convertNodeToLink(i, pDat->ReversePerm[RspTargetNode], pDat);
864 }
865
866 pDat->nb->writeFullRoutingTable(i, j, ReqTargetLink, RspTargetLink, BcTargetLinks, pDat->nb);
867 }
868 /* Clean up discovery 'footprint' that otherwise remains in the routing table. It didn't hurt
869 * anything, but might cause confusion during debug and validation. Do this by setting the
870 * route back to all self routes. Since it's the node that would be one more than actually installed,
871 * this only applies if less than maxNodes were found.
872 */
873 if (size < pDat->nb->maxNodes)
874 {
875 pDat->nb->writeFullRoutingTable(i, size, ROUTETOSELF, ROUTETOSELF, 0, pDat->nb);
876 }
877 }
878
879 }
880 else
881 {
882 /*
883 * No Matching Topology was found
884 * Error Strategy:
885 * Auto recovery doesn't seem likely, Force boot as 1P.
886 * For reporting, logging, provide number of nodes
887 * If not implemented or returns, boot as BSP uniprocessor.
888 */
889 if (pDat->HtBlock->AMD_CB_EventNotify)
890 {
Marc Jones212486e2008-07-17 19:50:37 +0000891 sHtEventCohNoTopology evt;
892 evt.eSize = sizeof(sHtEventCohNoTopology);
893 evt.totalNodes = pDat->NodesDiscovered;
Marc Jones8ae8c882007-12-19 01:32:08 +0000894
895 pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,
896 HT_EVENT_COH_NO_TOPOLOGY,
897 (u8 *)&evt);
898 }
899 STOP_HERE;
900 /* Force 1P */
901 pDat->NodesDiscovered = 0;
902 pDat->TotalLinks = 0;
903 pDat->nb->enableRoutingTables(0, pDat->nb);
904 }
905}
906#endif /* HT_BUILD_NC_ONLY */
907
908
909/*----------------------------------------------------------------------------------------
910 * void
911 * finializeCoherentInit(sMainData *pDat)
912 *
913 * Description:
914 * Find the total number of cores and update the number of nodes and cores in all cpus.
Elyes HAOUAS15279a92016-07-28 21:05:26 +0200915 * Limit CPU config access to installed cpus.
Marc Jones8ae8c882007-12-19 01:32:08 +0000916 *
917 * Parameters:
918 * @param[in] sMainData* pDat = our global state, number of nodes discovered.
919 * ---------------------------------------------------------------------------------------
920 */
Myles Watson075fbe82010-04-15 05:19:29 +0000921static void finializeCoherentInit(sMainData *pDat)
Marc Jones8ae8c882007-12-19 01:32:08 +0000922{
923 u8 curNode;
924
925 u8 totalCores = 0;
926 for (curNode = 0; curNode < pDat->NodesDiscovered+1; curNode++)
927 {
928 totalCores += pDat->nb->getNumCoresOnNode(curNode, pDat->nb);
929 }
930
931 for (curNode = 0; curNode < pDat->NodesDiscovered+1; curNode++)
932 {
933 pDat->nb->setTotalNodesAndCores(curNode, pDat->NodesDiscovered+1, totalCores, pDat->nb);
934 }
935
936 for (curNode = 0; curNode < pDat->NodesDiscovered+1; curNode++)
937 {
938 pDat->nb->limitNodes(curNode, pDat->nb);
939 }
940
941}
942
943/*----------------------------------------------------------------------------------------
944 * void
945 * coherentInit(sMainData *pDat)
946 *
947 * Description:
948 * Perform discovery and initialization of the coherent fabric.
949 *
950 * Parameters:
951 * @param[in] sMainData* pDat = our global state
952 * ---------------------------------------------------------------------------------------
953 */
Myles Watson075fbe82010-04-15 05:19:29 +0000954static void coherentInit(sMainData *pDat)
Marc Jones8ae8c882007-12-19 01:32:08 +0000955{
Marc Jones8ae8c882007-12-19 01:32:08 +0000956#ifdef HT_BUILD_NC_ONLY
957 /* Replace discovery process with:
958 * No other nodes, no coherent links
959 * Enable routing tables on currentNode, for power on self route
960 */
961 pDat->NodesDiscovered = 0;
962 pDat->TotalLinks = 0;
963 pDat->nb->enableRoutingTables(0, pDat->nb);
964#else
Xavi Drudis Ferrane660f042010-08-17 06:12:59 +0000965 u8 i, j;
966
Marc Jones8ae8c882007-12-19 01:32:08 +0000967 pDat->NodesDiscovered = 0;
968 pDat->TotalLinks = 0;
969 for (i = 0; i < MAX_NODES; i++)
970 {
971 pDat->sysDegree[i] = 0;
972 for (j = 0; j < MAX_NODES; j++)
973 {
974 pDat->sysMatrix[i][j] = 0;
975 }
976 }
977
978 htDiscoveryFloodFill(pDat);
979 lookupComputeAndLoadRoutingTables(pDat);
980#endif
981 finializeCoherentInit(pDat);
982}
983
984/***************************************************************************
985 *** Non-coherent init code ***
986 *** Algorithms ***
987 ***************************************************************************/
988/*----------------------------------------------------------------------------------------
989 * void
990 * processLink(u8 node, u8 link, sMainData *pDat)
991 *
992 * Description:
993 * Process a non-coherent link, enabling a range of bus numbers, and setting the device
994 * ID for all devices found
995 *
996 * Parameters:
997 * @param[in] u8 node = Node on which to process nc init
998 * @param[in] u8 link = The non-coherent link on that node
999 * @param[in] sMainData* pDat = our global state
1000 * ---------------------------------------------------------------------------------------
1001 */
Myles Watson075fbe82010-04-15 05:19:29 +00001002static void processLink(u8 node, u8 link, sMainData *pDat)
Marc Jones8ae8c882007-12-19 01:32:08 +00001003{
1004 u8 secBus, subBus;
1005 u32 currentBUID;
1006 u32 temp;
1007 u32 unitIDcnt;
1008 SBDFO currentPtr;
1009 u8 depth;
Patrick Georgi86224f62010-09-25 17:24:10 +00001010 const u8 *pSwapPtr;
Marc Jones8ae8c882007-12-19 01:32:08 +00001011
1012 SBDFO lastSBDFO = ILLEGAL_SBDFO;
1013 u8 lastLink = 0;
1014
1015 ASSERT(node < pDat->nb->maxNodes && link < pDat->nb->maxLinks);
1016
1017 if ((pDat->HtBlock->AMD_CB_OverrideBusNumbers == NULL)
1018 || !pDat->HtBlock->AMD_CB_OverrideBusNumbers(node, link, &secBus, &subBus))
1019 {
1020 /* Assign Bus numbers */
1021 if (pDat->AutoBusCurrent >= pDat->HtBlock->AutoBusMax)
1022 {
1023 /* If we run out of Bus Numbers notify, if call back unimplemented or if it
1024 * returns, skip this chain
1025 */
1026 if (pDat->HtBlock->AMD_CB_EventNotify)
1027 {
Marc Jones212486e2008-07-17 19:50:37 +00001028 sHTEventNcohBusMaxExceed evt;
1029 evt.eSize = sizeof(sHTEventNcohBusMaxExceed);
1030 evt.node = node;
1031 evt.link = link;
1032 evt.bus = pDat->AutoBusCurrent;
Marc Jones8ae8c882007-12-19 01:32:08 +00001033
1034 pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,HT_EVENT_NCOH_BUS_MAX_EXCEED,(u8 *)&evt);
1035 }
1036 STOP_HERE;
1037 return;
1038 }
1039
1040 if (pDat->UsedCfgMapEntires >= 4)
1041 {
1042 /* If we have used all the PCI Config maps we can't add another chain.
1043 * Notify and if call back is unimplemented or returns, skip this chain.
1044 */
1045 if (pDat->HtBlock->AMD_CB_EventNotify)
1046 {
Marc Jones212486e2008-07-17 19:50:37 +00001047 sHtEventNcohCfgMapExceed evt;
1048 evt.eSize = sizeof(sHtEventNcohCfgMapExceed);
1049 evt.node = node;
1050 evt.link = link;
Marc Jones8ae8c882007-12-19 01:32:08 +00001051
1052 pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,
1053 HT_EVENT_NCOH_CFG_MAP_EXCEED,
1054 (u8 *)&evt);
1055 }
1056 STOP_HERE;
1057 return;
1058 }
1059
1060 secBus = pDat->AutoBusCurrent;
1061 subBus = secBus + pDat->HtBlock->AutoBusIncrement-1;
1062 pDat->AutoBusCurrent += pDat->HtBlock->AutoBusIncrement;
1063 }
1064
1065 pDat->nb->setCFGAddrMap(pDat->UsedCfgMapEntires, secBus, subBus, node, link, pDat, pDat->nb);
1066 pDat->UsedCfgMapEntires++;
1067
1068 if ((pDat->HtBlock->AMD_CB_ManualBUIDSwapList != NULL)
1069 && pDat->HtBlock->AMD_CB_ManualBUIDSwapList(node, link, &pSwapPtr))
1070 {
1071 /* Manual non-coherent BUID assignment */
Timothy Pearsonc139c422015-01-23 20:27:26 -06001072 currentBUID = 1;
Marc Jones8ae8c882007-12-19 01:32:08 +00001073
1074 /* Assign BUID's per manual override */
1075 while (*pSwapPtr != 0xFF)
1076 {
1077 currentPtr = MAKE_SBDFO(0, secBus, *pSwapPtr, 0, 0);
1078 pSwapPtr++;
1079
1080 do
1081 {
1082 AmdPCIFindNextCap(&currentPtr);
1083 ASSERT(currentPtr != ILLEGAL_SBDFO);
1084 AmdPCIRead(currentPtr, &temp);
1085 } while (!IS_HT_SLAVE_CAPABILITY(temp));
1086
1087 currentBUID = *pSwapPtr;
1088 pSwapPtr++;
1089 AmdPCIWriteBits(currentPtr, 20, 16, &currentBUID);
1090 }
1091
1092 /* Build chain of devices */
1093 depth = 0;
1094 pSwapPtr++;
1095 while (*pSwapPtr != 0xFF)
1096 {
1097 pDat->PortList[pDat->TotalLinks*2].NodeID = node;
1098 if (depth == 0)
1099 {
1100 pDat->PortList[pDat->TotalLinks*2].Type = PORTLIST_TYPE_CPU;
1101 pDat->PortList[pDat->TotalLinks*2].Link = link;
1102 }
1103 else
1104 {
1105 pDat->PortList[pDat->TotalLinks*2].Type = PORTLIST_TYPE_IO;
1106 pDat->PortList[pDat->TotalLinks*2].Link = 1-lastLink;
1107 pDat->PortList[pDat->TotalLinks*2].HostLink = link;
1108 pDat->PortList[pDat->TotalLinks*2].HostDepth = depth-1;
1109 pDat->PortList[pDat->TotalLinks*2].Pointer = lastSBDFO;
1110 }
1111
1112 pDat->PortList[pDat->TotalLinks*2+1].Type = PORTLIST_TYPE_IO;
1113 pDat->PortList[pDat->TotalLinks*2+1].NodeID = node;
1114 pDat->PortList[pDat->TotalLinks*2+1].HostLink = link;
1115 pDat->PortList[pDat->TotalLinks*2+1].HostDepth = depth;
1116
1117 currentPtr = MAKE_SBDFO(0, secBus, (*pSwapPtr & 0x3F), 0, 0);
1118 do
1119 {
1120 AmdPCIFindNextCap(&currentPtr);
1121 ASSERT(currentPtr != ILLEGAL_SBDFO);
1122 AmdPCIRead(currentPtr, &temp);
1123 } while (!IS_HT_SLAVE_CAPABILITY(temp));
1124 pDat->PortList[pDat->TotalLinks*2+1].Pointer = currentPtr;
1125 lastSBDFO = currentPtr;
1126
1127 /* Bit 6 indicates whether orientation override is desired.
1128 * Bit 7 indicates the upstream link if overriding.
1129 */
1130 /* assert catches at least the one known incorrect setting */
1131 ASSERT ((*pSwapPtr & 0x40) || (!(*pSwapPtr & 0x80)));
1132 if (*pSwapPtr & 0x40)
1133 {
1134 /* Override the device's orientation */
1135 lastLink = *pSwapPtr >> 7;
1136 }
1137 else
1138 {
1139 /* Detect the device's orientation */
1140 AmdPCIReadBits(currentPtr, 26, 26, &temp);
1141 lastLink = (u8)temp;
1142 }
1143 pDat->PortList[pDat->TotalLinks*2+1].Link = lastLink;
1144
1145 depth++;
1146 pDat->TotalLinks++;
1147 pSwapPtr++;
1148 }
1149 }
1150 else
1151 {
1152 /* Automatic non-coherent device detection */
1153 depth = 0;
1154 currentBUID = 1;
1155 while (1)
1156 {
1157 currentPtr = MAKE_SBDFO(0, secBus, 0, 0, 0);
1158
1159 AmdPCIRead(currentPtr, &temp);
1160 if (temp == 0xFFFFFFFF)
1161 /* No device found at currentPtr */
1162 break;
1163
1164 if (pDat->TotalLinks == MAX_PLATFORM_LINKS)
1165 {
1166 /*
1167 * Exceeded our capacity to describe all non-coherent links found in the system.
1168 * Error strategy:
1169 * Auto recovery is not possible because data space is already all used.
1170 */
1171 if (pDat->HtBlock->AMD_CB_EventNotify)
1172 {
Marc Jones212486e2008-07-17 19:50:37 +00001173 sHtEventNcohLinkExceed evt;
1174 evt.eSize = sizeof(sHtEventNcohLinkExceed);
1175 evt.node = node;
1176 evt.link = link;
1177 evt.depth = depth;
1178 evt.maxLinks = pDat->nb->maxLinks;
Marc Jones8ae8c882007-12-19 01:32:08 +00001179
1180 pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,
1181 HT_EVENT_NCOH_LINK_EXCEED,
1182 (u8 *)&evt);
1183 }
1184 /* Force link loop to halt */
1185 STOP_HERE;
1186 break;
1187 }
1188
1189 pDat->PortList[pDat->TotalLinks*2].NodeID = node;
1190 if (depth == 0)
1191 {
1192 pDat->PortList[pDat->TotalLinks*2].Type = PORTLIST_TYPE_CPU;
1193 pDat->PortList[pDat->TotalLinks*2].Link = link;
1194 }
1195 else
1196 {
1197 pDat->PortList[pDat->TotalLinks*2].Type = PORTLIST_TYPE_IO;
1198 pDat->PortList[pDat->TotalLinks*2].Link = 1-lastLink;
1199 pDat->PortList[pDat->TotalLinks*2].HostLink = link;
1200 pDat->PortList[pDat->TotalLinks*2].HostDepth = depth-1;
1201 pDat->PortList[pDat->TotalLinks*2].Pointer = lastSBDFO;
1202 }
1203
1204 pDat->PortList[pDat->TotalLinks*2+1].Type = PORTLIST_TYPE_IO;
1205 pDat->PortList[pDat->TotalLinks*2+1].NodeID = node;
1206 pDat->PortList[pDat->TotalLinks*2+1].HostLink = link;
1207 pDat->PortList[pDat->TotalLinks*2+1].HostDepth = depth;
1208
1209 do
1210 {
1211 AmdPCIFindNextCap(&currentPtr);
1212 ASSERT(currentPtr != ILLEGAL_SBDFO);
1213 AmdPCIRead(currentPtr, &temp);
1214 } while (!IS_HT_SLAVE_CAPABILITY(temp));
1215
1216 AmdPCIReadBits(currentPtr, 25, 21, &unitIDcnt);
1217 if ((unitIDcnt + currentBUID > 31) || ((secBus == 0) && (unitIDcnt + currentBUID > 24)))
1218 {
1219 /* An error handler for the case where we run out of BUID's on a chain */
1220 if (pDat->HtBlock->AMD_CB_EventNotify)
1221 {
Marc Jones212486e2008-07-17 19:50:37 +00001222 sHtEventNcohBuidExceed evt;
1223 evt.eSize = sizeof(sHtEventNcohBuidExceed);
1224 evt.node = node;
1225 evt.link = link;
1226 evt.depth = depth;
1227 evt.currentBUID = (uint8)currentBUID;
1228 evt.unitCount = (uint8)unitIDcnt;
Marc Jones8ae8c882007-12-19 01:32:08 +00001229
1230 pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,HT_EVENT_NCOH_BUID_EXCEED,(u8 *)&evt);
1231 }
1232 STOP_HERE;
1233 break;
1234 }
1235 AmdPCIWriteBits(currentPtr, 20, 16, &currentBUID);
1236
1237
1238 currentPtr += MAKE_SBDFO(0, 0, currentBUID, 0, 0);
1239 AmdPCIReadBits(currentPtr, 20, 16, &temp);
1240 if (temp != currentBUID)
1241 {
1242 /* An error handler for this critical error */
1243 if (pDat->HtBlock->AMD_CB_EventNotify)
1244 {
Marc Jones212486e2008-07-17 19:50:37 +00001245 sHtEventNcohDeviceFailed evt;
1246 evt.eSize = sizeof(sHtEventNcohDeviceFailed);
1247 evt.node = node;
1248 evt.link = link;
1249 evt.depth = depth;
1250 evt.attemptedBUID = (uint8)currentBUID;
Marc Jones8ae8c882007-12-19 01:32:08 +00001251
1252 pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_ERROR,HT_EVENT_NCOH_DEVICE_FAILED,(u8 *)&evt);
1253 }
1254 STOP_HERE;
1255 break;
1256 }
1257
1258 AmdPCIReadBits(currentPtr, 26, 26, &temp);
1259 pDat->PortList[pDat->TotalLinks*2+1].Link = (u8)temp;
1260 pDat->PortList[pDat->TotalLinks*2+1].Pointer = currentPtr;
1261
1262 lastLink = (u8)temp;
1263 lastSBDFO = currentPtr;
1264
1265 depth++;
1266 pDat->TotalLinks++;
1267 currentBUID += unitIDcnt;
1268 }
1269 if (pDat->HtBlock->AMD_CB_EventNotify)
1270 {
1271 /* Provide information on automatic device results */
Marc Jones212486e2008-07-17 19:50:37 +00001272 sHtEventNcohAutoDepth evt;
1273 evt.eSize = sizeof(sHtEventNcohAutoDepth);
1274 evt.node = node;
1275 evt.link = link;
1276 evt.depth = (depth - 1);
Marc Jones8ae8c882007-12-19 01:32:08 +00001277
1278 pDat->HtBlock->AMD_CB_EventNotify(HT_EVENT_CLASS_INFO,HT_EVENT_NCOH_AUTO_DEPTH,(u8 *)&evt);
1279 }
1280 }
1281}
1282
1283
1284/*----------------------------------------------------------------------------------------
1285 * void
1286 * ncInit(sMainData *pDat)
1287 *
1288 * Description:
1289 * Initialize the non-coherent fabric. Begin with the compat link on the BSP, then
1290 * find and initialize all other non-coherent chains.
1291 *
1292 * Parameters:
1293 * @param[in] sMainData* pDat = our global state
1294 * ---------------------------------------------------------------------------------------
1295 */
Myles Watson075fbe82010-04-15 05:19:29 +00001296static void ncInit(sMainData *pDat)
Marc Jones8ae8c882007-12-19 01:32:08 +00001297{
1298 u8 node, link;
1299 u8 compatLink;
1300
1301 compatLink = pDat->nb->readSbLink(pDat->nb);
1302 processLink(0, compatLink, pDat);
1303
1304 for (node = 0; node <= pDat->NodesDiscovered; node++)
1305 {
1306 for (link = 0; link < pDat->nb->maxLinks; link++)
1307 {
1308 if (pDat->HtBlock->AMD_CB_IgnoreLink && pDat->HtBlock->AMD_CB_IgnoreLink(node, link))
Marc Jonesaee07962008-07-16 21:09:31 +00001309 continue; /* Skip the link */
Marc Jones8ae8c882007-12-19 01:32:08 +00001310
1311 if (node == 0 && link == compatLink)
1312 continue;
1313
1314 if (pDat->nb->readTrueLinkFailStatus(node, link, pDat, pDat->nb))
1315 continue;
1316
1317 if (pDat->nb->verifyLinkIsNonCoherent(node, link, pDat->nb))
1318 processLink(node, link, pDat);
1319 }
1320 }
1321}
1322
1323/***************************************************************************
1324 *** Link Optimization ***
1325 ***************************************************************************/
1326
1327/*----------------------------------------------------------------------------------------
1328 * void
1329 * regangLinks(sMainData *pDat)
1330 *
1331 * Description:
1332 * Test the sublinks of a link to see if they qualify to be reganged. If they do,
1333 * update the port list data to indicate that this should be done. Note that no
1334 * actual hardware state is changed in this routine.
1335 *
1336 * Parameters:
1337 * @param[in,out] sMainData* pDat = our global state
1338 * ---------------------------------------------------------------------------------------
1339 */
Myles Watson075fbe82010-04-15 05:19:29 +00001340static void regangLinks(sMainData *pDat)
Marc Jones8ae8c882007-12-19 01:32:08 +00001341{
1342#ifndef HT_BUILD_NC_ONLY
1343 u8 i, j;
1344 for (i = 0; i < pDat->TotalLinks*2; i += 2)
1345 {
Marc Jonesaee07962008-07-16 21:09:31 +00001346 ASSERT(pDat->PortList[i].Type < 2 && pDat->PortList[i].Link < pDat->nb->maxLinks); /* Data validation */
1347 ASSERT(pDat->PortList[i+1].Type < 2 && pDat->PortList[i+1].Link < pDat->nb->maxLinks); /* data validation */
1348 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 +00001349
1350 /* Regang is false unless we pass all conditions below */
1351 pDat->PortList[i].SelRegang = FALSE;
1352 pDat->PortList[i+1].SelRegang = FALSE;
1353
Elyes HAOUASa8131602016-09-19 10:27:57 -06001354 if ((pDat->PortList[i].Type != PORTLIST_TYPE_CPU) || (pDat->PortList[i+1].Type != PORTLIST_TYPE_CPU))
Elyes HAOUAS15279a92016-07-28 21:05:26 +02001355 continue; /* Only process CPU to CPU links */
Marc Jones8ae8c882007-12-19 01:32:08 +00001356
1357 for (j = i+2; j < pDat->TotalLinks*2; j += 2)
1358 {
Elyes HAOUASa8131602016-09-19 10:27:57 -06001359 if ((pDat->PortList[j].Type != PORTLIST_TYPE_CPU) || (pDat->PortList[j+1].Type != PORTLIST_TYPE_CPU))
Elyes HAOUAS15279a92016-07-28 21:05:26 +02001360 continue; /* Only process CPU to CPU links */
Marc Jones8ae8c882007-12-19 01:32:08 +00001361
1362 if (pDat->PortList[i].NodeID != pDat->PortList[j].NodeID)
Marc Jonesaee07962008-07-16 21:09:31 +00001363 continue; /* Links must be from the same source */
Marc Jones8ae8c882007-12-19 01:32:08 +00001364
1365 if (pDat->PortList[i+1].NodeID != pDat->PortList[j+1].NodeID)
Marc Jonesaee07962008-07-16 21:09:31 +00001366 continue; /* Link must be to the same target */
Marc Jones8ae8c882007-12-19 01:32:08 +00001367
1368 if ((pDat->PortList[i].Link & 3) != (pDat->PortList[j].Link & 3))
Marc Jonesaee07962008-07-16 21:09:31 +00001369 continue; /* Ensure same source base port */
Marc Jones8ae8c882007-12-19 01:32:08 +00001370
1371 if ((pDat->PortList[i+1].Link & 3) != (pDat->PortList[j+1].Link & 3))
Marc Jonesaee07962008-07-16 21:09:31 +00001372 continue; /* Ensure same destination base port */
Marc Jones8ae8c882007-12-19 01:32:08 +00001373
1374 if ((pDat->PortList[i].Link & 4) != (pDat->PortList[i+1].Link & 4))
Marc Jonesaee07962008-07-16 21:09:31 +00001375 continue; /* Ensure sublink0 routes to sublink0 */
Marc Jones8ae8c882007-12-19 01:32:08 +00001376
Marc Jonesaee07962008-07-16 21:09:31 +00001377 ASSERT((pDat->PortList[j].Link & 4) == (pDat->PortList[j+1].Link & 4)); /* (therefore sublink1 routes to sublink1) */
Marc Jones8ae8c882007-12-19 01:32:08 +00001378
1379 if (pDat->HtBlock->AMD_CB_SkipRegang &&
Marc Jonesaee07962008-07-16 21:09:31 +00001380 pDat->HtBlock->AMD_CB_SkipRegang(pDat->PortList[i].NodeID,
Marc Jones8ae8c882007-12-19 01:32:08 +00001381 pDat->PortList[i].Link & 0x03,
1382 pDat->PortList[i+1].NodeID,
1383 pDat->PortList[i+1].Link & 0x03))
1384 {
Marc Jonesaee07962008-07-16 21:09:31 +00001385 continue; /* Skip regang */
Marc Jones8ae8c882007-12-19 01:32:08 +00001386 }
1387
1388
Marc Jonesaee07962008-07-16 21:09:31 +00001389 pDat->PortList[i].Link &= 0x03; /* Force to point to sublink0 */
Marc Jones8ae8c882007-12-19 01:32:08 +00001390 pDat->PortList[i+1].Link &= 0x03;
Marc Jonesaee07962008-07-16 21:09:31 +00001391 pDat->PortList[i].SelRegang = TRUE; /* Enable link reganging */
Marc Jones8ae8c882007-12-19 01:32:08 +00001392 pDat->PortList[i+1].SelRegang = TRUE;
1393 pDat->PortList[i].PrvWidthOutCap = HT_WIDTH_16_BITS;
1394 pDat->PortList[i+1].PrvWidthOutCap = HT_WIDTH_16_BITS;
1395 pDat->PortList[i].PrvWidthInCap = HT_WIDTH_16_BITS;
1396 pDat->PortList[i+1].PrvWidthInCap = HT_WIDTH_16_BITS;
1397
Marc Jonesaee07962008-07-16 21:09:31 +00001398 /* Delete PortList[j, j+1], slow but easy to debug implementation */
Marc Jones8ae8c882007-12-19 01:32:08 +00001399 pDat->TotalLinks--;
1400 Amdmemcpy(&(pDat->PortList[j]), &(pDat->PortList[j+2]), sizeof(sPortDescriptor)*(pDat->TotalLinks*2-j));
1401 Amdmemset(&(pDat->PortList[pDat->TotalLinks*2]), INVALID_LINK, sizeof(sPortDescriptor)*2);
1402
Marc Jonesaee07962008-07-16 21:09:31 +00001403 /* //High performance, but would make debuging harder due to 'shuffling' of the records */
1404 /* //Amdmemcpy(PortList[TotalPorts-2], PortList[j], SIZEOF(sPortDescriptor)*2); */
1405 /* //TotalPorts -=2; */
Marc Jones8ae8c882007-12-19 01:32:08 +00001406
Marc Jonesaee07962008-07-16 21:09:31 +00001407 break; /* Exit loop, advance to PortList[i+2] */
Marc Jones8ae8c882007-12-19 01:32:08 +00001408 }
1409 }
1410#endif /* HT_BUILD_NC_ONLY */
1411}
1412
Timothy Pearson50001b82015-08-11 17:47:48 -05001413static void detectIoLinkIsochronousCapable(sMainData *pDat)
1414{
1415 uint8_t i;
1416 unsigned char iommu;
1417 uint8_t isochronous_capable = 0;
1418
1419 iommu = 1;
1420 get_option(&iommu, "iommu");
1421
1422 for (i = 0; i < pDat->TotalLinks*2; i += 2) {
1423 if ((pDat->PortList[i].Type == PORTLIST_TYPE_CPU) && (pDat->PortList[i+1].Type == PORTLIST_TYPE_IO)) {
1424 if ((pDat->PortList[i].PrvFeatureCap & 0x1) && (pDat->PortList[i+1].PrvFeatureCap & 0x1)) {
1425 pDat->PortList[i].enable_isochronous_mode = 1;
1426 pDat->PortList[i+1].enable_isochronous_mode = 1;
1427 isochronous_capable = 1;
1428 } else {
1429 pDat->PortList[i].enable_isochronous_mode = 0;
1430 pDat->PortList[i+1].enable_isochronous_mode = 0;
1431 }
1432 }
1433 }
1434
1435 if (isochronous_capable && iommu) {
1436 printk(BIOS_DEBUG, "Forcing HT links to isochronous mode due to enabled IOMMU\n");
1437 /* Isochronous mode must be set on all links if the IOMMU is enabled */
1438 for (i = 0; i < pDat->TotalLinks*2; i += 2) {
1439 pDat->PortList[i].enable_isochronous_mode = 1;
1440 pDat->PortList[i+1].enable_isochronous_mode = 1;
1441 }
1442 }
1443}
1444
Marc Jones8ae8c882007-12-19 01:32:08 +00001445/*----------------------------------------------------------------------------------------
1446 * void
1447 * selectOptimalWidthAndFrequency(sMainData *pDat)
1448 *
1449 * Description:
1450 * For all links:
1451 * Examine both sides of a link and determine the optimal frequency and width,
1452 * taking into account externally provided limits and enforcing any other limit
1453 * or matching rules as applicable except sublink balancing. Update the port
1454 * list date with the optimal settings.
1455 * Note no hardware state changes in this routine.
1456 *
1457 * Parameters:
1458 * @param[in,out] sMainData* pDat = our global state, port list data
1459 * ---------------------------------------------------------------------------------------
1460 */
Myles Watson075fbe82010-04-15 05:19:29 +00001461static void selectOptimalWidthAndFrequency(sMainData *pDat)
Marc Jones8ae8c882007-12-19 01:32:08 +00001462{
1463 u8 i, j;
Timothy Pearson7c55f372015-08-02 21:36:24 -05001464 uint32_t temp;
1465 uint32_t cbPCBFreqLimit;
1466 uint32_t cbPCBFreqLimit_NVRAM;
Marc Jones8ae8c882007-12-19 01:32:08 +00001467 u8 cbPCBABDownstreamWidth;
1468 u8 cbPCBBAUpstreamWidth;
1469
Timothy Pearson7c55f372015-08-02 21:36:24 -05001470 cbPCBFreqLimit_NVRAM = 0xfffff;
Timothy Pearson03259a82015-02-15 17:11:14 -06001471 if (get_option(&temp, "hypertransport_speed_limit") == CB_SUCCESS)
1472 cbPCBFreqLimit_NVRAM = ht_speed_limit[temp & 0xf];
1473
Timothy Pearson7c55f372015-08-02 21:36:24 -05001474 if (!is_fam15h()) {
1475 /* FIXME
1476 * By default limit frequency to 2.6 GHz as there are residual
1477 * problems with HT v3.1 implementation on at least some Socket G34
1478 * mainboards / Fam10h CPUs.
1479 * Debug the issues and reenable this...
1480 */
1481 if (cbPCBFreqLimit_NVRAM > 0xffff)
1482 cbPCBFreqLimit_NVRAM = 0xffff;
1483 }
1484
Marc Jones8ae8c882007-12-19 01:32:08 +00001485 for (i = 0; i < pDat->TotalLinks*2; i += 2)
1486 {
Timothy Pearson7c55f372015-08-02 21:36:24 -05001487 cbPCBFreqLimit = 0xfffff; // Maximum allowed by autoconfiguration
Timothy Pearson586d6e22015-02-16 14:57:06 -06001488 if (pDat->HtBlock->ht_link_configuration)
1489 cbPCBFreqLimit = ht_speed_mhz_to_hw(pDat->HtBlock->ht_link_configuration->ht_speed_limit);
Timothy Pearson03259a82015-02-15 17:11:14 -06001490 cbPCBFreqLimit = min(cbPCBFreqLimit, cbPCBFreqLimit_NVRAM);
Timothy Pearson55cf7bc2010-03-01 10:30:08 +00001491
Martin Roth77a58b92017-06-24 14:45:48 -06001492#if IS_ENABLED(CONFIG_LIMIT_HT_DOWN_WIDTH_8)
Timothy Pearson55cf7bc2010-03-01 10:30:08 +00001493 cbPCBABDownstreamWidth = 8;
1494#else
Marc Jones8ae8c882007-12-19 01:32:08 +00001495 cbPCBABDownstreamWidth = 16;
Timothy Pearson55cf7bc2010-03-01 10:30:08 +00001496#endif
1497
Martin Roth77a58b92017-06-24 14:45:48 -06001498#if IS_ENABLED(CONFIG_LIMIT_HT_UP_WIDTH_8)
Timothy Pearson55cf7bc2010-03-01 10:30:08 +00001499 cbPCBBAUpstreamWidth = 8;
1500#else
Marc Jones8ae8c882007-12-19 01:32:08 +00001501 cbPCBBAUpstreamWidth = 16;
Timothy Pearson55cf7bc2010-03-01 10:30:08 +00001502#endif
Marc Jones8ae8c882007-12-19 01:32:08 +00001503
Elyes HAOUASa8131602016-09-19 10:27:57 -06001504 if ((pDat->PortList[i].Type == PORTLIST_TYPE_CPU) && (pDat->PortList[i+1].Type == PORTLIST_TYPE_CPU))
Marc Jones8ae8c882007-12-19 01:32:08 +00001505 {
1506 if (pDat->HtBlock->AMD_CB_Cpu2CpuPCBLimits)
1507 {
1508 pDat->HtBlock->AMD_CB_Cpu2CpuPCBLimits(
1509 pDat->PortList[i].NodeID,
1510 pDat->PortList[i].Link,
1511 pDat->PortList[i+1].NodeID,
1512 pDat->PortList[i+1].Link,
1513 &cbPCBABDownstreamWidth,
1514 &cbPCBBAUpstreamWidth, &cbPCBFreqLimit
1515 );
1516 }
1517 }
1518 else
1519 {
1520 if (pDat->HtBlock->AMD_CB_IOPCBLimits)
1521 {
1522 pDat->HtBlock->AMD_CB_IOPCBLimits(
1523 pDat->PortList[i+1].NodeID,
1524 pDat->PortList[i+1].HostLink,
1525 pDat->PortList[i+1].HostDepth,
1526 &cbPCBABDownstreamWidth,
1527 &cbPCBBAUpstreamWidth, &cbPCBFreqLimit
1528 );
1529 }
1530 }
1531
Marc Jones8ae8c882007-12-19 01:32:08 +00001532 temp = pDat->PortList[i].PrvFrequencyCap;
1533 temp &= pDat->PortList[i+1].PrvFrequencyCap;
1534 temp &= cbPCBFreqLimit;
Timothy Pearson7c55f372015-08-02 21:36:24 -05001535 pDat->PortList[i].CompositeFrequencyCap = temp;
1536 pDat->PortList[i+1].CompositeFrequencyCap = temp;
Marc Jones8ae8c882007-12-19 01:32:08 +00001537
1538 ASSERT (temp != 0);
Elyes HAOUAS7db506c2016-10-02 11:56:39 +02001539 for (j = 19;; j--)
Marc Jones8ae8c882007-12-19 01:32:08 +00001540 {
Timothy Pearson7c55f372015-08-02 21:36:24 -05001541 if ((j == 16) || (j == 15))
1542 continue;
1543 if (temp & ((uint32_t)1 << j))
Marc Jones8ae8c882007-12-19 01:32:08 +00001544 break;
1545 }
1546
1547 pDat->PortList[i].SelFrequency = j;
1548 pDat->PortList[i+1].SelFrequency = j;
1549
1550 temp = pDat->PortList[i].PrvWidthOutCap;
1551 if (pDat->PortList[i+1].PrvWidthInCap < temp)
1552 temp = pDat->PortList[i+1].PrvWidthInCap;
1553 if (cbPCBABDownstreamWidth < temp)
1554 temp = cbPCBABDownstreamWidth;
1555 pDat->PortList[i].SelWidthOut = (u8)temp;
1556 pDat->PortList[i+1].SelWidthIn = (u8)temp;
1557
1558 temp = pDat->PortList[i].PrvWidthInCap;
1559 if (pDat->PortList[i+1].PrvWidthOutCap < temp)
1560 temp = pDat->PortList[i+1].PrvWidthOutCap;
1561 if (cbPCBBAUpstreamWidth < temp)
1562 temp = cbPCBBAUpstreamWidth;
1563 pDat->PortList[i].SelWidthIn = (u8)temp;
1564 pDat->PortList[i+1].SelWidthOut = (u8)temp;
Marc Jones8ae8c882007-12-19 01:32:08 +00001565 }
1566}
1567
1568/*----------------------------------------------------------------------------------------
1569 * void
1570 * hammerSublinkFixup(sMainData *pDat)
1571 *
1572 * Description:
1573 * Iterate through all links, checking the frequency of each sublink pair. Make the
1574 * adjustment to the port list data so that the frequencies are at a valid ratio,
1575 * reducing frequency as needed to achieve this. (All links support the minimum 200 MHz
1576 * frequency.) Repeat the above until no adjustments are needed.
1577 * Note no hardware state changes in this routine.
1578 *
1579 * Parameters:
1580 * @param[in,out] sMainData* pDat = our global state, link state and port list
1581 * ---------------------------------------------------------------------------------------
1582 */
Myles Watson075fbe82010-04-15 05:19:29 +00001583static void hammerSublinkFixup(sMainData *pDat)
Marc Jones8ae8c882007-12-19 01:32:08 +00001584{
1585#ifndef HT_BUILD_NC_ONLY
1586 u8 i, j, k;
1587 BOOL changes, downgrade;
1588
1589 u8 hiIndex;
1590 u8 hiFreq, loFreq;
1591
1592 u32 temp;
1593
1594 do
1595 {
1596 changes = FALSE;
1597 for (i = 0; i < pDat->TotalLinks*2; i++)
1598 {
Marc Jonesaee07962008-07-16 21:09:31 +00001599 if (pDat->PortList[i].Type != PORTLIST_TYPE_CPU) /* Must be a CPU link */
Marc Jones8ae8c882007-12-19 01:32:08 +00001600 continue;
Peter Lemenkov5797b2e2018-10-19 16:57:27 +02001601 if (pDat->PortList[i].Link < 4) /* Only look for sublink1's */
Marc Jones8ae8c882007-12-19 01:32:08 +00001602 continue;
1603
1604 for (j = 0; j < pDat->TotalLinks*2; j++)
1605 {
Marc Jonesaee07962008-07-16 21:09:31 +00001606 /* Step 1. Find the matching sublink0 */
Marc Jones8ae8c882007-12-19 01:32:08 +00001607 if (pDat->PortList[j].Type != PORTLIST_TYPE_CPU)
1608 continue;
1609 if (pDat->PortList[j].NodeID != pDat->PortList[i].NodeID)
1610 continue;
1611 if (pDat->PortList[j].Link != (pDat->PortList[i].Link & 0x03))
1612 continue;
1613
Marc Jonesaee07962008-07-16 21:09:31 +00001614 /* Step 2. Check for an illegal frequency ratio */
Marc Jones8ae8c882007-12-19 01:32:08 +00001615 if (pDat->PortList[i].SelFrequency >= pDat->PortList[j].SelFrequency)
1616 {
1617 hiIndex = i;
1618 hiFreq = pDat->PortList[i].SelFrequency;
1619 loFreq = pDat->PortList[j].SelFrequency;
1620 }
1621 else
1622 {
1623 hiIndex = j;
1624 hiFreq = pDat->PortList[j].SelFrequency;
1625 loFreq = pDat->PortList[i].SelFrequency;
1626 }
1627
1628 if (hiFreq == loFreq)
Marc Jonesaee07962008-07-16 21:09:31 +00001629 break; /* The frequencies are 1:1, no need to do anything */
Marc Jones8ae8c882007-12-19 01:32:08 +00001630
1631 downgrade = FALSE;
1632
1633 if (hiFreq == 13)
1634 {
Marc Jonesaee07962008-07-16 21:09:31 +00001635 if ((loFreq != 7) && /* {13, 7} 2400MHz / 1200MHz 2:1 */
1636 (loFreq != 4) && /* {13, 4} 2400MHz / 600MHz 4:1 */
Elyes HAOUASa8131602016-09-19 10:27:57 -06001637 (loFreq != 2)) /* {13, 2} 2400MHz / 400MHz 6:1 */
Marc Jones8ae8c882007-12-19 01:32:08 +00001638 downgrade = TRUE;
1639 }
1640 else if (hiFreq == 11)
1641 {
Marc Jonesaee07962008-07-16 21:09:31 +00001642 if ((loFreq != 6)) /* {11, 6} 2000MHz / 1000MHz 2:1 */
Marc Jones8ae8c882007-12-19 01:32:08 +00001643 downgrade = TRUE;
1644 }
1645 else if (hiFreq == 9)
1646 {
Marc Jonesaee07962008-07-16 21:09:31 +00001647 if ((loFreq != 5) && /* { 9, 5} 1600MHz / 800MHz 2:1 */
1648 (loFreq != 2) && /* { 9, 2} 1600MHz / 400MHz 4:1 */
Elyes HAOUASa8131602016-09-19 10:27:57 -06001649 (loFreq != 0)) /* { 9, 0} 1600MHz / 200MHz 8:1 */
Marc Jones8ae8c882007-12-19 01:32:08 +00001650 downgrade = TRUE;
1651 }
1652 else if (hiFreq == 7)
1653 {
Marc Jonesaee07962008-07-16 21:09:31 +00001654 if ((loFreq != 4) && /* { 7, 4} 1200MHz / 600MHz 2:1 */
Elyes HAOUASa8131602016-09-19 10:27:57 -06001655 (loFreq != 0)) /* { 7, 0} 1200MHz / 200MHz 6:1 */
Marc Jones8ae8c882007-12-19 01:32:08 +00001656 downgrade = TRUE;
1657 }
1658 else if (hiFreq == 5)
1659 {
Marc Jonesaee07962008-07-16 21:09:31 +00001660 if ((loFreq != 2) && /* { 5, 2} 800MHz / 400MHz 2:1 */
Elyes HAOUASa8131602016-09-19 10:27:57 -06001661 (loFreq != 0)) /* { 5, 0} 800MHz / 200MHz 4:1 */
Marc Jones8ae8c882007-12-19 01:32:08 +00001662 downgrade = TRUE;
1663 }
1664 else if (hiFreq == 2)
1665 {
Marc Jonesaee07962008-07-16 21:09:31 +00001666 if ((loFreq != 0)) /* { 2, 0} 400MHz / 200MHz 2:1 */
Marc Jones8ae8c882007-12-19 01:32:08 +00001667 downgrade = TRUE;
1668 }
1669 else
1670 {
Marc Jonesaee07962008-07-16 21:09:31 +00001671 downgrade = TRUE; /* no legal ratios for hiFreq */
Marc Jones8ae8c882007-12-19 01:32:08 +00001672 }
1673
Marc Jonesaee07962008-07-16 21:09:31 +00001674 /* Step 3. Downgrade the higher of the two frequencies, and set nochanges to FALSE */
Marc Jones8ae8c882007-12-19 01:32:08 +00001675 if (downgrade)
1676 {
Marc Jonesaee07962008-07-16 21:09:31 +00001677 /* Although the problem was with the port specified by hiIndex, we need to */
1678 /* downgrade both ends of the link. */
1679 hiIndex = hiIndex & 0xFE; /* Select the 'upstream' (i.e. even) port */
Marc Jones8ae8c882007-12-19 01:32:08 +00001680
1681 temp = pDat->PortList[hiIndex].CompositeFrequencyCap;
1682
Marc Jonesaee07962008-07-16 21:09:31 +00001683 /* Remove hiFreq from the list of valid frequencies */
1684 temp = temp & ~((uint32)1 << hiFreq);
Marc Jones8ae8c882007-12-19 01:32:08 +00001685 ASSERT (temp != 0);
Timothy Pearson7c55f372015-08-02 21:36:24 -05001686 pDat->PortList[hiIndex].CompositeFrequencyCap = temp;
1687 pDat->PortList[hiIndex+1].CompositeFrequencyCap = temp;
Marc Jones8ae8c882007-12-19 01:32:08 +00001688
Elyes HAOUAS7db506c2016-10-02 11:56:39 +02001689 for (k = 19;; k--)
Marc Jones8ae8c882007-12-19 01:32:08 +00001690 {
Timothy Pearson7c55f372015-08-02 21:36:24 -05001691 if ((j == 16) || (j == 15))
1692 continue;
1693 if (temp & ((uint32_t)1 << k))
Marc Jones8ae8c882007-12-19 01:32:08 +00001694 break;
1695 }
1696
1697 pDat->PortList[hiIndex].SelFrequency = k;
1698 pDat->PortList[hiIndex+1].SelFrequency = k;
1699
1700 changes = TRUE;
1701 }
1702 }
1703 }
Marc Jonesaee07962008-07-16 21:09:31 +00001704 } while (changes); /* Repeat until a valid configuration is reached */
Marc Jones8ae8c882007-12-19 01:32:08 +00001705#endif /* HT_BUILD_NC_ONLY */
1706}
1707
1708/*----------------------------------------------------------------------------------------
1709 * void
1710 * linkOptimization(sMainData *pDat)
1711 *
1712 * Description:
1713 * Based on link capabilities, apply optimization rules to come up with the real best
1714 * settings, including several external limit decision from call backs. This includes
1715 * handling of sublinks. Finally, after the port list data is updated, set the hardware
1716 * state for all links.
1717 *
1718 * Parameters:
1719 * @param[in] sMainData* pDat = our global state
1720 * ---------------------------------------------------------------------------------------
1721 */
Myles Watson075fbe82010-04-15 05:19:29 +00001722static void linkOptimization(sMainData *pDat)
Marc Jones8ae8c882007-12-19 01:32:08 +00001723{
1724 pDat->nb->gatherLinkData(pDat, pDat->nb);
1725 regangLinks(pDat);
Timothy Pearson50001b82015-08-11 17:47:48 -05001726 if (is_fam15h())
1727 detectIoLinkIsochronousCapable(pDat);
Marc Jones8ae8c882007-12-19 01:32:08 +00001728 selectOptimalWidthAndFrequency(pDat);
1729 hammerSublinkFixup(pDat);
1730 pDat->nb->setLinkData(pDat, pDat->nb);
1731}
1732
1733
1734/*----------------------------------------------------------------------------------------
1735 * void
1736 * trafficDistribution(sMainData *pDat)
1737 *
1738 * Description:
1739 * In the case of a two node system with both sublinks used, enable the traffic
1740 * distribution feature.
1741 *
1742 * Parameters:
1743 * @param[in] sMainData* pDat = our global state, port list data
1744 * ---------------------------------------------------------------------------------------
1745 */
Myles Watson075fbe82010-04-15 05:19:29 +00001746static void trafficDistribution(sMainData *pDat)
Marc Jones8ae8c882007-12-19 01:32:08 +00001747{
1748#ifndef HT_BUILD_NC_ONLY
1749 u32 links01, links10;
1750 u8 linkCount;
1751 u8 i;
1752
Marc Jonesaee07962008-07-16 21:09:31 +00001753 /* Traffic Distribution is only used when there are exactly two nodes in the system */
Marc Jones8ae8c882007-12-19 01:32:08 +00001754 if (pDat->NodesDiscovered+1 != 2)
1755 return;
1756
1757 links01 = 0;
1758 links10 = 0;
1759 linkCount = 0;
1760 for (i = 0; i < pDat->TotalLinks*2; i += 2)
1761 {
1762 if ((pDat->PortList[i].Type == PORTLIST_TYPE_CPU) && (pDat->PortList[i+1].Type == PORTLIST_TYPE_CPU))
1763 {
1764 links01 |= (u32)1 << pDat->PortList[i].Link;
1765 links10 |= (u32)1 << pDat->PortList[i+1].Link;
1766 linkCount++;
1767 }
1768 }
1769 ASSERT(linkCount != 0);
1770 if (linkCount == 1)
Marc Jonesaee07962008-07-16 21:09:31 +00001771 return; /* Don't setup Traffic Distribution if only one link is being used */
Marc Jones8ae8c882007-12-19 01:32:08 +00001772
1773 pDat->nb->writeTrafficDistribution(links01, links10, pDat->nb);
1774#endif /* HT_BUILD_NC_ONLY */
1775}
1776
1777/*----------------------------------------------------------------------------------------
1778 * void
1779 * tuning(sMainData *pDat)
1780 *
1781 * Description:
1782 * Handle system and performance tunings, such as traffic distribution, fifo and
1783 * buffer tuning, and special config tunings.
1784 *
1785 * Parameters:
1786 * @param[in] sMainData* pDat = our global state, port list data
1787 * ---------------------------------------------------------------------------------------
1788 */
Myles Watson075fbe82010-04-15 05:19:29 +00001789static void tuning(sMainData *pDat)
Marc Jones8ae8c882007-12-19 01:32:08 +00001790{
1791 u8 i;
1792
1793 /* See if traffic distribution can be done and do it if so
1794 * or allow system specific customization
1795 */
1796 if ((pDat->HtBlock->AMD_CB_CustomizeTrafficDistribution == NULL)
1797 || !pDat->HtBlock->AMD_CB_CustomizeTrafficDistribution())
1798 {
1799 trafficDistribution(pDat);
1800 }
1801
1802 /* For each node, invoke northbridge specific buffer tunings or
1803 * system specific customizations.
1804 */
Elyes HAOUASa8131602016-09-19 10:27:57 -06001805 for (i = 0; i < pDat->NodesDiscovered + 1; i++)
Marc Jones8ae8c882007-12-19 01:32:08 +00001806 {
1807 if ((pDat->HtBlock->AMD_CB_CustomizeBuffers == NULL)
1808 || !pDat->HtBlock->AMD_CB_CustomizeBuffers(i))
1809 {
1810 pDat->nb->bufferOptimizations(i, pDat, pDat->nb);
1811 }
1812 }
1813}
1814
1815/*----------------------------------------------------------------------------------------
1816 * BOOL
1817 * isSanityCheckOk()
1818 *
1819 * Description:
1820 * Perform any general sanity checks which should prevent HT from running if they fail.
1821 * Currently only the "Must run on BSP only" check.
1822 *
1823 * Parameters:
1824 * @param[out] result BOOL = true if check is ok, false if it failed
1825 * ---------------------------------------------------------------------------------------
1826 */
Myles Watson075fbe82010-04-15 05:19:29 +00001827static BOOL isSanityCheckOk(void)
Marc Jones8ae8c882007-12-19 01:32:08 +00001828{
1829 uint64 qValue;
1830
Elyes HAOUASa9473ec2018-10-24 15:55:53 +02001831 AmdMSRRead(LAPIC_BASE_MSR, &qValue);
Marc Jones8ae8c882007-12-19 01:32:08 +00001832
Elyes HAOUASa9473ec2018-10-24 15:55:53 +02001833 return ((qValue.lo & LAPIC_BASE_MSR_BOOTSTRAP_PROCESSOR) != 0);
Marc Jones8ae8c882007-12-19 01:32:08 +00001834}
1835
1836/***************************************************************************
1837 *** HT Initialize ***
1838 ***************************************************************************/
1839
1840/*----------------------------------------------------------------------------------------
1841 * void
1842 * htInitialize(AMD_HTBLOCK *pBlock)
1843 *
1844 * Description:
1845 * This is the top level external interface for Hypertransport Initialization.
1846 * Create our initial internal state, initialize the coherent fabric,
1847 * initialize the non-coherent chains, and perform any required fabric tuning or
1848 * optimization.
1849 *
1850 * Parameters:
1851 * @param[in] AMD_HTBLOCK* pBlock = Our Initial State including possible
1852 * topologies and routings, non coherent bus
1853 * assignment info, and actual
1854 * wrapper or OEM call back routines.
1855 * ---------------------------------------------------------------------------------------
1856 */
1857void amdHtInitialize(AMD_HTBLOCK *pBlock)
1858{
1859 sMainData pDat;
1860 cNorthBridge nb;
1861
1862 if (isSanityCheckOk())
1863 {
1864 newNorthBridge(0, &nb);
1865
1866 pDat.HtBlock = pBlock;
1867 pDat.nb = &nb;
1868 pDat.sysMpCap = nb.maxNodes;
1869 nb.isCapable(0, &pDat, pDat.nb);
1870 coherentInit(&pDat);
1871
1872 pDat.AutoBusCurrent = pBlock->AutoBusStart;
1873 pDat.UsedCfgMapEntires = 0;
1874 ncInit(&pDat);
1875 linkOptimization(&pDat);
1876 tuning(&pDat);
1877 }
1878}