blob: cf84b3977c48668a5ce266ceebdb569067fd2830 [file] [log] [blame]
Martin Roth28920232013-01-17 12:04:08 -07001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright (C) 2012 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
Paul Menzela46a7122013-02-23 18:37:27 +010017 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Martin Roth28920232013-01-17 12:04:08 -070018 */
19
20#include "agesawrapper.h"
21#include "amdlib.h"
22#include "Ids.h"
23#include "OptionsIds.h"
24#include "heapManager.h"
25#include "FchPlatform.h"
26#include "cbfs.h"
Martin Roth7fb692b2013-01-20 10:38:58 -070027#include "dimmSpd.h"
Martin Roth28920232013-01-17 12:04:08 -070028#include "fam15tn_callouts.h"
Rudolf Marek5ce05062013-05-27 16:09:44 +020029#include <cbmem.h>
30
31#define AGESA_RUNTIME_SIZE 4096
32
33static AGESA_STATUS alloc_cbmem(AGESA_BUFFER_PARAMS *AllocParams) {
34 static unsigned int used = 0;
35 void *p = cbmem_find(CBMEM_ID_AGESA_RUNTIME);
36
37 if ((AGESA_RUNTIME_SIZE - used) < AllocParams->BufferLength) {
38 return AGESA_BOUNDS_CHK;
39 }
40
41 /* first time allocation */
42 if (!p) {
43 p = cbmem_add(CBMEM_ID_AGESA_RUNTIME, AGESA_RUNTIME_SIZE);
44 if (!p)
45 return AGESA_BOUNDS_CHK;
46 }
47
48 AllocParams->BufferPointer = p + used;
49 used += AllocParams->BufferLength;
50 return AGESA_SUCCESS;
51}
Martin Roth28920232013-01-17 12:04:08 -070052
53AGESA_STATUS fam15tn_AllocateBuffer (UINT32 Func, UINT32 Data, VOID *ConfigPtr)
54{
55 UINT32 AvailableHeapSize;
56 UINT8 *BiosHeapBaseAddr;
57 UINT32 CurrNodeOffset;
58 UINT32 PrevNodeOffset;
59 UINT32 FreedNodeOffset;
60 UINT32 BestFitNodeOffset;
61 UINT32 BestFitPrevNodeOffset;
62 UINT32 NextFreeOffset;
63 BIOS_BUFFER_NODE *CurrNodePtr;
64 BIOS_BUFFER_NODE *FreedNodePtr;
65 BIOS_BUFFER_NODE *BestFitNodePtr;
66 BIOS_BUFFER_NODE *BestFitPrevNodePtr;
67 BIOS_BUFFER_NODE *NextFreePtr;
68 BIOS_HEAP_MANAGER *BiosHeapBasePtr;
69 AGESA_BUFFER_PARAMS *AllocParams;
70
71 AllocParams = ((AGESA_BUFFER_PARAMS *) ConfigPtr);
72 AllocParams->BufferPointer = NULL;
73
Rudolf Marek5ce05062013-05-27 16:09:44 +020074 /* if the allocation is for runtime use simple CBMEM data */
75 if (Data == HEAP_CALLOUT_RUNTIME)
76 return alloc_cbmem(AllocParams);
77
Martin Roth28920232013-01-17 12:04:08 -070078 AvailableHeapSize = BIOS_HEAP_SIZE - sizeof (BIOS_HEAP_MANAGER);
79 BiosHeapBaseAddr = (UINT8 *) GetHeapBase(&(AllocParams->StdHeader));
80 BiosHeapBasePtr = (BIOS_HEAP_MANAGER *) BiosHeapBaseAddr;
81
82 if (BiosHeapBasePtr->StartOfAllocatedNodes == 0) {
83 /* First allocation */
84 CurrNodeOffset = sizeof (BIOS_HEAP_MANAGER);
85 CurrNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + CurrNodeOffset);
86 CurrNodePtr->BufferHandle = AllocParams->BufferHandle;
87 CurrNodePtr->BufferSize = AllocParams->BufferLength;
88 CurrNodePtr->NextNodeOffset = 0;
89 AllocParams->BufferPointer = (UINT8 *) CurrNodePtr + sizeof (BIOS_BUFFER_NODE);
90
91 /* Update the remaining free space */
92 FreedNodeOffset = CurrNodeOffset + CurrNodePtr->BufferSize + sizeof (BIOS_BUFFER_NODE);
93 FreedNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + FreedNodeOffset);
94 FreedNodePtr->BufferSize = AvailableHeapSize - sizeof (BIOS_BUFFER_NODE) - CurrNodePtr->BufferSize;
95 FreedNodePtr->NextNodeOffset = 0;
96
97 /* Update the offsets for Allocated and Freed nodes */
98 BiosHeapBasePtr->StartOfAllocatedNodes = CurrNodeOffset;
99 BiosHeapBasePtr->StartOfFreedNodes = FreedNodeOffset;
100 } else {
101 /* Find out whether BufferHandle has been allocated on the heap. */
102 /* If it has, return AGESA_BOUNDS_CHK */
103 CurrNodeOffset = BiosHeapBasePtr->StartOfAllocatedNodes;
104 CurrNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + CurrNodeOffset);
105
106 while (CurrNodeOffset != 0) {
107 CurrNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + CurrNodeOffset);
108 if (CurrNodePtr->BufferHandle == AllocParams->BufferHandle) {
109 return AGESA_BOUNDS_CHK;
110 }
111 CurrNodeOffset = CurrNodePtr->NextNodeOffset;
112 /* If BufferHandle has not been allocated on the heap, CurrNodePtr here points
113 to the end of the allocated nodes list.
114 */
115
116 }
117 /* Find the node that best fits the requested buffer size */
118 FreedNodeOffset = BiosHeapBasePtr->StartOfFreedNodes;
119 PrevNodeOffset = FreedNodeOffset;
120 BestFitNodeOffset = 0;
121 BestFitPrevNodeOffset = 0;
122 while (FreedNodeOffset != 0) {
123 FreedNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + FreedNodeOffset);
124 if (FreedNodePtr->BufferSize >= (AllocParams->BufferLength + sizeof (BIOS_BUFFER_NODE))) {
125 if (BestFitNodeOffset == 0) {
126 /* First node that fits the requested buffer size */
127 BestFitNodeOffset = FreedNodeOffset;
128 BestFitPrevNodeOffset = PrevNodeOffset;
129 } else {
130 /* Find out whether current node is a better fit than the previous nodes */
131 BestFitNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + BestFitNodeOffset);
132 if (BestFitNodePtr->BufferSize > FreedNodePtr->BufferSize) {
133 BestFitNodeOffset = FreedNodeOffset;
134 BestFitPrevNodeOffset = PrevNodeOffset;
135 }
136 }
137 }
138 PrevNodeOffset = FreedNodeOffset;
139 FreedNodeOffset = FreedNodePtr->NextNodeOffset;
140 } /* end of while loop */
141
142 if (BestFitNodeOffset == 0) {
143 /* If we could not find a node that fits the requested buffer */
144 /* size, return AGESA_BOUNDS_CHK */
145 return AGESA_BOUNDS_CHK;
146 } else {
147 BestFitNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + BestFitNodeOffset);
148 BestFitPrevNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + BestFitPrevNodeOffset);
149
150 /* If BestFitNode is larger than the requested buffer, fragment the node further */
151 if (BestFitNodePtr->BufferSize > (AllocParams->BufferLength + sizeof (BIOS_BUFFER_NODE))) {
152 NextFreeOffset = BestFitNodeOffset + AllocParams->BufferLength + sizeof (BIOS_BUFFER_NODE);
153
154 NextFreePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + NextFreeOffset);
155 NextFreePtr->BufferSize = BestFitNodePtr->BufferSize - (AllocParams->BufferLength + sizeof (BIOS_BUFFER_NODE));
156 NextFreePtr->NextNodeOffset = BestFitNodePtr->NextNodeOffset;
157 } else {
158 /* Otherwise, next free node is NextNodeOffset of BestFitNode */
159 NextFreeOffset = BestFitNodePtr->NextNodeOffset;
160 }
161
162 /* If BestFitNode is the first buffer in the list, then update
163 StartOfFreedNodes to reflect the new free node
164 */
165 if (BestFitNodeOffset == BiosHeapBasePtr->StartOfFreedNodes) {
166 BiosHeapBasePtr->StartOfFreedNodes = NextFreeOffset;
167 } else {
168 BestFitPrevNodePtr->NextNodeOffset = NextFreeOffset;
169 }
170
171 /* Add BestFitNode to the list of Allocated nodes */
172 CurrNodePtr->NextNodeOffset = BestFitNodeOffset;
173 BestFitNodePtr->BufferSize = AllocParams->BufferLength;
174 BestFitNodePtr->BufferHandle = AllocParams->BufferHandle;
175 BestFitNodePtr->NextNodeOffset = 0;
176
177 /* Remove BestFitNode from list of Freed nodes */
178 AllocParams->BufferPointer = (UINT8 *) BestFitNodePtr + sizeof (BIOS_BUFFER_NODE);
179 }
180 }
181
182 return AGESA_SUCCESS;
183}
184
185AGESA_STATUS fam15tn_DeallocateBuffer (UINT32 Func, UINT32 Data, VOID *ConfigPtr)
186{
187
188 UINT8 *BiosHeapBaseAddr;
189 UINT32 AllocNodeOffset;
190 UINT32 PrevNodeOffset;
191 UINT32 NextNodeOffset;
192 UINT32 FreedNodeOffset;
193 UINT32 EndNodeOffset;
194 BIOS_BUFFER_NODE *AllocNodePtr;
195 BIOS_BUFFER_NODE *PrevNodePtr;
196 BIOS_BUFFER_NODE *FreedNodePtr;
197 BIOS_BUFFER_NODE *NextNodePtr;
198 BIOS_HEAP_MANAGER *BiosHeapBasePtr;
199 AGESA_BUFFER_PARAMS *AllocParams;
200
201 AllocParams = (AGESA_BUFFER_PARAMS *) ConfigPtr;
202
203 BiosHeapBaseAddr = (UINT8 *) GetHeapBase(&(AllocParams->StdHeader));
204 BiosHeapBasePtr = (BIOS_HEAP_MANAGER *) BiosHeapBaseAddr;
205
206 /* Find target node to deallocate in list of allocated nodes.
207 Return AGESA_BOUNDS_CHK if the BufferHandle is not found
208 */
209 AllocNodeOffset = BiosHeapBasePtr->StartOfAllocatedNodes;
210 AllocNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + AllocNodeOffset);
211 PrevNodeOffset = AllocNodeOffset;
212
213 while (AllocNodePtr->BufferHandle != AllocParams->BufferHandle) {
214 if (AllocNodePtr->NextNodeOffset == 0) {
215 return AGESA_BOUNDS_CHK;
216 }
217 PrevNodeOffset = AllocNodeOffset;
218 AllocNodeOffset = AllocNodePtr->NextNodeOffset;
219 AllocNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + AllocNodeOffset);
220 }
221
222 /* Remove target node from list of allocated nodes */
223 PrevNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + PrevNodeOffset);
224 PrevNodePtr->NextNodeOffset = AllocNodePtr->NextNodeOffset;
225
226 /* Zero out the buffer, and clear the BufferHandle */
227 LibAmdMemFill ((UINT8 *)AllocNodePtr + sizeof (BIOS_BUFFER_NODE), 0, AllocNodePtr->BufferSize, &(AllocParams->StdHeader));
228 AllocNodePtr->BufferHandle = 0;
229 AllocNodePtr->BufferSize += sizeof (BIOS_BUFFER_NODE);
230
231 /* Add deallocated node in order to the list of freed nodes */
232 FreedNodeOffset = BiosHeapBasePtr->StartOfFreedNodes;
233 FreedNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + FreedNodeOffset);
234
235 EndNodeOffset = AllocNodeOffset + AllocNodePtr->BufferSize;
236
237 if (AllocNodeOffset < FreedNodeOffset) {
238 /* Add to the start of the freed list */
239 if (EndNodeOffset == FreedNodeOffset) {
240 /* If the freed node is adjacent to the first node in the list, concatenate both nodes */
241 AllocNodePtr->BufferSize += FreedNodePtr->BufferSize;
242 AllocNodePtr->NextNodeOffset = FreedNodePtr->NextNodeOffset;
243
244 /* Clear the BufferSize and NextNodeOffset of the previous first node */
245 FreedNodePtr->BufferSize = 0;
246 FreedNodePtr->NextNodeOffset = 0;
247
248 } else {
249 /* Otherwise, add freed node to the start of the list
250 Update NextNodeOffset and BufferSize to include the
251 size of BIOS_BUFFER_NODE
252 */
253 AllocNodePtr->NextNodeOffset = FreedNodeOffset;
254 }
255 /* Update StartOfFreedNodes to the new first node */
256 BiosHeapBasePtr->StartOfFreedNodes = AllocNodeOffset;
257 } else {
258 /* Traverse list of freed nodes to find where the deallocated node
259 should be place
260 */
261 NextNodeOffset = FreedNodeOffset;
262 NextNodePtr = FreedNodePtr;
263 while (AllocNodeOffset > NextNodeOffset) {
264 PrevNodeOffset = NextNodeOffset;
265 if (NextNodePtr->NextNodeOffset == 0) {
266 break;
267 }
268 NextNodeOffset = NextNodePtr->NextNodeOffset;
269 NextNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + NextNodeOffset);
270 }
271
272 /* If deallocated node is adjacent to the next node,
273 concatenate both nodes
274 */
275 if (NextNodeOffset == EndNodeOffset) {
276 NextNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + NextNodeOffset);
277 AllocNodePtr->BufferSize += NextNodePtr->BufferSize;
278 AllocNodePtr->NextNodeOffset = NextNodePtr->NextNodeOffset;
279
280 NextNodePtr->BufferSize = 0;
281 NextNodePtr->NextNodeOffset = 0;
282 } else {
283 /*AllocNodePtr->NextNodeOffset = FreedNodePtr->NextNodeOffset; */
284 AllocNodePtr->NextNodeOffset = NextNodeOffset;
285 }
286 /* If deallocated node is adjacent to the previous node,
287 concatenate both nodes
288 */
289 PrevNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + PrevNodeOffset);
290 EndNodeOffset = PrevNodeOffset + PrevNodePtr->BufferSize;
291 if (AllocNodeOffset == EndNodeOffset) {
292 PrevNodePtr->NextNodeOffset = AllocNodePtr->NextNodeOffset;
293 PrevNodePtr->BufferSize += AllocNodePtr->BufferSize;
294
295 AllocNodePtr->BufferSize = 0;
296 AllocNodePtr->NextNodeOffset = 0;
297 } else {
298 PrevNodePtr->NextNodeOffset = AllocNodeOffset;
299 }
300 }
301 return AGESA_SUCCESS;
302}
303
304AGESA_STATUS fam15tn_LocateBuffer (UINT32 Func, UINT32 Data, VOID *ConfigPtr)
305{
306 UINT32 AllocNodeOffset;
307 UINT8 *BiosHeapBaseAddr;
308 BIOS_BUFFER_NODE *AllocNodePtr;
309 BIOS_HEAP_MANAGER *BiosHeapBasePtr;
310 AGESA_BUFFER_PARAMS *AllocParams;
311
312 AllocParams = (AGESA_BUFFER_PARAMS *) ConfigPtr;
313
314 BiosHeapBaseAddr = (UINT8 *) GetHeapBase(&(AllocParams->StdHeader));
315 BiosHeapBasePtr = (BIOS_HEAP_MANAGER *) BiosHeapBaseAddr;
316
317 AllocNodeOffset = BiosHeapBasePtr->StartOfAllocatedNodes;
318 AllocNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + AllocNodeOffset);
319
320 while (AllocParams->BufferHandle != AllocNodePtr->BufferHandle) {
321 if (AllocNodePtr->NextNodeOffset == 0) {
322 AllocParams->BufferPointer = NULL;
323 AllocParams->BufferLength = 0;
324 return AGESA_BOUNDS_CHK;
325 } else {
326 AllocNodeOffset = AllocNodePtr->NextNodeOffset;
327 AllocNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + AllocNodeOffset);
328 }
329 }
330
331 AllocParams->BufferPointer = (UINT8 *) ((UINT8 *) AllocNodePtr + sizeof (BIOS_BUFFER_NODE));
332 AllocParams->BufferLength = AllocNodePtr->BufferSize;
333
334 return AGESA_SUCCESS;
335
336}
337
338CONST IDS_NV_ITEM IdsData[] =
339{
340 {
341 0xFFFF,
342 0xFFFF
343 }
344};
345
346#define NUM_IDS_ENTRIES (sizeof (IdsData) / sizeof (IDS_NV_ITEM))
347
348AGESA_STATUS fam15tn_GetIdsInitData (UINT32 Func, UINT32 Data, VOID *ConfigPtr)
349{
350 UINTN i;
351 IDS_NV_ITEM *IdsPtr;
352
353 IdsPtr = ((IDS_CALLOUT_STRUCT *) ConfigPtr)->IdsNvPtr;
354
355 if (Data == IDS_CALLOUT_INIT) {
356 for (i = 0; i < NUM_IDS_ENTRIES; i++) {
357 IdsPtr[i].IdsNvValue = IdsData[i].IdsNvValue;
358 IdsPtr[i].IdsNvId = IdsData[i].IdsNvId;
359 }
360 }
361 return AGESA_SUCCESS;
362}
363
364AGESA_STATUS fam15tn_Reset (UINT32 Func, UINT32 Data, VOID *ConfigPtr)
365{
366 AGESA_STATUS Status;
367 UINT8 Value;
368 UINTN ResetType;
369 AMD_CONFIG_PARAMS *StdHeader;
370
371 ResetType = Data;
372 StdHeader = ConfigPtr;
373
374 //
375 // Perform the RESET based upon the ResetType. In case of
376 // WARM_RESET_WHENVER and COLD_RESET_WHENEVER, the request will go to
377 // AmdResetManager. During the critical condition, where reset is required
378 // immediately, the reset will be invoked directly by writing 0x04 to port
379 // 0xCF9 (Reset Port).
380 //
381 switch (ResetType) {
382 case WARM_RESET_WHENEVER:
383 case COLD_RESET_WHENEVER:
384 break;
385
386 case WARM_RESET_IMMEDIATELY:
387 case COLD_RESET_IMMEDIATELY:
388 Value = 0x06;
389 LibAmdIoWrite (AccessWidth8, 0xCf9, &Value, StdHeader);
390 break;
391
392 default:
393 break;
394 }
395
396 Status = 0;
397 return Status;
398}
399
400AGESA_STATUS fam15tn_RunFuncOnAp (UINT32 Func, UINT32 Data, VOID *ConfigPtr)
401{
402 AGESA_STATUS Status;
403
404 Status = agesawrapper_amdlaterunaptask (Func, Data, ConfigPtr);
405 return Status;
406}
407
408/* Call the host environment interface to provide a user hook opportunity. */
409AGESA_STATUS fam15tn_HookBeforeDQSTraining (UINT32 Func, UINT32 Data, VOID *ConfigPtr)
410{
411 return AGESA_SUCCESS;
412}
413
414/* Call the host environment interface to provide a user hook opportunity. */
415AGESA_STATUS fam15tn_HookBeforeExitSelfRefresh (UINT32 Func, UINT32 Data, VOID *ConfigPtr)
416{
417 return AGESA_SUCCESS;
418}
419
420AGESA_STATUS fam15tn_DefaultRet (UINT32 Func, UINT32 Data, VOID *ConfigPtr)
421{
422 return AGESA_UNSUPPORTED;
423}
424
425
426AGESA_STATUS fam15tn_HookGfxGetVbiosImage(UINT32 Func, UINT32 FchData, VOID *ConfigPrt)
427{
428 GFX_VBIOS_IMAGE_INFO *pVbiosImageInfo = (GFX_VBIOS_IMAGE_INFO *)ConfigPrt;
Hung-Te Lin6fe0cab2013-01-22 18:57:56 +0800429 pVbiosImageInfo->ImagePtr = cbfs_get_file_content(
430 CBFS_DEFAULT_MEDIA, "pci"CONFIG_VGA_BIOS_ID".rom",
Vladimir Serbinenko0af61b62014-01-12 13:45:52 +0100431 CBFS_TYPE_OPTIONROM, NULL);
Martin Roth28920232013-01-17 12:04:08 -0700432 /* printk(BIOS_DEBUG, "IMGptr=%x\n", pVbiosImageInfo->ImagePtr); */
433 return pVbiosImageInfo->ImagePtr == NULL ? AGESA_WARNING : AGESA_SUCCESS;
434}
435
Martin Roth7fb692b2013-01-20 10:38:58 -0700436AGESA_STATUS fam15tn_ReadSpd (UINT32 Func, UINT32 Data, VOID *ConfigPtr)
437{
438 AGESA_STATUS Status;
439 Status = AmdMemoryReadSPD (Func, Data, ConfigPtr);
440
441 return Status;
442}