]> git.proxmox.com Git - mirror_edk2.git/blame - ArmPkg/Library/ArmDmaLib/ArmDmaLib.c
Add DMA Lib for generic ARM cache coherency model.
[mirror_edk2.git] / ArmPkg / Library / ArmDmaLib / ArmDmaLib.c
CommitLineData
938f46b4 1/** @file\r
2 Generic ARM implementation of DmaLib.h\r
3\r
4 Copyright (c) 2008 - 2010, Apple Inc. All rights reserved.<BR>\r
5 \r
6 This program and the accompanying materials\r
7 are licensed and made available under the terms and conditions of the BSD License\r
8 which accompanies this distribution. The full text of the license may be found at\r
9 http://opensource.org/licenses/bsd-license.php\r
10\r
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
13\r
14**/\r
15\r
16#include <Base.h>\r
17#include <Library/DebugLib.h>\r
18#include <Library/DmaLib.h>\r
19#include <Library/MemoryAllocationLib.h>\r
20#include <Library/UefiBootServicesTableLib.h>\r
21#include <Library/UncachedMemoryAllocationLib.h>\r
22#include <Library/IoLib.h>\r
23#include <Library/BaseMemoryLib.h>\r
24#include <Library/ArmLib.h>\r
25\r
26#include <Protocol/Cpu.h>\r
27\r
28typedef struct {\r
29 EFI_PHYSICAL_ADDRESS HostAddress;\r
30 EFI_PHYSICAL_ADDRESS DeviceAddress;\r
31 UINTN NumberOfBytes;\r
32 DMA_MAP_OPERATION Operation;\r
33 BOOLEAN DoubleBuffer;\r
34} MAP_INFO_INSTANCE;\r
35\r
36\r
37\r
38EFI_CPU_ARCH_PROTOCOL *gCpu;\r
39UINTN gCacheAlignment = 0;\r
40\r
41\r
42\r
43\r
44/** \r
45 Provides the DMA controller-specific addresses needed to access system memory.\r
46 \r
47 Operation is relative to the DMA bus master.\r
48 \r
49 @param Operation Indicates if the bus master is going to read or write to system memory.\r
50 @param HostAddress The system memory address to map to the DMA controller.\r
51 @param NumberOfBytes On input the number of bytes to map. On output the number of bytes\r
52 that were mapped. \r
53 @param DeviceAddress The resulting map address for the bus master controller to use to\r
54 access the hosts HostAddress. \r
55 @param Mapping A resulting value to pass to Unmap().\r
56 \r
57 @retval EFI_SUCCESS The range was mapped for the returned NumberOfBytes.\r
58 @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a common buffer. \r
59 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
60 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.\r
61 @retval EFI_DEVICE_ERROR The system hardware could not map the requested address.\r
62 \r
63**/\r
64EFI_STATUS\r
65EFIAPI\r
66DmaMap (\r
67 IN DMA_MAP_OPERATION Operation,\r
68 IN VOID *HostAddress,\r
69 IN OUT UINTN *NumberOfBytes,\r
70 OUT PHYSICAL_ADDRESS *DeviceAddress,\r
71 OUT VOID **Mapping\r
72 )\r
73{\r
74 EFI_STATUS Status;\r
75 MAP_INFO_INSTANCE *Map;\r
76 VOID *Buffer;\r
77\r
78 if ( HostAddress == NULL || NumberOfBytes == NULL || \r
79 DeviceAddress == NULL || Mapping == NULL ) {\r
80 return EFI_INVALID_PARAMETER;\r
81 }\r
82 \r
83\r
84 if (Operation >= MapOperationMaximum) {\r
85 return EFI_INVALID_PARAMETER;\r
86 }\r
87\r
88 *DeviceAddress = ConvertToPhysicalAddress (HostAddress);\r
89\r
90 // Remember range so we can flush on the other side\r
91 Map = AllocatePool (sizeof (MAP_INFO_INSTANCE));\r
92 if (Map == NULL) {\r
93 return EFI_OUT_OF_RESOURCES;\r
94 }\r
95 \r
96 *Mapping = Map;\r
97\r
98 if (((UINTN)HostAddress & (gCacheAlignment - 1)) != 0) {\r
99 Map->DoubleBuffer = TRUE;\r
100 Status = DmaAllocateBuffer (EfiBootServicesData, EFI_SIZE_TO_PAGES (*NumberOfBytes), &Buffer);\r
101 if (EFI_ERROR (Status)) {\r
102 return Status;\r
103 }\r
104 \r
105 *DeviceAddress = (PHYSICAL_ADDRESS)(UINTN)Buffer;\r
106 \r
107 } else {\r
108 Map->DoubleBuffer = FALSE;\r
109 }\r
110\r
111 *NumberOfBytes &= *NumberOfBytes & ~(gCacheAlignment - 1); // Only do it on full cache lines\r
112 \r
113 Map->HostAddress = (UINTN)HostAddress;\r
114 Map->DeviceAddress = *DeviceAddress;\r
115 Map->NumberOfBytes = *NumberOfBytes;\r
116 Map->Operation = Operation;\r
117\r
118 if (Map->DoubleBuffer) {\r
119 if (Map->Operation == MapOperationBusMasterWrite) {\r
120 CopyMem ((VOID *)(UINTN)Map->DeviceAddress, (VOID *)(UINTN)Map->HostAddress, Map->NumberOfBytes);\r
121 }\r
122 } else {\r
123 // EfiCpuFlushTypeWriteBack, EfiCpuFlushTypeInvalidate\r
124 if (Map->Operation == MapOperationBusMasterWrite || Map->Operation == MapOperationBusMasterRead) {\r
125 gCpu->FlushDataCache (gCpu, (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress, Map->NumberOfBytes, EfiCpuFlushTypeWriteBackInvalidate);\r
126 }\r
127 }\r
128 \r
129 return EFI_SUCCESS;\r
130}\r
131\r
132\r
133/** \r
134 Completes the DmaMapBusMasterRead(), DmaMapBusMasterWrite(), or DmaMapBusMasterCommonBuffer()\r
135 operation and releases any corresponding resources.\r
136 \r
137 @param Mapping The mapping value returned from DmaMap*().\r
138 \r
139 @retval EFI_SUCCESS The range was unmapped.\r
140 @retval EFI_DEVICE_ERROR The data was not committed to the target system memory.\r
141 \r
142**/\r
143EFI_STATUS\r
144EFIAPI\r
145DmaUnmap (\r
146 IN VOID *Mapping\r
147 )\r
148{\r
149 MAP_INFO_INSTANCE *Map;\r
150 \r
151 if (Mapping == NULL) {\r
152 ASSERT (FALSE);\r
153 return EFI_INVALID_PARAMETER;\r
154 }\r
155 \r
156 Map = (MAP_INFO_INSTANCE *)Mapping;\r
157 \r
158 if (Map->DoubleBuffer) {\r
159 if (Map->Operation == MapOperationBusMasterRead) {\r
160 CopyMem ((VOID *)(UINTN)Map->HostAddress, (VOID *)(UINTN)Map->DeviceAddress, Map->NumberOfBytes);\r
161 }\r
162 \r
163 DmaFreeBuffer (EFI_SIZE_TO_PAGES (Map->NumberOfBytes), (VOID *)(UINTN)Map->DeviceAddress);\r
164 \r
165 } else {\r
166 if (Map->Operation == MapOperationBusMasterWrite) {\r
167 //\r
168 // Make sure we read buffer from uncached memory and not the cache\r
169 //\r
170 gCpu->FlushDataCache (gCpu, Map->HostAddress, Map->NumberOfBytes, EfiCpuFlushTypeInvalidate);\r
171 }\r
172 }\r
173 \r
174 FreePool (Map);\r
175\r
176 return EFI_SUCCESS;\r
177}\r
178\r
179/** \r
180 Allocates pages that are suitable for an DmaMap() of type MapOperationBusMasterCommonBuffer.\r
181 mapping. \r
182 \r
183 @param MemoryType The type of memory to allocate, EfiBootServicesData or\r
184 EfiRuntimeServicesData. \r
185 @param Pages The number of pages to allocate. \r
186 @param HostAddress A pointer to store the base system memory address of the\r
187 allocated range. \r
188\r
189 @retval EFI_SUCCESS The requested memory pages were allocated.\r
190 @retval EFI_UNSUPPORTED Attributes is unsupported. The only legal attribute bits are\r
191 MEMORY_WRITE_COMBINE and MEMORY_CACHED. \r
192 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
193 @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated. \r
194 \r
195**/\r
196EFI_STATUS\r
197EFIAPI\r
198DmaAllocateBuffer (\r
199 IN EFI_MEMORY_TYPE MemoryType,\r
200 IN UINTN Pages,\r
201 OUT VOID **HostAddress\r
202 )\r
203{\r
204 if (HostAddress == NULL) {\r
205 return EFI_INVALID_PARAMETER;\r
206 }\r
207\r
208 //\r
209 // The only valid memory types are EfiBootServicesData and EfiRuntimeServicesData\r
210 //\r
211 // We used uncached memory to keep coherency\r
212 //\r
213 if (MemoryType == EfiBootServicesData) {\r
214 *HostAddress = UncachedAllocatePages (Pages);\r
215 } else if (MemoryType != EfiRuntimeServicesData) {\r
216 *HostAddress = UncachedAllocateRuntimePages (Pages);\r
217 } else {\r
218 return EFI_INVALID_PARAMETER;\r
219 }\r
220\r
221 return EFI_SUCCESS;\r
222}\r
223\r
224\r
225/** \r
226 Frees memory that was allocated with DmaAllocateBuffer().\r
227 \r
228 @param Pages The number of pages to free. \r
229 @param HostAddress The base system memory address of the allocated range. \r
230 \r
231 @retval EFI_SUCCESS The requested memory pages were freed.\r
232 @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and Pages\r
233 was not allocated with DmaAllocateBuffer().\r
234 \r
235**/\r
236EFI_STATUS\r
237EFIAPI\r
238DmaFreeBuffer (\r
239 IN UINTN Pages,\r
240 IN VOID *HostAddress\r
241 )\r
242{\r
243 if (HostAddress == NULL) {\r
244 return EFI_INVALID_PARAMETER;\r
245 } \r
246 \r
247 UncachedFreePages (HostAddress, Pages);\r
248 return EFI_SUCCESS;\r
249}\r
250\r
251\r
252EFI_STATUS\r
253EFIAPI\r
254ArmDmaLibConstructor (\r
255 IN EFI_HANDLE ImageHandle,\r
256 IN EFI_SYSTEM_TABLE *SystemTable\r
257 )\r
258{\r
259 EFI_STATUS Status;\r
260\r
261 // Get the Cpu protocol for later use\r
262 Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **)&gCpu);\r
263 ASSERT_EFI_ERROR(Status);\r
264\r
265 gCacheAlignment = ArmDataCacheLineLength ();\r
266 \r
267 return Status;\r
268}\r
269\r