1 /** @file
2 Main file of the MMC Dxe driver. The driver entrypoint is defined into this file.
3
4 Copyright (c) 2011-2013, ARM Limited. All rights reserved.
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 #include <Protocol/DevicePath.h>
17
18 #include <Library/BaseLib.h>
19 #include <Library/BaseMemoryLib.h>
20 #include <Library/MemoryAllocationLib.h>
21 #include <Library/UefiBootServicesTableLib.h>
22 #include <Library/DevicePathLib.h>
23
24 #include "Mmc.h"
25
26 EFI_BLOCK_IO_MEDIA mMmcMediaTemplate = {
27 SIGNATURE_32('m','m','c','o'), // MediaId
28 TRUE, // RemovableMedia
29 FALSE, // MediaPresent
30 FALSE, // LogicalPartition
31 FALSE, // ReadOnly
32 FALSE, // WriteCaching
33 512, // BlockSize
34 4, // IoAlign
35 0, // Pad
36 0 // LastBlock
37 };
38
39 //
40 // This device structure is serviced as a header.
41 // Its next field points to the first root bridge device node.
42 //
43 LIST_ENTRY mMmcHostPool;
44
45 /**
46 Event triggered by the timer to check if any cards have been removed
47 or if new ones have been plugged in
48 **/
49
50 EFI_EVENT gCheckCardsEvent;
51
52 /**
53 Initialize the MMC Host Pool to support multiple MMC devices
54 **/
55 VOID
InitializeMmcHostPool(VOID)56 InitializeMmcHostPool (
57 VOID
58 )
59 {
60 InitializeListHead (&mMmcHostPool);
61 }
62
63 /**
64 Insert a new Mmc Host controller to the pool
65 **/
66 VOID
InsertMmcHost(IN MMC_HOST_INSTANCE * MmcHostInstance)67 InsertMmcHost (
68 IN MMC_HOST_INSTANCE *MmcHostInstance
69 )
70 {
71 InsertTailList (&mMmcHostPool, &(MmcHostInstance->Link));
72 }
73
74 /*
75 Remove a new Mmc Host controller to the pool
76 */
77 VOID
RemoveMmcHost(IN MMC_HOST_INSTANCE * MmcHostInstance)78 RemoveMmcHost (
79 IN MMC_HOST_INSTANCE *MmcHostInstance
80 )
81 {
82 RemoveEntryList (&(MmcHostInstance->Link));
83 }
84
CreateMmcHostInstance(IN EFI_MMC_HOST_PROTOCOL * MmcHost)85 MMC_HOST_INSTANCE* CreateMmcHostInstance (
86 IN EFI_MMC_HOST_PROTOCOL* MmcHost
87 )
88 {
89 EFI_STATUS Status;
90 MMC_HOST_INSTANCE* MmcHostInstance;
91 EFI_DEVICE_PATH_PROTOCOL *NewDevicePathNode;
92 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
93
94 MmcHostInstance = AllocateZeroPool (sizeof (MMC_HOST_INSTANCE));
95 if (MmcHostInstance == NULL) {
96 return NULL;
97 }
98
99 MmcHostInstance->Signature = MMC_HOST_INSTANCE_SIGNATURE;
100
101 MmcHostInstance->State = MmcHwInitializationState;
102
103 MmcHostInstance->BlockIo.Media = AllocateCopyPool (sizeof(EFI_BLOCK_IO_MEDIA), &mMmcMediaTemplate);
104 if (MmcHostInstance->BlockIo.Media == NULL) {
105 goto FREE_INSTANCE;
106 }
107
108 MmcHostInstance->BlockIo.Revision = EFI_BLOCK_IO_INTERFACE_REVISION;
109 MmcHostInstance->BlockIo.Reset = MmcReset;
110 MmcHostInstance->BlockIo.ReadBlocks = MmcReadBlocks;
111 MmcHostInstance->BlockIo.WriteBlocks = MmcWriteBlocks;
112 MmcHostInstance->BlockIo.FlushBlocks = MmcFlushBlocks;
113
114 MmcHostInstance->EraseBlockProtocol.Revision = EFI_ERASE_BLOCK_PROTOCOL_REVISION;
115 MmcHostInstance->EraseBlockProtocol.EraseLengthGranularity = 1; //TO BE CHANGED, SHIVA
116 MmcHostInstance->EraseBlockProtocol.EraseBlocks = MmcEraseBlocks;
117
118 MmcHostInstance->MmcHost = MmcHost;
119
120 // Create DevicePath for the new MMC Host
121 Status = MmcHost->BuildDevicePath (MmcHost, &NewDevicePathNode);
122 if (EFI_ERROR (Status)) {
123 goto FREE_MEDIA;
124 }
125
126 DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) AllocatePool (END_DEVICE_PATH_LENGTH);
127 if (DevicePath == NULL) {
128 goto FREE_MEDIA;
129 }
130
131 SetDevicePathEndNode (DevicePath);
132 MmcHostInstance->DevicePath = AppendDevicePathNode (DevicePath, NewDevicePathNode);
133
134 // Publish BlockIO protocol interface
135 Status = gBS->InstallMultipleProtocolInterfaces (
136 &MmcHostInstance->MmcHandle,
137 &gEfiBlockIoProtocolGuid,&MmcHostInstance->BlockIo,
138 &gEfiEraseBlockProtocolGuid,&MmcHostInstance->EraseBlockProtocol,
139 &gEfiDevicePathProtocolGuid,MmcHostInstance->DevicePath,
140 NULL
141 );
142 if (EFI_ERROR(Status)) {
143 goto FREE_DEVICE_PATH;
144 }
145
146 return MmcHostInstance;
147
148 FREE_DEVICE_PATH:
149 FreePool(DevicePath);
150
151 FREE_MEDIA:
152 FreePool(MmcHostInstance->BlockIo.Media);
153
154 FREE_INSTANCE:
155 FreePool(MmcHostInstance);
156
157 return NULL;
158 }
159
DestroyMmcHostInstance(IN MMC_HOST_INSTANCE * MmcHostInstance)160 EFI_STATUS DestroyMmcHostInstance (
161 IN MMC_HOST_INSTANCE* MmcHostInstance
162 )
163 {
164 EFI_STATUS Status;
165
166 // Uninstall Protocol Interfaces
167 Status = gBS->UninstallMultipleProtocolInterfaces (
168 MmcHostInstance->MmcHandle,
169 &gEfiBlockIoProtocolGuid,&(MmcHostInstance->BlockIo),
170 &gEfiEraseBlockProtocolGuid,&MmcHostInstance->EraseBlockProtocol,
171 &gEfiDevicePathProtocolGuid,MmcHostInstance->DevicePath,
172 NULL
173 );
174 ASSERT_EFI_ERROR (Status);
175
176 // Free Memory allocated for the instance
177 if (MmcHostInstance->BlockIo.Media) {
178 FreePool(MmcHostInstance->BlockIo.Media);
179 }
180 FreePool (MmcHostInstance);
181
182 return Status;
183 }
184
185 /**
186 This function checks if the controller implement the Mmc Host and the Device Path Protocols
187 **/
188 EFI_STATUS
189 EFIAPI
MmcDriverBindingSupported(IN EFI_DRIVER_BINDING_PROTOCOL * This,IN EFI_HANDLE Controller,IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath)190 MmcDriverBindingSupported (
191 IN EFI_DRIVER_BINDING_PROTOCOL *This,
192 IN EFI_HANDLE Controller,
193 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
194 )
195 {
196 EFI_STATUS Status;
197 //EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;
198 EFI_MMC_HOST_PROTOCOL *MmcHost;
199 EFI_DEV_PATH_PTR Node;
200
201 //
202 // Check RemainingDevicePath validation
203 //
204 if (RemainingDevicePath != NULL) {
205 //
206 // Check if RemainingDevicePath is the End of Device Path Node,
207 // if yes, go on checking other conditions
208 //
209 if (!IsDevicePathEnd (RemainingDevicePath)) {
210 //
211 // If RemainingDevicePath isn't the End of Device Path Node,
212 // check its validation
213 //
214 Node.DevPath = RemainingDevicePath;
215 if (Node.DevPath->Type != HARDWARE_DEVICE_PATH ||
216 Node.DevPath->SubType != HW_VENDOR_DP ||
217 DevicePathNodeLength(Node.DevPath) != sizeof(VENDOR_DEVICE_PATH)) {
218 return EFI_UNSUPPORTED;
219 }
220 }
221 }
222
223 //
224 // Check if Mmc Host protocol is installed by platform
225 //
226 Status = gBS->OpenProtocol (
227 Controller,
228 &gEfiMmcHostProtocolGuid,
229 (VOID **) &MmcHost,
230 This->DriverBindingHandle,
231 Controller,
232 EFI_OPEN_PROTOCOL_BY_DRIVER
233 );
234 if (Status == EFI_ALREADY_STARTED) {
235 return EFI_SUCCESS;
236 }
237 if (EFI_ERROR (Status)) {
238 return Status;
239 }
240
241 //
242 // Close the Mmc Host used to perform the supported test
243 //
244 gBS->CloseProtocol (
245 Controller,
246 &gEfiMmcHostProtocolGuid,
247 This->DriverBindingHandle,
248 Controller
249 );
250
251 return EFI_SUCCESS;
252 }
253
254 /**
255
256 **/
257 EFI_STATUS
258 EFIAPI
MmcDriverBindingStart(IN EFI_DRIVER_BINDING_PROTOCOL * This,IN EFI_HANDLE Controller,IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath)259 MmcDriverBindingStart (
260 IN EFI_DRIVER_BINDING_PROTOCOL *This,
261 IN EFI_HANDLE Controller,
262 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
263 )
264 {
265 EFI_STATUS Status;
266 MMC_HOST_INSTANCE *MmcHostInstance;
267 EFI_MMC_HOST_PROTOCOL *MmcHost;
268
269 //
270 // Check RemainingDevicePath validation
271 //
272 if (RemainingDevicePath != NULL) {
273 //
274 // Check if RemainingDevicePath is the End of Device Path Node,
275 // if yes, return EFI_SUCCESS
276 //
277 if (IsDevicePathEnd (RemainingDevicePath)) {
278 return EFI_SUCCESS;
279 }
280 }
281
282 //
283 // Get the Mmc Host protocol
284 //
285 Status = gBS->OpenProtocol (
286 Controller,
287 &gEfiMmcHostProtocolGuid,
288 (VOID **) &MmcHost,
289 This->DriverBindingHandle,
290 Controller,
291 EFI_OPEN_PROTOCOL_BY_DRIVER
292 );
293 if (EFI_ERROR (Status)) {
294 if (Status == EFI_ALREADY_STARTED) {
295 return EFI_SUCCESS;
296 }
297 return Status;
298 }
299
300 MmcHostInstance = CreateMmcHostInstance(MmcHost);
301 if (MmcHostInstance != NULL) {
302 // Add the handle to the pool
303 InsertMmcHost (MmcHostInstance);
304
305 MmcHostInstance->Initialized = FALSE;
306
307 // Detect card presence now
308 CheckCardsCallback (NULL, NULL);
309 }
310
311 return EFI_SUCCESS;
312 }
313
314 /**
315
316 **/
317 EFI_STATUS
318 EFIAPI
MmcDriverBindingStop(IN EFI_DRIVER_BINDING_PROTOCOL * This,IN EFI_HANDLE Controller,IN UINTN NumberOfChildren,IN EFI_HANDLE * ChildHandleBuffer)319 MmcDriverBindingStop (
320 IN EFI_DRIVER_BINDING_PROTOCOL *This,
321 IN EFI_HANDLE Controller,
322 IN UINTN NumberOfChildren,
323 IN EFI_HANDLE *ChildHandleBuffer
324 )
325 {
326 EFI_STATUS Status = EFI_SUCCESS;
327 LIST_ENTRY *CurrentLink;
328 MMC_HOST_INSTANCE *MmcHostInstance;
329
330 MMC_TRACE("MmcDriverBindingStop()");
331
332 // For each MMC instance
333 CurrentLink = mMmcHostPool.ForwardLink;
334 while (CurrentLink != NULL && CurrentLink != &mMmcHostPool && (Status == EFI_SUCCESS)) {
335 MmcHostInstance = MMC_HOST_INSTANCE_FROM_LINK(CurrentLink);
336 ASSERT(MmcHostInstance != NULL);
337
338 // Close gEfiMmcHostProtocolGuid
339 Status = gBS->CloseProtocol (
340 Controller,
341 &gEfiMmcHostProtocolGuid,(VOID **) &MmcHostInstance->MmcHost,
342 This->DriverBindingHandle
343 );
344
345 // Remove MMC Host Instance from the pool
346 RemoveMmcHost (MmcHostInstance);
347
348 // Destroy MmcHostInstance
349 DestroyMmcHostInstance (MmcHostInstance);
350 }
351
352 return Status;
353 }
354
355 VOID
356 EFIAPI
CheckCardsCallback(IN EFI_EVENT Event,IN VOID * Context)357 CheckCardsCallback (
358 IN EFI_EVENT Event,
359 IN VOID *Context
360 )
361 {
362 LIST_ENTRY *CurrentLink;
363 MMC_HOST_INSTANCE *MmcHostInstance;
364 EFI_STATUS Status;
365
366 CurrentLink = mMmcHostPool.ForwardLink;
367 while (CurrentLink != NULL && CurrentLink != &mMmcHostPool) {
368 MmcHostInstance = MMC_HOST_INSTANCE_FROM_LINK(CurrentLink);
369 ASSERT(MmcHostInstance != NULL);
370
371 if (MmcHostInstance->MmcHost->IsCardPresent (MmcHostInstance->MmcHost) == !MmcHostInstance->Initialized) {
372 MmcHostInstance->State = MmcHwInitializationState;
373 MmcHostInstance->BlockIo.Media->MediaPresent = !MmcHostInstance->Initialized;
374 MmcHostInstance->Initialized = !MmcHostInstance->Initialized;
375
376 if (MmcHostInstance->BlockIo.Media->MediaPresent) {
377 InitializeMmcDevice (MmcHostInstance);
378 }
379
380 Status = gBS->ReinstallProtocolInterface (
381 (MmcHostInstance->MmcHandle),
382 &gEfiBlockIoProtocolGuid,
383 &(MmcHostInstance->BlockIo),
384 &(MmcHostInstance->BlockIo)
385 );
386 Status = gBS->ReinstallProtocolInterface (
387 (MmcHostInstance->MmcHandle),
388 &gEfiEraseBlockProtocolGuid,
389 &(MmcHostInstance->EraseBlockProtocol),
390 &(MmcHostInstance->EraseBlockProtocol)
391 );
392
393 if (EFI_ERROR(Status)) {
394 Print(L"MMC Card: Error reinstalling BlockIo interface\n");
395 }
396 }
397
398 CurrentLink = CurrentLink->ForwardLink;
399 }
400 }
401
402
403 EFI_DRIVER_BINDING_PROTOCOL gMmcDriverBinding = {
404 MmcDriverBindingSupported,
405 MmcDriverBindingStart,
406 MmcDriverBindingStop,
407 0xa,
408 NULL,
409 NULL
410 };
411
412 /**
413
414 **/
415 EFI_STATUS
416 EFIAPI
MmcDxeInitialize(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)417 MmcDxeInitialize (
418 IN EFI_HANDLE ImageHandle,
419 IN EFI_SYSTEM_TABLE *SystemTable
420 )
421 {
422 EFI_STATUS Status;
423
424 //
425 // Initializes MMC Host pool
426 //
427 InitializeMmcHostPool ();
428
429 //
430 // Install driver model protocol(s).
431 //
432 Status = EfiLibInstallDriverBindingComponentName2 (
433 ImageHandle,
434 SystemTable,
435 &gMmcDriverBinding,
436 ImageHandle,
437 &gMmcComponentName,
438 &gMmcComponentName2
439 );
440 ASSERT_EFI_ERROR (Status);
441
442 // Install driver diagnostics
443 Status = gBS->InstallMultipleProtocolInterfaces (
444 &ImageHandle,
445 &gEfiDriverDiagnostics2ProtocolGuid,&gMmcDriverDiagnostics2,
446 NULL
447 );
448 ASSERT_EFI_ERROR (Status);
449
450 // Use a timer to detect if a card has been plugged in or removed
451 Status = gBS->CreateEvent (
452 EVT_NOTIFY_SIGNAL | EVT_TIMER,
453 TPL_CALLBACK,
454 CheckCardsCallback,
455 NULL,
456 &gCheckCardsEvent);
457 ASSERT_EFI_ERROR (Status);
458
459 Status = gBS->SetTimer(
460 gCheckCardsEvent,
461 TimerPeriodic,
462 (UINT64)(10*1000*200)); // 200 ms
463 ASSERT_EFI_ERROR (Status);
464
465 return Status;
466 }
467