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