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