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