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