]> git.proxmox.com Git - mirror_edk2.git/blame - QuarkSocPkg/QuarkSouthCluster/Usb/Ohci/Pei/UsbHcMem.c
QuarkSocPkg: Add new package for Quark SoC X1000
[mirror_edk2.git] / QuarkSocPkg / QuarkSouthCluster / Usb / Ohci / Pei / UsbHcMem.c
CommitLineData
9b6bbcdb
MK
1/** @file\r
2Routine procedures for memory allocate/free.\r
3\r
4Copyright (c) 2013-2015 Intel Corporation.\r
5\r
6This program and the accompanying materials\r
7are licensed and made available under the terms and conditions of the BSD License\r
8which accompanies this distribution. The full text of the license may be found at\r
9http://opensource.org/licenses/bsd-license.php\r
10\r
11THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
12WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
13\r
14**/\r
15\r
16\r
17#include "OhcPeim.h"\r
18\r
19\r
20/**\r
21 Allocate a block of memory to be used by the buffer pool.\r
22\r
23 Use Redirect memory services to allocate memmory so that USB DMA transfers do\r
24 not cause IMR violations on Quark.\r
25\r
26 @param Pool The buffer pool to allocate memory for.\r
27 @param Pages How many pages to allocate.\r
28\r
29 @return The allocated memory block or NULL if failed.\r
30\r
31**/\r
32USBHC_MEM_BLOCK *\r
33UsbHcAllocMemBlock (\r
34 IN USBHC_MEM_POOL *Pool,\r
35 IN UINTN Pages\r
36 )\r
37{\r
38 USBHC_MEM_BLOCK *Block;\r
39 VOID *BufHost;\r
40 VOID *Mapping;\r
41 EFI_PHYSICAL_ADDRESS MappedAddr;\r
42 EFI_STATUS Status;\r
43 UINTN PageNumber;\r
44 EFI_PHYSICAL_ADDRESS TempPtr;\r
45\r
46 Mapping = NULL;\r
47 PageNumber = sizeof(USBHC_MEM_BLOCK)/PAGESIZE +1;\r
48 Status = PeiServicesAllocatePages (\r
49 EfiBootServicesCode,\r
50 PageNumber,\r
51 &TempPtr\r
52 );\r
53\r
54 if (EFI_ERROR (Status)) {\r
55 return NULL;\r
56 }\r
57 ZeroMem ((VOID *)(UINTN)TempPtr, PageNumber*EFI_PAGE_SIZE);\r
58\r
59 //\r
60 // each bit in the bit array represents USBHC_MEM_UNIT\r
61 // bytes of memory in the memory block.\r
62 //\r
63 ASSERT (USBHC_MEM_UNIT * 8 <= EFI_PAGE_SIZE);\r
64\r
65 Block = (USBHC_MEM_BLOCK*)(UINTN)TempPtr;\r
66 Block->BufLen = EFI_PAGES_TO_SIZE (Pages);\r
67 Block->BitsLen = Block->BufLen / (USBHC_MEM_UNIT * 8);\r
68\r
69 PageNumber = (Block->BitsLen)/PAGESIZE +1;\r
70 Status = PeiServicesAllocatePages (\r
71 EfiBootServicesCode,\r
72 PageNumber,\r
73 &TempPtr\r
74 );\r
75 if (EFI_ERROR (Status)) {\r
76 return NULL;\r
77 }\r
78 ZeroMem ((VOID *)(UINTN)TempPtr, PageNumber*EFI_PAGE_SIZE);\r
79\r
80 Block->Bits = (UINT8 *)(UINTN)TempPtr;\r
81\r
82 Status = PeiServicesAllocatePages (\r
83 EfiBootServicesCode,\r
84 Pages,\r
85 &TempPtr\r
86 );\r
87 ZeroMem ((VOID *)(UINTN)TempPtr, Pages*EFI_PAGE_SIZE);\r
88\r
89 BufHost = (VOID *)(UINTN)TempPtr;\r
90 MappedAddr = (EFI_PHYSICAL_ADDRESS) (UINTN) BufHost;\r
91 //\r
92 // Check whether the data structure used by the host controller\r
93 // should be restricted into the same 4G\r
94 //\r
95 if (Pool->Check4G && (Pool->Which4G != USB_HC_HIGH_32BIT (MappedAddr))) {\r
96 return NULL;\r
97 }\r
98\r
99 Block->BufHost = BufHost;\r
100 Block->Buf = (UINT8 *) ((UINTN) MappedAddr);\r
101 Block->Mapping = Mapping;\r
102 Block->Next = NULL;\r
103\r
104 return Block;\r
105\r
106}\r
107\r
108\r
109/**\r
110 Free the memory block from the memory pool.\r
111\r
112 @param Pool The memory pool to free the block from.\r
113 @param Block The memory block to free.\r
114\r
115**/\r
116VOID\r
117UsbHcFreeMemBlock (\r
118 IN USBHC_MEM_POOL *Pool,\r
119 IN USBHC_MEM_BLOCK *Block\r
120 )\r
121{\r
122\r
123 ASSERT ((Pool != NULL) && (Block != NULL));\r
124}\r
125\r
126\r
127/**\r
128 Alloc some memory from the block.\r
129\r
130 @param Block The memory block to allocate memory from.\r
131 @param Units Number of memory units to allocate.\r
132\r
133 @return The pointer to the allocated memory. If couldn't allocate the needed memory,\r
134 the return value is NULL.\r
135\r
136**/\r
137VOID *\r
138UsbHcAllocMemFromBlock (\r
139 IN USBHC_MEM_BLOCK *Block,\r
140 IN UINTN Units\r
141 )\r
142{\r
143 UINTN Byte;\r
144 UINT8 Bit;\r
145 UINTN StartByte;\r
146 UINT8 StartBit;\r
147 UINTN Available;\r
148 UINTN Count;\r
149\r
150 ASSERT ((Block != 0) && (Units != 0));\r
151\r
152 StartByte = 0;\r
153 StartBit = 0;\r
154 Available = 0;\r
155\r
156 for (Byte = 0, Bit = 0; Byte < Block->BitsLen;) {\r
157 //\r
158 // If current bit is zero, the corresponding memory unit is\r
159 // available, otherwise we need to restart our searching.\r
160 // Available counts the consective number of zero bit.\r
161 //\r
162 if (!USB_HC_BIT_IS_SET (Block->Bits[Byte], Bit)) {\r
163 Available++;\r
164\r
165 if (Available >= Units) {\r
166 break;\r
167 }\r
168\r
169 NEXT_BIT (Byte, Bit);\r
170\r
171 } else {\r
172 NEXT_BIT (Byte, Bit);\r
173\r
174 Available = 0;\r
175 StartByte = Byte;\r
176 StartBit = Bit;\r
177 }\r
178 }\r
179\r
180 if (Available < Units) {\r
181 return NULL;\r
182 }\r
183\r
184 //\r
185 // Mark the memory as allocated\r
186 //\r
187 Byte = StartByte;\r
188 Bit = StartBit;\r
189\r
190 for (Count = 0; Count < Units; Count++) {\r
191 ASSERT (!USB_HC_BIT_IS_SET (Block->Bits[Byte], Bit));\r
192\r
193 Block->Bits[Byte] = (UINT8) (Block->Bits[Byte] | (UINT8) USB_HC_BIT (Bit));\r
194 NEXT_BIT (Byte, Bit);\r
195 }\r
196\r
197 return Block->BufHost + (StartByte * 8 + StartBit) * USBHC_MEM_UNIT;\r
198}\r
199\r
200/**\r
201 Insert the memory block to the pool's list of the blocks.\r
202\r
203 @param Head The head of the memory pool's block list.\r
204 @param Block The memory block to insert.\r
205\r
206**/\r
207VOID\r
208UsbHcInsertMemBlockToPool (\r
209 IN USBHC_MEM_BLOCK *Head,\r
210 IN USBHC_MEM_BLOCK *Block\r
211 )\r
212{\r
213 ASSERT ((Head != NULL) && (Block != NULL));\r
214 Block->Next = Head->Next;\r
215 Head->Next = Block;\r
216}\r
217\r
218\r
219/**\r
220 Is the memory block empty?\r
221\r
222 @param Block The memory block to check.\r
223\r
224 @retval TRUE The memory block is empty.\r
225 @retval FALSE The memory block isn't empty.\r
226\r
227**/\r
228BOOLEAN\r
229UsbHcIsMemBlockEmpty (\r
230 IN USBHC_MEM_BLOCK *Block\r
231 )\r
232{\r
233 UINTN Index;\r
234\r
235 for (Index = 0; Index < Block->BitsLen; Index++) {\r
236 if (Block->Bits[Index] != 0) {\r
237 return FALSE;\r
238 }\r
239 }\r
240\r
241 return TRUE;\r
242}\r
243\r
244\r
245/**\r
246 Unlink the memory block from the pool's list.\r
247\r
248 @param Head The block list head of the memory's pool.\r
249 @param BlockToUnlink The memory block to unlink.\r
250\r
251**/\r
252VOID\r
253UsbHcUnlinkMemBlock (\r
254 IN USBHC_MEM_BLOCK *Head,\r
255 IN USBHC_MEM_BLOCK *BlockToUnlink\r
256 )\r
257{\r
258 USBHC_MEM_BLOCK *Block;\r
259\r
260 ASSERT ((Head != NULL) && (BlockToUnlink != NULL));\r
261\r
262 for (Block = Head; Block != NULL; Block = Block->Next) {\r
263 if (Block->Next == BlockToUnlink) {\r
264 Block->Next = BlockToUnlink->Next;\r
265 BlockToUnlink->Next = NULL;\r
266 break;\r
267 }\r
268 }\r
269}\r
270\r
271\r
272/**\r
273 Initialize the memory management pool for the host controller.\r
274\r
275 @param PciIo The PciIo that can be used to access the host controller.\r
276 @param Check4G Whether the host controller requires allocated memory\r
277 from one 4G address space.\r
278 @param Which4G The 4G memory area each memory allocated should be from.\r
279\r
280 @retval EFI_SUCCESS The memory pool is initialized.\r
281 @retval EFI_OUT_OF_RESOURCE Fail to init the memory pool.\r
282\r
283**/\r
284USBHC_MEM_POOL *\r
285UsbHcInitMemPool (\r
286 IN BOOLEAN Check4G,\r
287 IN UINT32 Which4G\r
288 )\r
289{\r
290 USBHC_MEM_POOL *Pool;\r
291 UINTN PageNumber;\r
292 EFI_STATUS Status;\r
293 EFI_PHYSICAL_ADDRESS TempPtr;\r
294\r
295 PageNumber = sizeof(USBHC_MEM_POOL)/PAGESIZE +1;\r
296 Status = PeiServicesAllocatePages (\r
297 EfiBootServicesCode,\r
298 PageNumber,\r
299 &TempPtr\r
300 );\r
301 if (EFI_ERROR (Status)) {\r
302 return NULL;\r
303 }\r
304 ZeroMem ((VOID *)(UINTN)TempPtr, PageNumber*EFI_PAGE_SIZE);\r
305\r
306 Pool = (USBHC_MEM_POOL *) ((UINTN) TempPtr);\r
307 Pool->Check4G = Check4G;\r
308 Pool->Which4G = Which4G;\r
309 Pool->Head = UsbHcAllocMemBlock (Pool, USBHC_MEM_DEFAULT_PAGES);\r
310\r
311 if (Pool->Head == NULL) {\r
312 Pool = NULL;\r
313 }\r
314\r
315 return Pool;\r
316}\r
317\r
318\r
319/**\r
320 Release the memory management pool.\r
321\r
322 @param Pool The USB memory pool to free.\r
323\r
324 @retval EFI_SUCCESS The memory pool is freed.\r
325 @retval EFI_DEVICE_ERROR Failed to free the memory pool.\r
326\r
327**/\r
328EFI_STATUS\r
329UsbHcFreeMemPool (\r
330 IN USBHC_MEM_POOL *Pool\r
331 )\r
332{\r
333 USBHC_MEM_BLOCK *Block;\r
334\r
335 ASSERT (Pool->Head != NULL);\r
336\r
337 //\r
338 // Unlink all the memory blocks from the pool, then free them.\r
339 // UsbHcUnlinkMemBlock can't be used to unlink and free the\r
340 // first block.\r
341 //\r
342 for (Block = Pool->Head->Next; Block != NULL; Block = Pool->Head->Next) {\r
343 UsbHcFreeMemBlock (Pool, Block);\r
344 }\r
345\r
346 UsbHcFreeMemBlock (Pool, Pool->Head);\r
347\r
348 return EFI_SUCCESS;\r
349}\r
350\r
351\r
352/**\r
353 Allocate some memory from the host controller's memory pool\r
354 which can be used to communicate with host controller.\r
355\r
356 @param Pool The host controller's memory pool.\r
357 @param Size Size of the memory to allocate.\r
358\r
359 @return The allocated memory or NULL.\r
360\r
361**/\r
362VOID *\r
363UsbHcAllocateMem (\r
364 IN USBHC_MEM_POOL *Pool,\r
365 IN UINTN Size\r
366 )\r
367{\r
368 USBHC_MEM_BLOCK *Head;\r
369 USBHC_MEM_BLOCK *Block;\r
370 USBHC_MEM_BLOCK *NewBlock;\r
371 VOID *Mem;\r
372 UINTN AllocSize;\r
373 UINTN Pages;\r
374\r
375 Mem = NULL;\r
376 AllocSize = USBHC_MEM_ROUND (Size);\r
377 Head = Pool->Head;\r
378 ASSERT (Head != NULL);\r
379\r
380 //\r
381 // First check whether current memory blocks can satisfy the allocation.\r
382 //\r
383 for (Block = Head; Block != NULL; Block = Block->Next) {\r
384 Mem = UsbHcAllocMemFromBlock (Block, AllocSize / USBHC_MEM_UNIT);\r
385\r
386 if (Mem != NULL) {\r
387 ZeroMem (Mem, Size);\r
388 break;\r
389 }\r
390 }\r
391\r
392 if (Mem != NULL) {\r
393 return Mem;\r
394 }\r
395\r
396 //\r
397 // Create a new memory block if there is not enough memory\r
398 // in the pool. If the allocation size is larger than the\r
399 // default page number, just allocate a large enough memory\r
400 // block. Otherwise allocate default pages.\r
401 //\r
402 if (AllocSize > EFI_PAGES_TO_SIZE (USBHC_MEM_DEFAULT_PAGES)) {\r
403 Pages = EFI_SIZE_TO_PAGES (AllocSize) + 1;\r
404 } else {\r
405 Pages = USBHC_MEM_DEFAULT_PAGES;\r
406 }\r
407\r
408 NewBlock = UsbHcAllocMemBlock (Pool, Pages);\r
409\r
410 if (NewBlock == NULL) {\r
411 DEBUG ((EFI_D_INFO, "UsbHcAllocateMem: failed to allocate block\n"));\r
412 return NULL;\r
413 }\r
414\r
415 //\r
416 // Add the new memory block to the pool, then allocate memory from it\r
417 //\r
418 UsbHcInsertMemBlockToPool (Head, NewBlock);\r
419 Mem = UsbHcAllocMemFromBlock (NewBlock, AllocSize / USBHC_MEM_UNIT);\r
420\r
421 if (Mem != NULL) {\r
422 ZeroMem (Mem, Size);\r
423 }\r
424\r
425 return Mem;\r
426}\r
427\r
428\r
429/**\r
430 Free the allocated memory back to the memory pool.\r
431\r
432 @param Pool The memory pool of the host controller.\r
433 @param Mem The memory to free.\r
434 @param Size The size of the memory to free.\r
435\r
436**/\r
437VOID\r
438UsbHcFreeMem (\r
439 IN USBHC_MEM_POOL *Pool,\r
440 IN VOID *Mem,\r
441 IN UINTN Size\r
442 )\r
443{\r
444 USBHC_MEM_BLOCK *Head;\r
445 USBHC_MEM_BLOCK *Block;\r
446 UINT8 *ToFree;\r
447 UINTN AllocSize;\r
448 UINTN Byte;\r
449 UINTN Bit;\r
450 UINTN Count;\r
451\r
452 Head = Pool->Head;\r
453 AllocSize = USBHC_MEM_ROUND (Size);\r
454 ToFree = (UINT8 *) Mem;\r
455\r
456 for (Block = Head; Block != NULL; Block = Block->Next) {\r
457 //\r
458 // scan the memory block list for the memory block that\r
459 // completely contains the memory to free.\r
460 //\r
461 if ((Block->BufHost <= ToFree) && ((ToFree + AllocSize) <= (Block->BufHost + Block->BufLen))) {\r
462 //\r
463 // compute the start byte and bit in the bit array\r
464 //\r
465 Byte = ((ToFree - Block->BufHost) / USBHC_MEM_UNIT) / 8;\r
466 Bit = ((ToFree - Block->BufHost) / USBHC_MEM_UNIT) % 8;\r
467\r
468 //\r
469 // reset associated bits in bit arry\r
470 //\r
471 for (Count = 0; Count < (AllocSize / USBHC_MEM_UNIT); Count++) {\r
472 ASSERT (USB_HC_BIT_IS_SET (Block->Bits[Byte], Bit));\r
473\r
474 Block->Bits[Byte] = (UINT8) (Block->Bits[Byte] ^ USB_HC_BIT (Bit));\r
475 NEXT_BIT (Byte, Bit);\r
476 }\r
477\r
478 break;\r
479 }\r
480 }\r
481\r
482 //\r
483 // If Block == NULL, it means that the current memory isn't\r
484 // in the host controller's pool. This is critical because\r
485 // the caller has passed in a wrong memory point\r
486 //\r
487 ASSERT (Block != NULL);\r
488\r
489 //\r
490 // Release the current memory block if it is empty and not the head\r
491 //\r
492 if ((Block != Head) && UsbHcIsMemBlockEmpty (Block)) {\r
493 UsbHcFreeMemBlock (Pool, Block);\r
494 }\r
495\r
496 return ;\r
497}\r