b7f012403439fc85ad5bdfb1704e5582e6ab4001
[coreboot.git] / src / mainboard / supermicro / h8qgi / BiosCallOuts.c
1 /*
2  * This file is part of the coreboot project.
3  *
4  * Copyright (C) 2011 Advanced Micro Devices, Inc.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; version 2 of the License.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
18  */
19
20 #include "agesawrapper.h"
21 #include "amdlib.h"
22 #include "BiosCallOuts.h"
23 #include "Ids.h"
24 #include "OptionsIds.h"
25 #include "heapManager.h"
26
27 STATIC BIOS_CALLOUT_STRUCT BiosCallouts[REQUIRED_CALLOUTS] =
28 {
29         {
30                 AGESA_ALLOCATE_BUFFER,
31                 BiosAllocateBuffer
32         },
33
34         {
35                 AGESA_DEALLOCATE_BUFFER,
36                 BiosDeallocateBuffer
37         },
38
39         {
40                 AGESA_DO_RESET,
41                 BiosReset
42         },
43
44         {
45                 AGESA_LOCATE_BUFFER,
46                 BiosLocateBuffer
47         },
48
49         {
50                 AGESA_READ_SPD,
51                 BiosReadSpd
52         },
53
54         {
55                 AGESA_READ_SPD_RECOVERY,
56                 BiosDefaultRet
57         },
58
59         {
60                 AGESA_RUNFUNC_ONAP,
61                 BiosRunFuncOnAp
62         },
63
64         {
65                 AGESA_GET_IDS_INIT_DATA,
66                 BiosGetIdsInitData
67         },
68
69         {
70                 AGESA_HOOKBEFORE_DQS_TRAINING,
71                 BiosHookBeforeDQSTraining
72         },
73
74         {
75                 AGESA_HOOKBEFORE_DRAM_INIT,
76                 BiosHookBeforeDramInit
77         },
78         {
79                 AGESA_HOOKBEFORE_EXIT_SELF_REF,
80                 BiosHookBeforeExitSelfRefresh
81         },
82 };
83
84 extern AGESA_STATUS AmdMemoryReadSPD (UINT32 unused1, UINT32 unused2, AGESA_READ_SPD_PARAMS *info);
85
86 AGESA_STATUS GetBiosCallout (UINT32 Func, UINT32 Data, VOID *ConfigPtr)
87 {
88         UINTN i;
89         AGESA_STATUS CalloutStatus;
90
91         for (i = 0; i < REQUIRED_CALLOUTS; i++) {
92                 if (BiosCallouts[i].CalloutName == Func) {
93                         break;
94                 }
95         }
96
97         if(i >= REQUIRED_CALLOUTS) {
98                 return AGESA_UNSUPPORTED;
99         }
100
101         CalloutStatus = BiosCallouts[i].CalloutPtr (Func, Data, ConfigPtr);
102
103         return CalloutStatus;
104 }
105
106
107 CONST IDS_NV_ITEM IdsData[] =
108 {
109         /*{
110           AGESA_IDS_NV_MAIN_PLL_CON,
111           0x1
112           },
113           {
114           AGESA_IDS_NV_MAIN_PLL_FID_EN,
115           0x1
116           },
117           {
118           AGESA_IDS_NV_MAIN_PLL_FID,
119           0x8
120           },
121
122           {
123           AGESA_IDS_NV_CUSTOM_NB_PSTATE,
124           },
125           {
126           AGESA_IDS_NV_CUSTOM_NB_P0_DIV_CTRL,
127           },
128           {
129           AGESA_IDS_NV_CUSTOM_NB_P1_DIV_CTRL,
130           },
131           {
132           AGESA_IDS_NV_FORCE_NB_PSTATE,
133           },
134          */
135         {
136                 0xFFFF,
137                 0xFFFF
138         }
139 };
140
141 #define   NUM_IDS_ENTRIES    (sizeof (IdsData) / sizeof (IDS_NV_ITEM))
142
143 AGESA_STATUS BiosGetIdsInitData (UINT32 Func, UINT32 Data, VOID *ConfigPtr)
144 {
145         UINTN   i;
146         IDS_NV_ITEM *IdsPtr;
147
148         IdsPtr = ((IDS_CALLOUT_STRUCT *) ConfigPtr)->IdsNvPtr;
149
150         if (Data == IDS_CALLOUT_INIT) {
151                 for (i = 0; i < NUM_IDS_ENTRIES; i++) {
152                         IdsPtr[i].IdsNvValue = IdsData[i].IdsNvValue;
153                         IdsPtr[i].IdsNvId = IdsData[i].IdsNvId;
154                 }
155         }
156         return AGESA_SUCCESS;
157 }
158
159
160 AGESA_STATUS BiosAllocateBuffer (UINT32 Func, UINT32 Data, VOID *ConfigPtr)
161 {
162         UINT32              AvailableHeapSize;
163         UINT8               *BiosHeapBaseAddr;
164         UINT32              CurrNodeOffset;
165         UINT32              PrevNodeOffset;
166         UINT32              FreedNodeOffset;
167         UINT32              BestFitNodeOffset;
168         UINT32              BestFitPrevNodeOffset;
169         UINT32              NextFreeOffset;
170         BIOS_BUFFER_NODE   *CurrNodePtr;
171         BIOS_BUFFER_NODE   *FreedNodePtr;
172         BIOS_BUFFER_NODE   *BestFitNodePtr;
173         BIOS_BUFFER_NODE   *BestFitPrevNodePtr;
174         BIOS_BUFFER_NODE   *NextFreePtr;
175         BIOS_HEAP_MANAGER  *BiosHeapBasePtr;
176         AGESA_BUFFER_PARAMS *AllocParams;
177
178         AllocParams = ((AGESA_BUFFER_PARAMS *) ConfigPtr);
179         AllocParams->BufferPointer = NULL;
180
181         AvailableHeapSize = BIOS_HEAP_SIZE - sizeof (BIOS_HEAP_MANAGER);
182         BiosHeapBaseAddr = (UINT8 *) BIOS_HEAP_START_ADDRESS;
183         BiosHeapBasePtr = (BIOS_HEAP_MANAGER *) BIOS_HEAP_START_ADDRESS;
184
185         if (BiosHeapBasePtr->StartOfAllocatedNodes == 0) {
186                 /* First allocation */
187                 CurrNodeOffset = sizeof (BIOS_HEAP_MANAGER);
188                 CurrNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + CurrNodeOffset);
189                 CurrNodePtr->BufferHandle = AllocParams->BufferHandle;
190                 CurrNodePtr->BufferSize = AllocParams->BufferLength;
191                 CurrNodePtr->NextNodeOffset = 0;
192                 AllocParams->BufferPointer = (UINT8 *) CurrNodePtr + sizeof (BIOS_BUFFER_NODE);
193
194                 /* Update the remaining free space */
195                 FreedNodeOffset = CurrNodeOffset + CurrNodePtr->BufferSize + sizeof (BIOS_BUFFER_NODE);
196                 FreedNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + FreedNodeOffset);
197                 FreedNodePtr->BufferSize = AvailableHeapSize - sizeof (BIOS_BUFFER_NODE) - CurrNodePtr->BufferSize;
198                 FreedNodePtr->NextNodeOffset = 0;
199
200                 /* Update the offsets for Allocated and Freed nodes */
201                 BiosHeapBasePtr->StartOfAllocatedNodes = CurrNodeOffset;
202                 BiosHeapBasePtr->StartOfFreedNodes = FreedNodeOffset;
203         } else {
204                 /* Find out whether BufferHandle has been allocated on the heap. */
205                 /* If it has, return AGESA_BOUNDS_CHK */
206                 CurrNodeOffset = BiosHeapBasePtr->StartOfAllocatedNodes;
207                 CurrNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + CurrNodeOffset);
208
209                 while (CurrNodeOffset != 0) {
210                         CurrNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + CurrNodeOffset);
211                         if (CurrNodePtr->BufferHandle == AllocParams->BufferHandle) {
212                                 return AGESA_BOUNDS_CHK;
213                         }
214                         CurrNodeOffset = CurrNodePtr->NextNodeOffset;
215                         /* If BufferHandle has not been allocated on the heap, CurrNodePtr here points
216                            to the end of the allocated nodes list.
217                          */
218
219                 }
220                 /* Find the node that best fits the requested buffer size */
221                 FreedNodeOffset = BiosHeapBasePtr->StartOfFreedNodes;
222                 PrevNodeOffset = FreedNodeOffset;
223                 BestFitNodeOffset = 0;
224                 BestFitPrevNodeOffset = 0;
225                 while (FreedNodeOffset != 0) {
226                         FreedNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + FreedNodeOffset);
227                         if (FreedNodePtr->BufferSize >= (AllocParams->BufferLength + sizeof (BIOS_BUFFER_NODE))) {
228                                 if (BestFitNodeOffset == 0) {
229                                         /* First node that fits the requested buffer size */
230                                         BestFitNodeOffset = FreedNodeOffset;
231                                         BestFitPrevNodeOffset = PrevNodeOffset;
232                                 } else {
233                                         /* Find out whether current node is a better fit than the previous nodes */
234                                         BestFitNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + BestFitNodeOffset);
235                                         if (BestFitNodePtr->BufferSize > FreedNodePtr->BufferSize) {
236                                                 BestFitNodeOffset = FreedNodeOffset;
237                                                 BestFitPrevNodeOffset = PrevNodeOffset;
238                                         }
239                                 }
240                         }
241                         PrevNodeOffset = FreedNodeOffset;
242                         FreedNodeOffset = FreedNodePtr->NextNodeOffset;
243                 } /* end of while loop */
244
245
246                 if (BestFitNodeOffset == 0) {
247                         /* If we could not find a node that fits the requested buffer */
248                         /* size, return AGESA_BOUNDS_CHK */
249                         return AGESA_BOUNDS_CHK;
250                 } else {
251                         BestFitNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + BestFitNodeOffset);
252                         BestFitPrevNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + BestFitPrevNodeOffset);
253
254                         /* If BestFitNode is larger than the requested buffer, fragment the node further */
255                         if (BestFitNodePtr->BufferSize > (AllocParams->BufferLength + sizeof (BIOS_BUFFER_NODE))) {
256                                 NextFreeOffset = BestFitNodeOffset + AllocParams->BufferLength + sizeof (BIOS_BUFFER_NODE);
257
258                                 NextFreePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + NextFreeOffset);
259                                 NextFreePtr->BufferSize = BestFitNodePtr->BufferSize - (AllocParams->BufferLength + sizeof (BIOS_BUFFER_NODE));
260                                 NextFreePtr->NextNodeOffset = BestFitNodePtr->NextNodeOffset;
261                         } else {
262                                 /* Otherwise, next free node is NextNodeOffset of BestFitNode */
263                                 NextFreeOffset = BestFitNodePtr->NextNodeOffset;
264                         }
265
266                         /* If BestFitNode is the first buffer in the list, then update
267                            StartOfFreedNodes to reflect the new free node
268                          */
269                         if (BestFitNodeOffset == BiosHeapBasePtr->StartOfFreedNodes) {
270                                 BiosHeapBasePtr->StartOfFreedNodes = NextFreeOffset;
271                         } else {
272                                 BestFitPrevNodePtr->NextNodeOffset = NextFreeOffset;
273                         }
274
275                         /* Add BestFitNode to the list of Allocated nodes */
276                         CurrNodePtr->NextNodeOffset = BestFitNodeOffset;
277                         BestFitNodePtr->BufferSize = AllocParams->BufferLength;
278                         BestFitNodePtr->BufferHandle = AllocParams->BufferHandle;
279                         BestFitNodePtr->NextNodeOffset = 0;
280
281                         /* Remove BestFitNode from list of Freed nodes */
282                         AllocParams->BufferPointer = (UINT8 *) BestFitNodePtr + sizeof (BIOS_BUFFER_NODE);
283                 }
284         }
285
286         return AGESA_SUCCESS;
287 }
288
289 AGESA_STATUS BiosDeallocateBuffer (UINT32 Func, UINT32 Data, VOID *ConfigPtr)
290 {
291
292         UINT8               *BiosHeapBaseAddr;
293         UINT32              AllocNodeOffset;
294         UINT32              PrevNodeOffset;
295         UINT32              NextNodeOffset;
296         UINT32              FreedNodeOffset;
297         UINT32              EndNodeOffset;
298         BIOS_BUFFER_NODE   *AllocNodePtr;
299         BIOS_BUFFER_NODE   *PrevNodePtr;
300         BIOS_BUFFER_NODE   *FreedNodePtr;
301         BIOS_BUFFER_NODE   *NextNodePtr;
302         BIOS_HEAP_MANAGER  *BiosHeapBasePtr;
303         AGESA_BUFFER_PARAMS *AllocParams;
304
305         BiosHeapBaseAddr = (UINT8 *) BIOS_HEAP_START_ADDRESS;
306         BiosHeapBasePtr = (BIOS_HEAP_MANAGER *) BIOS_HEAP_START_ADDRESS;
307
308         AllocParams = (AGESA_BUFFER_PARAMS *) ConfigPtr;
309
310         /* Find target node to deallocate in list of allocated nodes.
311            Return AGESA_BOUNDS_CHK if the BufferHandle is not found
312          */
313         AllocNodeOffset = BiosHeapBasePtr->StartOfAllocatedNodes;
314         AllocNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + AllocNodeOffset);
315         PrevNodeOffset = AllocNodeOffset;
316
317         while (AllocNodePtr->BufferHandle !=  AllocParams->BufferHandle) {
318                 if (AllocNodePtr->NextNodeOffset == 0) {
319                         return AGESA_BOUNDS_CHK;
320                 }
321                 PrevNodeOffset = AllocNodeOffset;
322                 AllocNodeOffset = AllocNodePtr->NextNodeOffset;
323                 AllocNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + AllocNodeOffset);
324         }
325
326         /* Remove target node from list of allocated nodes */
327         PrevNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + PrevNodeOffset);
328         PrevNodePtr->NextNodeOffset = AllocNodePtr->NextNodeOffset;
329
330         /* Zero out the buffer, and clear the BufferHandle */
331         LibAmdMemFill ((UINT8 *)AllocNodePtr + sizeof (BIOS_BUFFER_NODE), 0, AllocNodePtr->BufferSize, &(AllocParams->StdHeader));
332         AllocNodePtr->BufferHandle = 0;
333         AllocNodePtr->BufferSize += sizeof (BIOS_BUFFER_NODE);
334
335         /* Add deallocated node in order to the list of freed nodes */
336         FreedNodeOffset = BiosHeapBasePtr->StartOfFreedNodes;
337         FreedNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + FreedNodeOffset);
338
339         EndNodeOffset = AllocNodeOffset + AllocNodePtr->BufferSize;
340
341         if (AllocNodeOffset < FreedNodeOffset) {
342                 /* Add to the start of the freed list */
343                 if (EndNodeOffset == FreedNodeOffset) {
344                         /* If the freed node is adjacent to the first node in the list, concatenate both nodes */
345                         AllocNodePtr->BufferSize += FreedNodePtr->BufferSize;
346                         AllocNodePtr->NextNodeOffset = FreedNodePtr->NextNodeOffset;
347
348                         /* Clear the BufferSize and NextNodeOffset of the previous first node */
349                         FreedNodePtr->BufferSize = 0;
350                         FreedNodePtr->NextNodeOffset = 0;
351
352                 } else {
353                         /* Otherwise, add freed node to the start of the list
354                            Update NextNodeOffset and BufferSize to include the
355                            size of BIOS_BUFFER_NODE
356                          */
357                         AllocNodePtr->NextNodeOffset = FreedNodeOffset;
358                 }
359                 /* Update StartOfFreedNodes to the new first node */
360                 BiosHeapBasePtr->StartOfFreedNodes = AllocNodeOffset;
361         } else {
362                 /* Traverse list of freed nodes to find where the deallocated node
363                    should be place
364                  */
365                 NextNodeOffset = FreedNodeOffset;
366                 NextNodePtr = FreedNodePtr;
367                 while (AllocNodeOffset > NextNodeOffset) {
368                         PrevNodeOffset = NextNodeOffset;
369                         if (NextNodePtr->NextNodeOffset == 0) {
370                                 break;
371                         }
372                         NextNodeOffset = NextNodePtr->NextNodeOffset;
373                         NextNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + NextNodeOffset);
374                 }
375
376                 /* If deallocated node is adjacent to the next node,
377                    concatenate both nodes
378                  */
379                 if (NextNodeOffset == EndNodeOffset) {
380                         NextNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + NextNodeOffset);
381                         AllocNodePtr->BufferSize += NextNodePtr->BufferSize;
382                         AllocNodePtr->NextNodeOffset = NextNodePtr->NextNodeOffset;
383
384                         NextNodePtr->BufferSize = 0;
385                         NextNodePtr->NextNodeOffset = 0;
386                 } else {
387                         /*AllocNodePtr->NextNodeOffset = FreedNodePtr->NextNodeOffset; */
388                         AllocNodePtr->NextNodeOffset = NextNodeOffset;
389                 }
390                 /* If deallocated node is adjacent to the previous node,
391                    concatenate both nodes
392                  */
393                 PrevNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + PrevNodeOffset);
394                 EndNodeOffset = PrevNodeOffset + PrevNodePtr->BufferSize;
395                 if (AllocNodeOffset == EndNodeOffset) {
396                         PrevNodePtr->NextNodeOffset = AllocNodePtr->NextNodeOffset;
397                         PrevNodePtr->BufferSize += AllocNodePtr->BufferSize;
398
399                         AllocNodePtr->BufferSize = 0;
400                         AllocNodePtr->NextNodeOffset = 0;
401                 } else {
402                         PrevNodePtr->NextNodeOffset = AllocNodeOffset;
403                 }
404         }
405         return AGESA_SUCCESS;
406 }
407
408 AGESA_STATUS BiosLocateBuffer (UINT32 Func, UINT32 Data, VOID *ConfigPtr)
409 {
410         UINT32              AllocNodeOffset;
411         UINT8               *BiosHeapBaseAddr;
412         BIOS_BUFFER_NODE   *AllocNodePtr;
413         BIOS_HEAP_MANAGER  *BiosHeapBasePtr;
414         AGESA_BUFFER_PARAMS *AllocParams;
415
416         AllocParams = (AGESA_BUFFER_PARAMS *) ConfigPtr;
417
418         BiosHeapBaseAddr = (UINT8 *) BIOS_HEAP_START_ADDRESS;
419         BiosHeapBasePtr = (BIOS_HEAP_MANAGER *) BIOS_HEAP_START_ADDRESS;
420
421         AllocNodeOffset = BiosHeapBasePtr->StartOfAllocatedNodes;
422         AllocNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + AllocNodeOffset);
423
424         while (AllocParams->BufferHandle != AllocNodePtr->BufferHandle) {
425                 if (AllocNodePtr->NextNodeOffset == 0) {
426                         AllocParams->BufferPointer = NULL;
427                         AllocParams->BufferLength = 0;
428                         return AGESA_BOUNDS_CHK;
429                 } else {
430                         AllocNodeOffset = AllocNodePtr->NextNodeOffset;
431                         AllocNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + AllocNodeOffset);
432                 }
433         }
434
435         AllocParams->BufferPointer = (UINT8 *) ((UINT8 *) AllocNodePtr + sizeof (BIOS_BUFFER_NODE));
436         AllocParams->BufferLength = AllocNodePtr->BufferSize;
437
438         return AGESA_SUCCESS;
439
440 }
441
442 AGESA_STATUS BiosRunFuncOnAp (UINT32 Func, UINT32 Data, VOID *ConfigPtr)
443 {
444         AGESA_STATUS        Status;
445
446         Status = agesawrapper_amdlaterunaptask (Data, ConfigPtr);
447         return Status;
448 }
449
450 AGESA_STATUS BiosReset (UINT32 Func, UINT32 Data, VOID *ConfigPtr)
451 {
452         AGESA_STATUS        Status;
453         UINT8                 Value;
454         UINTN               ResetType;
455         AMD_CONFIG_PARAMS   *StdHeader;
456
457         ResetType = Data;
458         StdHeader = ConfigPtr;
459
460         //
461         // Perform the RESET based upon the ResetType. In case of
462         // WARM_RESET_WHENVER and COLD_RESET_WHENEVER, the request will go to
463         // AmdResetManager. During the critical condition, where reset is required
464         // immediately, the reset will be invoked directly by writing 0x04 to port
465         // 0xCF9 (Reset Port).
466         //
467         switch (ResetType) {
468                 case WARM_RESET_WHENEVER:
469                 case COLD_RESET_WHENEVER:
470                         break;
471
472                 case WARM_RESET_IMMEDIATELY:
473                 case COLD_RESET_IMMEDIATELY:
474                         Value = 0x06;
475                         LibAmdIoWrite (AccessWidth8, 0xCf9, &Value, StdHeader);
476                         break;
477
478                 default:
479                         break;
480         }
481
482         Status = 0;
483         return Status;
484 }
485
486 AGESA_STATUS BiosReadSpd (UINT32 Func, UINT32 Data, VOID *ConfigPtr)
487 {
488         AGESA_STATUS Status;
489         Status = AmdMemoryReadSPD (Func, Data, ConfigPtr);
490
491         return Status;
492 }
493
494 AGESA_STATUS BiosDefaultRet (UINT32 Func, UINT32 Data, VOID *ConfigPtr)
495 {
496         return AGESA_UNSUPPORTED;
497 }
498
499 /*  Call the host environment interface to provide a user hook opportunity. */
500 AGESA_STATUS BiosHookBeforeDQSTraining (UINT32 Func, UINT32 Data, VOID *ConfigPtr)
501 {
502         return AGESA_SUCCESS;
503 }
504
505 /*  Call the host environment interface to provide a user hook opportunity. */
506 AGESA_STATUS BiosHookBeforeDramInit (UINT32 Func, UINT32 Data, VOID *ConfigPtr)
507 {
508         return AGESA_SUCCESS;
509 }
510
511 /*  Call the host environment interface to provide a user hook opportunity. */
512 AGESA_STATUS BiosHookBeforeExitSelfRefresh (UINT32 Func, UINT32 Data, VOID *ConfigPtr)
513 {
514         return AGESA_SUCCESS;
515 }
516