AGESA F15: AMD family15 AGESA code
[coreboot.git] / src / vendorcode / amd / agesa / f15 / Proc / HT / htInterfaceGeneral.c
1 /* $NoKeywords:$ */
2 /**
3  * @file
4  *
5  * External Interface implementation, general purpose features.
6  *
7  * Contains routines for implementing the interface to the client BIOS.  This file
8  * includes the interface support which is not removed with various build options.
9  *
10  * @xrefitem bom "File Content Label" "Release Content"
11  * @e project:      AGESA
12  * @e sub-project:  HyperTransport
13  * @e \$Revision: 56279 $   @e \$Date: 2011-07-11 13:11:28 -0600 (Mon, 11 Jul 2011) $
14  *
15  */
16 /*
17  *****************************************************************************
18  *
19  * Copyright (C) 2012 Advanced Micro Devices, Inc.
20  * All rights reserved.
21  *
22  * Redistribution and use in source and binary forms, with or without
23  * modification, are permitted provided that the following conditions are met:
24  *     * Redistributions of source code must retain the above copyright
25  *       notice, this list of conditions and the following disclaimer.
26  *     * Redistributions in binary form must reproduce the above copyright
27  *       notice, this list of conditions and the following disclaimer in the
28  *       documentation and/or other materials provided with the distribution.
29  *     * Neither the name of Advanced Micro Devices, Inc. nor the names of
30  *       its contributors may be used to endorse or promote products derived
31  *       from this software without specific prior written permission.
32  *
33  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
34  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
35  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
36  * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY
37  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
38  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
39  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
40  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
41  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
42  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
43  *
44  * ***************************************************************************
45  *
46  */
47
48 /*
49  *----------------------------------------------------------------------------
50  *                                MODULES USED
51  *
52  *----------------------------------------------------------------------------
53  */
54
55
56
57 #include "AGESA.h"
58 #include "amdlib.h"
59 #include "OptionMultiSocket.h"
60 #include "Ids.h"
61 #include "Topology.h"
62 #include "htFeat.h"
63 #include "htInterface.h"
64 #include "htInterfaceGeneral.h"
65 #include "htNb.h"
66 #include "cpuServices.h"
67 #include "cpuFeatures.h"
68 #include "heapManager.h"
69 #include "Filecode.h"
70 CODE_GROUP (G1_PEICC)
71 RDATA_GROUP (G2_PEI)
72
73 #define FILECODE PROC_HT_HTINTERFACEGENERAL_FILECODE
74 /*----------------------------------------------------------------------------
75  *                          DEFINITIONS AND MACROS
76  *
77  *----------------------------------------------------------------------------
78  */
79 extern OPTION_MULTISOCKET_CONFIGURATION OptionMultiSocketConfiguration;
80
81 /*----------------------------------------------------------------------------
82  *                           TYPEDEFS AND STRUCTURES
83  *
84  *----------------------------------------------------------------------------
85  */
86
87 /*----------------------------------------------------------------------------
88  *                        PROTOTYPES OF LOCAL FUNCTIONS
89  *
90  *----------------------------------------------------------------------------
91  */
92
93 /*----------------------------------------------------------------------------
94  *                            EXPORTED FUNCTIONS
95  *
96  *----------------------------------------------------------------------------
97  */
98
99 /*----------------------------------------------------------------------------
100  *                              LOCAL FUNCTIONS
101  *
102  *----------------------------------------------------------------------------
103  */
104
105 /*----------------------------------------------------------------------------------------*/
106 /**
107  * Is PackageLink an Internal Link?
108  *
109  * This is a test for the logical link match codes in the user interface, not a test for
110  * the actual northbridge links.
111  *
112  * @param[in]    PackageLink   The link
113  *
114  * @retval       TRUE          This is an internal link
115  * @retval       FALSE         This is not an internal link
116  */
117 BOOLEAN
118 IsPackageLinkInternal (
119   IN       UINT8  PackageLink
120   )
121 {
122   return (BOOLEAN) ((PackageLink <= HT_LIST_MATCH_INTERNAL_LINK_2) && (PackageLink >= HT_LIST_MATCH_INTERNAL_LINK_0));
123 }
124
125 /*----------------------------------------------------------------------------------------*/
126 /**
127  * Ignore a Link.
128  *
129  * @HtInterfaceMethod{::F_GET_IGNORE_LINK}
130  *
131  * This routine is called every time a coherent Link is found and then every time a
132  * non-coherent Link from a CPU is found.  Any coherent or non-coherent Link from a
133  * CPU can be ignored and not used for discovery or initialization.  Useful for
134  * connection based systems.
135  *
136  * @note not called for IO device to IO Device Links.
137  *
138  * @param[in]     Node             The Node on which this Link is located
139  * @param[in]     Link             The Link about to be initialized
140  * @param[in]     NbIgnoreLinkList The northbridge default ignore link list
141  * @param[in]     State            the input data
142  *
143  * @retval        MATCHED     ignore this Link and skip it
144  * @retval        POWERED_OFF ignore this link and power it off.
145  * @retval        UNMATCHED   initialize the Link normally
146  */
147 FINAL_LINK_STATE
148 GetIgnoreLink (
149   IN       UINT8            Node,
150   IN       UINT8            Link,
151   IN       IGNORE_LINK     *NbIgnoreLinkList,
152   IN       STATE_DATA      *State
153   )
154 {
155   IGNORE_LINK *p;
156   FINAL_LINK_STATE Result;
157   BOOLEAN IsFound;
158   UINT8 Socket;
159   UINT8 PackageLink;
160
161   ASSERT ((Node < MAX_NODES) && (Link < MAX_NODES));
162
163   Result = UNMATCHED;
164   IsFound = FALSE;
165   Socket = State->HtInterface->GetSocketFromMap (Node, State);
166   PackageLink = State->Nb->GetPackageLink (Node, Link, State->Nb);
167
168   if (State->HtBlock->IgnoreLinkList != NULL) {
169     p = State->HtBlock->IgnoreLinkList;
170     while (p->Socket != HT_LIST_TERMINAL) {
171       if (((p->Socket == Socket) || (p->Socket == HT_LIST_MATCH_ANY)) &&
172           ((p->Link == PackageLink) ||
173            ((p->Link == HT_LIST_MATCH_ANY) && (!IsPackageLinkInternal (PackageLink))) ||
174            ((p->Link == HT_LIST_MATCH_INTERNAL_LINK) && (IsPackageLinkInternal (PackageLink))))) {
175         // Found a match return the desired link state.
176         ASSERT (Result < MaxFinalLinkState);
177         Result = p->LinkState;
178         IsFound = TRUE;
179         break;
180       } else {
181         p++;
182       }
183     }
184   }
185   // If there wasn't a match in the user interface, see if the northbridge provides one.
186   if (!IsFound && (NbIgnoreLinkList != NULL)) {
187     p = NbIgnoreLinkList;
188     while (p->Socket != HT_LIST_TERMINAL) {
189       if (((p->Socket == Socket) || (p->Socket == HT_LIST_MATCH_ANY)) &&
190           ((p->Link == PackageLink) ||
191            ((p->Link == HT_LIST_MATCH_ANY) && (!IsPackageLinkInternal (PackageLink))) ||
192            ((p->Link == HT_LIST_MATCH_INTERNAL_LINK) && (IsPackageLinkInternal (PackageLink))))) {
193         // Found a match return the desired link state.
194         ASSERT (Result < MaxFinalLinkState);
195         Result = p->LinkState;
196         break;
197       } else {
198         p++;
199       }
200     }
201   }
202   return Result;
203 }
204
205 /*----------------------------------------------------------------------------------------*/
206 /**
207  * Get the Socket number for a given Node number.
208  *
209  * @HtInterfaceMethod{::F_GET_SOCKET_FROM_MAP}
210  *
211  * Return the id.
212  *
213  * @param[in]     Node    The Node to translate
214  * @param[in]     State   reference to Node to socket map
215  *
216  * @return the socket id
217  *
218  */
219 UINT8
220 GetSocketFromMap (
221   IN       UINT8         Node,
222   IN       STATE_DATA    *State
223   )
224 {
225   UINT8 Socket;
226
227   ASSERT (State->NodeToSocketDieMap != NULL);
228
229   Socket = (*State->NodeToSocketDieMap)[Node].Socket;
230   return Socket;
231 }
232
233 /*----------------------------------------------------------------------------------------*/
234 /**
235  * Get a new Socket Die to Node Map.
236  *
237  * @HtInterfaceMethod{::F_NEW_NODE_AND_SOCKET_TABLES}
238  *
239  * Put the Socket Die Table in heap with a known handle.  Content will be generated as
240  * each node is discovered.
241  *
242  * @param[in,out] State global state
243  */
244 VOID
245 NewNodeAndSocketTables (
246   IN OUT   STATE_DATA *State
247   )
248 {
249   UINT8 i;
250   UINT8 j;
251   ALLOCATE_HEAP_PARAMS AllocHeapParams;
252
253   // Allocate heap for the table
254   State->SocketDieToNodeMap = NULL;
255   AllocHeapParams.RequestedBufferSize = (((MAX_SOCKETS) * (MAX_DIES)) * sizeof (SOCKET_DIE_TO_NODE_ITEM));
256   AllocHeapParams.BufferHandle = SOCKET_DIE_MAP_HANDLE;
257   AllocHeapParams.Persist = HEAP_SYSTEM_MEM;
258   if (HeapAllocateBuffer (&AllocHeapParams, State->ConfigHandle) == AGESA_SUCCESS) {
259     State->SocketDieToNodeMap = (SOCKET_DIE_TO_NODE_MAP)AllocHeapParams.BufferPtr;
260     // Initialize shared data structures
261     for (i = 0; i < MAX_SOCKETS; i++) {
262       for (j = 0; j < MAX_DIES; j++) {
263         (*State->SocketDieToNodeMap)[i][j].Node = HT_LIST_TERMINAL;
264         (*State->SocketDieToNodeMap)[i][j].LowCore = HT_LIST_TERMINAL;
265         (*State->SocketDieToNodeMap)[i][j].HighCore = HT_LIST_TERMINAL;
266       }
267     }
268   }
269   // Allocate heap for the table
270   State->NodeToSocketDieMap = NULL;
271   AllocHeapParams.RequestedBufferSize = (MAX_NODES * sizeof (NODE_TO_SOCKET_DIE_ITEM));
272   AllocHeapParams.BufferHandle = NODE_ID_MAP_HANDLE;
273   AllocHeapParams.Persist = HEAP_SYSTEM_MEM;
274   if (HeapAllocateBuffer (&AllocHeapParams, State->ConfigHandle) == AGESA_SUCCESS) {
275     State->NodeToSocketDieMap = (NODE_TO_SOCKET_DIE_MAP)AllocHeapParams.BufferPtr;
276     // Initialize shared data structures
277     for (i = 0; i < MAX_NODES; i++) {
278       (*State->NodeToSocketDieMap)[i].Socket = HT_LIST_TERMINAL;
279       (*State->NodeToSocketDieMap)[i].Die = HT_LIST_TERMINAL;
280     }
281   }
282 }
283
284 /*----------------------------------------------------------------------------------------*/
285 /**
286  * Get the minimum Northbridge frequency for the system.
287  *
288  * @HtInterfaceMethod{::F_GET_MIN_NB_CORE_FREQ}
289  *
290  * Invoke the CPU component power mgt interface.
291  *
292  * @param[in]    PlatformConfig   Platform profile/build option config structure.
293  * @param[in]    StdHeader        Config for library and services.
294  *
295  * @return Frequency in MHz.
296  *
297  */
298 UINT32
299 GetMinNbCoreFreq (
300   IN       PLATFORM_CONFIGURATION *PlatformConfig,
301   IN       AMD_CONFIG_PARAMS      *StdHeader
302   )
303 {
304   UINT32 MinSysNbFreq;
305   UINT32 MinP0NbFreq;
306
307   OptionMultiSocketConfiguration.GetMinNbCof (PlatformConfig, &MinSysNbFreq, &MinP0NbFreq, StdHeader);
308
309   ASSERT (MinSysNbFreq != 0);
310
311   return MinSysNbFreq;
312 }
313
314 /**
315  * @page physicalsockethowto Physical Socket Map, How To Create
316  *
317  *  To create a physical system socket map for a platform:
318  *
319  *  - Start at the Node which will be the BSP.
320  *
321  *  - Begin a breadth first enumeration of all the coherent Links between sockets
322  *  by creating a socket structure for each socket connection from the BSP.
323  *  For example, if the BSP is in socket zero and Link one connects to socket two,
324  *  create socket {0, 1, 2}.
325  *
326  *  - When all Links from the BSP are described, go to the first socket connected
327  *  to the BSP and continue the breadth first enumeration.
328  *
329  *  - It should not be necessary to describe the back Links; in the example above, there
330  *  should be no need to create {2, 1, 0} (assuming socket two connects back to
331  *  socket zero on its Link one).
332  *
333  *  - When completed:
334  *
335  *     - Every socket except the BSP's (usually zero) must be listed as a targetSocket,
336  *     at least once.  Some sockets may be listed more than once.
337  *
338  *     - There usually should be at least as many entries as Links. An exception is a
339  *     fully connected system, only the Links from the BSP are needed.
340  *
341  *     - Every socket but the last one in the breadth first order should usually have one
342  *     or more entries listing it as a currentSocket. (The last one has only back Links.)
343  *
344  *  There are no strict assumptions about the ordering of the socket structures.
345  */
346
347 /*----------------------------------------------------------------------------------------*/
348 /**
349  * Update maps between Sockets and Nodes for a specific newly discovered node.
350  *
351  * @HtInterfaceMethod{::F_SET_NODE_TO_SOCKET_MAP}
352  *
353  * There are two methods for providing socket naming of nodes.
354  *
355  * Hardware Method (preferred):  A value strapped in hardware by the board is read and
356  * passed to this routine.
357  *
358  * Software Method:  The current node's socket is looked up, since it was
359  * previously a new node and went through this process. The link is converted to
360  * a package level link.  A user data structure describing the package level
361  * layout of the system is searched for the current node's socket and package link,
362  * and now we know the new node's socket.
363  *
364  * In either case, the Socket, Module to Node map and the Node to Socket, Module
365  * map are updated with the new node, socket, and module.
366  *
367  * Data needed to do this is passed in to the routine as arguments rather than read by this routine,
368  * so that it is not necessary to know a valid temporary route to either node at the time this code runs.
369  *
370  * @param[in] Node           Node from which a new node was discovered
371  * @param[in] CurrentNodeModule  The current node's module id in it's processor.
372  * @param[in] PackageLink    The package link for the current node's link.
373  * @param[in] NewNode        The new node's id
374  * @param[in] HardwareSocket If we use the hardware method (preferred), this is the socket of new node.
375  * @param[in] Module         The new node's module id in it's processor.
376  * @param[in] State          our State
377  */
378 VOID
379 SetNodeToSocketMap (
380   IN       UINT8 Node,
381   IN       UINT8 CurrentNodeModule,
382   IN       UINT8 PackageLink,
383   IN       UINT8 NewNode,
384   IN       UINT8 HardwareSocket,
385   IN       UINT8 Module,
386   IN       STATE_DATA *State
387   )
388 {
389   UINT8 SourceSocket;
390   UINT8 TargetSocket;
391   SYSTEM_PHYSICAL_SOCKET_MAP *Map;
392
393   // While this code could be written to recover from a NULL socket map, AGESA cannot function without one.
394   ASSERT (State->SocketDieToNodeMap != NULL);
395
396   if (State->HtBlock->SystemPhysicalSocketMap != NULL) {
397     if (NewNode != 0) {
398       // Find the logical Node from which a new Node was discovered in the Node field of
399       // some socket.  It must already be there, Nodes are assigned ascending.
400       //
401       for (SourceSocket = 0; SourceSocket < MAX_SOCKETS; SourceSocket++) {
402         if ((*State->SocketDieToNodeMap)[SourceSocket][CurrentNodeModule].Node == Node) {
403           break;
404         }
405       }
406       // This ASSERT should be understood as "the Node did not have a match", not as a limit check on SourceSocket.
407       ASSERT (SourceSocket != MAX_SOCKETS);
408
409       // Find the sourceSocket in the CurrentSocket field, for the Link on which a new Node
410       // was discovered.  When we find an entry with that socket and Link number, update the
411       // Node for that socket.
412       //
413       if (IsPackageLinkInternal (PackageLink)) {
414         // Internal Nodes are in the same socket, don't search the physical system map.
415         TargetSocket = SourceSocket;
416       } else {
417         // Find the target socket in the physical system map.
418         Map = State->HtBlock->SystemPhysicalSocketMap;
419         while ((Map->CurrentSocket != 0xFF) &&
420                ((Map->CurrentSocket != SourceSocket) || (Map->CurrentLink != PackageLink))) {
421           Map++;
422         }
423         ASSERT (Map->CurrentSocket != 0xFF);
424         TargetSocket = Map->TargetSocket;
425       }
426     } else {
427       // The BSP (BSN, if you will) has no predecessor node from which it is discovered.
428       TargetSocket = 0;
429     }
430   } else {
431     // Use the hardware method
432     // The hardware strapped socket id is passed to us in this case.
433     TargetSocket = HardwareSocket;
434   }
435   // If the target socket, module is already mapped to something, that's not good.  Socket labeling conflict.
436     // Check that the board is strapped correctly. If not you need a SystemPhysicalSocketMap.  If you have one,
437     // check it for correctness.
438   ASSERT ((*State->SocketDieToNodeMap)[TargetSocket][Module].Node == 0xFF);
439   // Update the map for the rest of agesa
440   (*State->SocketDieToNodeMap)[TargetSocket][Module].Node = NewNode;
441   // and the node to socket map
442   ASSERT (State->NodeToSocketDieMap != NULL);
443   (*State->NodeToSocketDieMap)[NewNode].Socket = TargetSocket;
444   (*State->NodeToSocketDieMap)[NewNode].Die = Module;
445 }
446
447 /*----------------------------------------------------------------------------------------*/
448 /**
449  * Clean up the map structures after severe event has caused a fall back to 1 node.
450  *
451  * @HtInterfaceMethod{::F_CLEAN_MAPS_AFTER_ERROR}
452  *
453  * @param[in] State Our state, access to socket, node maps
454  *
455  */
456 VOID
457 CleanMapsAfterError (
458   IN       STATE_DATA *State
459   )
460 {
461   UINTN  Socket;
462   UINTN  Module;
463   UINTN  Node;
464
465   ASSERT (State->NodeToSocketDieMap != NULL);
466   ASSERT (State->SocketDieToNodeMap != NULL);
467
468   // Clear all the socket, module items except for the socket and module containing node zero.
469   for (Socket = 0; Socket < MAX_SOCKETS; Socket++) {
470     for (Module = 0; Module < MAX_DIES; Module++) {
471       if (((*State->NodeToSocketDieMap)[0].Socket != Socket) || ((*State->NodeToSocketDieMap)[0].Die != Module)) {
472         (*State->SocketDieToNodeMap)[Socket][Module].Node = HT_LIST_TERMINAL;
473         (*State->SocketDieToNodeMap)[Socket][Module].LowCore = HT_LIST_TERMINAL;
474         (*State->SocketDieToNodeMap)[Socket][Module].HighCore = HT_LIST_TERMINAL;
475       }
476     }
477   }
478   // Clear all the node items except for node zero.
479   for (Node = 1; Node < MAX_NODES; Node++) {
480     (*State->NodeToSocketDieMap)[Node].Socket = HT_LIST_TERMINAL;
481     (*State->NodeToSocketDieMap)[Node].Die = HT_LIST_TERMINAL;
482   }
483 }
484
485 /*----------------------------------------------------------------------------------------*/
486 /**
487  * Post Node id and other context info to AP cores via mailbox.
488  *
489  * @HtInterfaceMethod{::F_POST_MAP_TO_AP}
490  *
491  * Since Ap's can not view map until after mp communication is established,
492  * provide them with initial context info via a mailbox register. A mailbox
493  * register is one that can be written in PCI space and read in MSR space.
494  *
495  * @param[in] State Our state, access to socket, node maps
496  */
497 VOID
498 PostMapToAp (
499   IN       STATE_DATA *State
500   )
501 {
502   UINT8 ModuleType;
503   UINT8 Module;
504   AP_MAILBOXES ApMailboxes;
505   UINT8 Node;
506   UINT32 Degree;
507   AGESA_STATUS CalledStatus;
508
509   // Dispatch any features (such as Preserve Mailbox) that need to run as soon as discovery is completed.
510   IDS_HDT_CONSOLE (CPU_TRACE, "  Dispatch CPU features after HT discovery\n");
511   CalledStatus = DispatchCpuFeatures (CPU_FEAT_AFTER_COHERENT_DISCOVERY, State->PlatformConfiguration, State->ConfigHandle);
512
513   ASSERT (State->Fabric != NULL);
514   Degree = 0;
515   // Compute the degree of the system by finding the maximum degree of any node.
516   for (Node = 0; Node < (State->NodesDiscovered + 1); Node++) {
517     if (State->Fabric->SysDegree[Node] > Degree) {
518       Degree = State->Fabric->SysDegree[Node];
519     }
520   }
521   // Post the information on all nodes.
522   for (Node = 0; Node < (State->NodesDiscovered + 1); Node++) {
523     ModuleType = 0;
524     Module = 0;
525     State->Nb->GetModuleInfo (Node, &ModuleType, &Module, State->Nb);
526     ApMailboxes.ApMailInfo.Info = 0;
527     ApMailboxes.ApMailInfo.Fields.Node = Node;
528     ApMailboxes.ApMailInfo.Fields.Socket = State->HtInterface->GetSocketFromMap (Node, State);
529     ApMailboxes.ApMailInfo.Fields.ModuleType = ModuleType;
530     ApMailboxes.ApMailInfo.Fields.Module = Module;
531     ApMailboxes.ApMailExtInfo.Info = 0;
532     ApMailboxes.ApMailExtInfo.Fields.SystemDegree = Degree;
533     // other fields of the extended info are used during ap init, and will be initialized at that time.
534     State->Nb->PostMailbox (Node, ApMailboxes, State->Nb);
535   }
536   // Now that the mailboxes have been initialized, cache the info on the BSC.  The APs
537   // will cache during heap initialization.
538   CacheApMailbox (State->ConfigHandle);
539 }