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