2 * This file is part of the coreboot project.
4 * Copyright (C) 2011 Advanced Micro Devices, Inc.
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.
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.
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
20 #include "agesawrapper.h"
22 #include "BiosCallOuts.h"
24 #include "OptionsIds.h"
25 #include "heapManager.h"
27 STATIC BIOS_CALLOUT_STRUCT BiosCallouts[REQUIRED_CALLOUTS] =
30 AGESA_ALLOCATE_BUFFER,
35 AGESA_DEALLOCATE_BUFFER,
55 AGESA_READ_SPD_RECOVERY,
65 AGESA_GET_IDS_INIT_DATA,
70 AGESA_HOOKBEFORE_DQS_TRAINING,
71 BiosHookBeforeDQSTraining
75 AGESA_HOOKBEFORE_DRAM_INIT,
76 BiosHookBeforeDramInit
79 AGESA_HOOKBEFORE_EXIT_SELF_REF,
80 BiosHookBeforeExitSelfRefresh
84 extern AGESA_STATUS AmdMemoryReadSPD (UINT32 unused1, UINT32 unused2, AGESA_READ_SPD_PARAMS *info);
86 AGESA_STATUS GetBiosCallout (UINT32 Func, UINT32 Data, VOID *ConfigPtr)
89 AGESA_STATUS CalloutStatus;
91 for (i = 0; i < REQUIRED_CALLOUTS; i++) {
92 if (BiosCallouts[i].CalloutName == Func) {
97 if(i >= REQUIRED_CALLOUTS) {
98 return AGESA_UNSUPPORTED;
101 CalloutStatus = BiosCallouts[i].CalloutPtr (Func, Data, ConfigPtr);
103 return CalloutStatus;
107 CONST IDS_NV_ITEM IdsData[] =
110 AGESA_IDS_NV_MAIN_PLL_CON,
114 AGESA_IDS_NV_MAIN_PLL_FID_EN,
118 AGESA_IDS_NV_MAIN_PLL_FID,
123 AGESA_IDS_NV_CUSTOM_NB_PSTATE,
126 AGESA_IDS_NV_CUSTOM_NB_P0_DIV_CTRL,
129 AGESA_IDS_NV_CUSTOM_NB_P1_DIV_CTRL,
132 AGESA_IDS_NV_FORCE_NB_PSTATE,
141 #define NUM_IDS_ENTRIES (sizeof (IdsData) / sizeof (IDS_NV_ITEM))
143 AGESA_STATUS BiosGetIdsInitData (UINT32 Func, UINT32 Data, VOID *ConfigPtr)
148 IdsPtr = ((IDS_CALLOUT_STRUCT *) ConfigPtr)->IdsNvPtr;
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;
156 return AGESA_SUCCESS;
160 AGESA_STATUS BiosAllocateBuffer (UINT32 Func, UINT32 Data, VOID *ConfigPtr)
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;
178 AllocParams = ((AGESA_BUFFER_PARAMS *) ConfigPtr);
179 AllocParams->BufferPointer = NULL;
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;
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);
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;
200 /* Update the offsets for Allocated and Freed nodes */
201 BiosHeapBasePtr->StartOfAllocatedNodes = CurrNodeOffset;
202 BiosHeapBasePtr->StartOfFreedNodes = FreedNodeOffset;
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);
209 while (CurrNodeOffset != 0) {
210 CurrNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + CurrNodeOffset);
211 if (CurrNodePtr->BufferHandle == AllocParams->BufferHandle) {
212 return AGESA_BOUNDS_CHK;
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.
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;
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;
241 PrevNodeOffset = FreedNodeOffset;
242 FreedNodeOffset = FreedNodePtr->NextNodeOffset;
243 } /* end of while loop */
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;
251 BestFitNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + BestFitNodeOffset);
252 BestFitPrevNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + BestFitPrevNodeOffset);
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);
258 NextFreePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + NextFreeOffset);
259 NextFreePtr->BufferSize = BestFitNodePtr->BufferSize - (AllocParams->BufferLength + sizeof (BIOS_BUFFER_NODE));
260 NextFreePtr->NextNodeOffset = BestFitNodePtr->NextNodeOffset;
262 /* Otherwise, next free node is NextNodeOffset of BestFitNode */
263 NextFreeOffset = BestFitNodePtr->NextNodeOffset;
266 /* If BestFitNode is the first buffer in the list, then update
267 StartOfFreedNodes to reflect the new free node
269 if (BestFitNodeOffset == BiosHeapBasePtr->StartOfFreedNodes) {
270 BiosHeapBasePtr->StartOfFreedNodes = NextFreeOffset;
272 BestFitPrevNodePtr->NextNodeOffset = NextFreeOffset;
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;
281 /* Remove BestFitNode from list of Freed nodes */
282 AllocParams->BufferPointer = (UINT8 *) BestFitNodePtr + sizeof (BIOS_BUFFER_NODE);
286 return AGESA_SUCCESS;
289 AGESA_STATUS BiosDeallocateBuffer (UINT32 Func, UINT32 Data, VOID *ConfigPtr)
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;
305 BiosHeapBaseAddr = (UINT8 *) BIOS_HEAP_START_ADDRESS;
306 BiosHeapBasePtr = (BIOS_HEAP_MANAGER *) BIOS_HEAP_START_ADDRESS;
308 AllocParams = (AGESA_BUFFER_PARAMS *) ConfigPtr;
310 /* Find target node to deallocate in list of allocated nodes.
311 Return AGESA_BOUNDS_CHK if the BufferHandle is not found
313 AllocNodeOffset = BiosHeapBasePtr->StartOfAllocatedNodes;
314 AllocNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + AllocNodeOffset);
315 PrevNodeOffset = AllocNodeOffset;
317 while (AllocNodePtr->BufferHandle != AllocParams->BufferHandle) {
318 if (AllocNodePtr->NextNodeOffset == 0) {
319 return AGESA_BOUNDS_CHK;
321 PrevNodeOffset = AllocNodeOffset;
322 AllocNodeOffset = AllocNodePtr->NextNodeOffset;
323 AllocNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + AllocNodeOffset);
326 /* Remove target node from list of allocated nodes */
327 PrevNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + PrevNodeOffset);
328 PrevNodePtr->NextNodeOffset = AllocNodePtr->NextNodeOffset;
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);
335 /* Add deallocated node in order to the list of freed nodes */
336 FreedNodeOffset = BiosHeapBasePtr->StartOfFreedNodes;
337 FreedNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + FreedNodeOffset);
339 EndNodeOffset = AllocNodeOffset + AllocNodePtr->BufferSize;
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;
348 /* Clear the BufferSize and NextNodeOffset of the previous first node */
349 FreedNodePtr->BufferSize = 0;
350 FreedNodePtr->NextNodeOffset = 0;
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
357 AllocNodePtr->NextNodeOffset = FreedNodeOffset;
359 /* Update StartOfFreedNodes to the new first node */
360 BiosHeapBasePtr->StartOfFreedNodes = AllocNodeOffset;
362 /* Traverse list of freed nodes to find where the deallocated node
365 NextNodeOffset = FreedNodeOffset;
366 NextNodePtr = FreedNodePtr;
367 while (AllocNodeOffset > NextNodeOffset) {
368 PrevNodeOffset = NextNodeOffset;
369 if (NextNodePtr->NextNodeOffset == 0) {
372 NextNodeOffset = NextNodePtr->NextNodeOffset;
373 NextNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + NextNodeOffset);
376 /* If deallocated node is adjacent to the next node,
377 concatenate both nodes
379 if (NextNodeOffset == EndNodeOffset) {
380 NextNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + NextNodeOffset);
381 AllocNodePtr->BufferSize += NextNodePtr->BufferSize;
382 AllocNodePtr->NextNodeOffset = NextNodePtr->NextNodeOffset;
384 NextNodePtr->BufferSize = 0;
385 NextNodePtr->NextNodeOffset = 0;
387 /*AllocNodePtr->NextNodeOffset = FreedNodePtr->NextNodeOffset; */
388 AllocNodePtr->NextNodeOffset = NextNodeOffset;
390 /* If deallocated node is adjacent to the previous node,
391 concatenate both nodes
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;
399 AllocNodePtr->BufferSize = 0;
400 AllocNodePtr->NextNodeOffset = 0;
402 PrevNodePtr->NextNodeOffset = AllocNodeOffset;
405 return AGESA_SUCCESS;
408 AGESA_STATUS BiosLocateBuffer (UINT32 Func, UINT32 Data, VOID *ConfigPtr)
410 UINT32 AllocNodeOffset;
411 UINT8 *BiosHeapBaseAddr;
412 BIOS_BUFFER_NODE *AllocNodePtr;
413 BIOS_HEAP_MANAGER *BiosHeapBasePtr;
414 AGESA_BUFFER_PARAMS *AllocParams;
416 AllocParams = (AGESA_BUFFER_PARAMS *) ConfigPtr;
418 BiosHeapBaseAddr = (UINT8 *) BIOS_HEAP_START_ADDRESS;
419 BiosHeapBasePtr = (BIOS_HEAP_MANAGER *) BIOS_HEAP_START_ADDRESS;
421 AllocNodeOffset = BiosHeapBasePtr->StartOfAllocatedNodes;
422 AllocNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + AllocNodeOffset);
424 while (AllocParams->BufferHandle != AllocNodePtr->BufferHandle) {
425 if (AllocNodePtr->NextNodeOffset == 0) {
426 AllocParams->BufferPointer = NULL;
427 AllocParams->BufferLength = 0;
428 return AGESA_BOUNDS_CHK;
430 AllocNodeOffset = AllocNodePtr->NextNodeOffset;
431 AllocNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + AllocNodeOffset);
435 AllocParams->BufferPointer = (UINT8 *) ((UINT8 *) AllocNodePtr + sizeof (BIOS_BUFFER_NODE));
436 AllocParams->BufferLength = AllocNodePtr->BufferSize;
438 return AGESA_SUCCESS;
442 AGESA_STATUS BiosRunFuncOnAp (UINT32 Func, UINT32 Data, VOID *ConfigPtr)
446 Status = agesawrapper_amdlaterunaptask (Data, ConfigPtr);
450 AGESA_STATUS BiosReset (UINT32 Func, UINT32 Data, VOID *ConfigPtr)
455 AMD_CONFIG_PARAMS *StdHeader;
458 StdHeader = ConfigPtr;
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).
468 case WARM_RESET_WHENEVER:
469 case COLD_RESET_WHENEVER:
472 case WARM_RESET_IMMEDIATELY:
473 case COLD_RESET_IMMEDIATELY:
475 LibAmdIoWrite (AccessWidth8, 0xCf9, &Value, StdHeader);
486 AGESA_STATUS BiosReadSpd (UINT32 Func, UINT32 Data, VOID *ConfigPtr)
489 Status = AmdMemoryReadSPD (Func, Data, ConfigPtr);
494 AGESA_STATUS BiosDefaultRet (UINT32 Func, UINT32 Data, VOID *ConfigPtr)
496 return AGESA_UNSUPPORTED;
499 /* Call the host environment interface to provide a user hook opportunity. */
500 AGESA_STATUS BiosHookBeforeDQSTraining (UINT32 Func, UINT32 Data, VOID *ConfigPtr)
502 return AGESA_SUCCESS;
505 /* Call the host environment interface to provide a user hook opportunity. */
506 AGESA_STATUS BiosHookBeforeDramInit (UINT32 Func, UINT32 Data, VOID *ConfigPtr)
508 return AGESA_SUCCESS;
511 /* Call the host environment interface to provide a user hook opportunity. */
512 AGESA_STATUS BiosHookBeforeExitSelfRefresh (UINT32 Func, UINT32 Data, VOID *ConfigPtr)
514 return AGESA_SUCCESS;