| /** @file | |
| Main file of the MMC Dxe driver. The driver entrypoint is defined into this file. | |
| Copyright (c) 2011-2013, ARM Limited. All rights reserved. | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include <Protocol/DevicePath.h> | |
| #include <Library/BaseLib.h> | |
| #include <Library/BaseMemoryLib.h> | |
| #include <Library/MemoryAllocationLib.h> | |
| #include <Library/UefiBootServicesTableLib.h> | |
| #include <Library/DevicePathLib.h> | |
| #include "Mmc.h" | |
| EFI_BLOCK_IO_MEDIA mMmcMediaTemplate = { | |
| SIGNATURE_32 ('m', 'm', 'c', 'o'), // MediaId | |
| TRUE, // RemovableMedia | |
| FALSE, // MediaPresent | |
| FALSE, // LogicalPartition | |
| FALSE, // ReadOnly | |
| FALSE, // WriteCaching | |
| 512, // BlockSize | |
| 4, // IoAlign | |
| 0, // Pad | |
| 0 // LastBlock | |
| }; | |
| // | |
| // This device structure is serviced as a header. | |
| // Its next field points to the first root bridge device node. | |
| // | |
| LIST_ENTRY mMmcHostPool; | |
| /** | |
| Event triggered by the timer to check if any cards have been removed | |
| or if new ones have been plugged in | |
| **/ | |
| EFI_EVENT gCheckCardsEvent; | |
| /** | |
| Initialize the MMC Host Pool to support multiple MMC devices | |
| **/ | |
| VOID | |
| InitializeMmcHostPool ( | |
| VOID | |
| ) | |
| { | |
| InitializeListHead (&mMmcHostPool); | |
| } | |
| /** | |
| Insert a new Mmc Host controller to the pool | |
| **/ | |
| VOID | |
| InsertMmcHost ( | |
| IN MMC_HOST_INSTANCE *MmcHostInstance | |
| ) | |
| { | |
| InsertTailList (&mMmcHostPool, &(MmcHostInstance->Link)); | |
| } | |
| /* | |
| Remove a new Mmc Host controller to the pool | |
| */ | |
| VOID | |
| RemoveMmcHost ( | |
| IN MMC_HOST_INSTANCE *MmcHostInstance | |
| ) | |
| { | |
| RemoveEntryList (&(MmcHostInstance->Link)); | |
| } | |
| MMC_HOST_INSTANCE * | |
| CreateMmcHostInstance ( | |
| IN EFI_MMC_HOST_PROTOCOL *MmcHost | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| MMC_HOST_INSTANCE *MmcHostInstance; | |
| EFI_DEVICE_PATH_PROTOCOL *NewDevicePathNode; | |
| EFI_DEVICE_PATH_PROTOCOL *DevicePath; | |
| MmcHostInstance = AllocateZeroPool (sizeof (MMC_HOST_INSTANCE)); | |
| if (MmcHostInstance == NULL) { | |
| return NULL; | |
| } | |
| MmcHostInstance->Signature = MMC_HOST_INSTANCE_SIGNATURE; | |
| MmcHostInstance->State = MmcHwInitializationState; | |
| MmcHostInstance->BlockIo.Media = AllocateCopyPool (sizeof (EFI_BLOCK_IO_MEDIA), &mMmcMediaTemplate); | |
| if (MmcHostInstance->BlockIo.Media == NULL) { | |
| goto FREE_INSTANCE; | |
| } | |
| MmcHostInstance->BlockIo.Revision = EFI_BLOCK_IO_INTERFACE_REVISION; | |
| MmcHostInstance->BlockIo.Reset = MmcReset; | |
| MmcHostInstance->BlockIo.ReadBlocks = MmcReadBlocks; | |
| MmcHostInstance->BlockIo.WriteBlocks = MmcWriteBlocks; | |
| MmcHostInstance->BlockIo.FlushBlocks = MmcFlushBlocks; | |
| MmcHostInstance->MmcHost = MmcHost; | |
| // Create DevicePath for the new MMC Host | |
| Status = MmcHost->BuildDevicePath (MmcHost, &NewDevicePathNode); | |
| if (EFI_ERROR (Status)) { | |
| goto FREE_MEDIA; | |
| } | |
| DevicePath = (EFI_DEVICE_PATH_PROTOCOL *)AllocatePool (END_DEVICE_PATH_LENGTH); | |
| if (DevicePath == NULL) { | |
| goto FREE_MEDIA; | |
| } | |
| SetDevicePathEndNode (DevicePath); | |
| MmcHostInstance->DevicePath = AppendDevicePathNode (DevicePath, NewDevicePathNode); | |
| // Publish BlockIO protocol interface | |
| Status = gBS->InstallMultipleProtocolInterfaces ( | |
| &MmcHostInstance->MmcHandle, | |
| &gEfiBlockIoProtocolGuid, | |
| &MmcHostInstance->BlockIo, | |
| &gEfiDevicePathProtocolGuid, | |
| MmcHostInstance->DevicePath, | |
| NULL | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| goto FREE_DEVICE_PATH; | |
| } | |
| return MmcHostInstance; | |
| FREE_DEVICE_PATH: | |
| FreePool (DevicePath); | |
| FREE_MEDIA: | |
| FreePool (MmcHostInstance->BlockIo.Media); | |
| FREE_INSTANCE: | |
| FreePool (MmcHostInstance); | |
| return NULL; | |
| } | |
| EFI_STATUS | |
| DestroyMmcHostInstance ( | |
| IN MMC_HOST_INSTANCE *MmcHostInstance | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| // Uninstall Protocol Interfaces | |
| Status = gBS->UninstallMultipleProtocolInterfaces ( | |
| MmcHostInstance->MmcHandle, | |
| &gEfiBlockIoProtocolGuid, | |
| &(MmcHostInstance->BlockIo), | |
| &gEfiDevicePathProtocolGuid, | |
| MmcHostInstance->DevicePath, | |
| NULL | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| // Free Memory allocated for the instance | |
| if (MmcHostInstance->BlockIo.Media) { | |
| FreePool (MmcHostInstance->BlockIo.Media); | |
| } | |
| if (MmcHostInstance->CardInfo.ECSDData) { | |
| FreePages (MmcHostInstance->CardInfo.ECSDData, EFI_SIZE_TO_PAGES (sizeof (ECSD))); | |
| } | |
| FreePool (MmcHostInstance); | |
| return Status; | |
| } | |
| /** | |
| This function checks if the controller implement the Mmc Host and the Device Path Protocols | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| MmcDriverBindingSupported ( | |
| IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
| IN EFI_HANDLE Controller, | |
| IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| // EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; | |
| EFI_MMC_HOST_PROTOCOL *MmcHost; | |
| EFI_DEV_PATH_PTR Node; | |
| // | |
| // Check RemainingDevicePath validation | |
| // | |
| if (RemainingDevicePath != NULL) { | |
| // | |
| // Check if RemainingDevicePath is the End of Device Path Node, | |
| // if yes, go on checking other conditions | |
| // | |
| if (!IsDevicePathEnd (RemainingDevicePath)) { | |
| // | |
| // If RemainingDevicePath isn't the End of Device Path Node, | |
| // check its validation | |
| // | |
| Node.DevPath = RemainingDevicePath; | |
| if ((Node.DevPath->Type != HARDWARE_DEVICE_PATH) || | |
| (Node.DevPath->SubType != HW_VENDOR_DP) || | |
| (DevicePathNodeLength (Node.DevPath) != sizeof (VENDOR_DEVICE_PATH))) | |
| { | |
| return EFI_UNSUPPORTED; | |
| } | |
| } | |
| } | |
| // | |
| // Check if Mmc Host protocol is installed by platform | |
| // | |
| Status = gBS->OpenProtocol ( | |
| Controller, | |
| &gEmbeddedMmcHostProtocolGuid, | |
| (VOID **)&MmcHost, | |
| This->DriverBindingHandle, | |
| Controller, | |
| EFI_OPEN_PROTOCOL_BY_DRIVER | |
| ); | |
| if (Status == EFI_ALREADY_STARTED) { | |
| return EFI_SUCCESS; | |
| } | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| // | |
| // Close the Mmc Host used to perform the supported test | |
| // | |
| gBS->CloseProtocol ( | |
| Controller, | |
| &gEmbeddedMmcHostProtocolGuid, | |
| This->DriverBindingHandle, | |
| Controller | |
| ); | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| MmcDriverBindingStart ( | |
| IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
| IN EFI_HANDLE Controller, | |
| IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| MMC_HOST_INSTANCE *MmcHostInstance; | |
| EFI_MMC_HOST_PROTOCOL *MmcHost; | |
| // | |
| // Check RemainingDevicePath validation | |
| // | |
| if (RemainingDevicePath != NULL) { | |
| // | |
| // Check if RemainingDevicePath is the End of Device Path Node, | |
| // if yes, return EFI_SUCCESS | |
| // | |
| if (IsDevicePathEnd (RemainingDevicePath)) { | |
| return EFI_SUCCESS; | |
| } | |
| } | |
| // | |
| // Get the Mmc Host protocol | |
| // | |
| Status = gBS->OpenProtocol ( | |
| Controller, | |
| &gEmbeddedMmcHostProtocolGuid, | |
| (VOID **)&MmcHost, | |
| This->DriverBindingHandle, | |
| Controller, | |
| EFI_OPEN_PROTOCOL_BY_DRIVER | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| if (Status == EFI_ALREADY_STARTED) { | |
| return EFI_SUCCESS; | |
| } | |
| return Status; | |
| } | |
| MmcHostInstance = CreateMmcHostInstance (MmcHost); | |
| if (MmcHostInstance != NULL) { | |
| // Add the handle to the pool | |
| InsertMmcHost (MmcHostInstance); | |
| MmcHostInstance->Initialized = FALSE; | |
| // Detect card presence now | |
| CheckCardsCallback (NULL, NULL); | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| MmcDriverBindingStop ( | |
| IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
| IN EFI_HANDLE Controller, | |
| IN UINTN NumberOfChildren, | |
| IN EFI_HANDLE *ChildHandleBuffer | |
| ) | |
| { | |
| EFI_STATUS Status = EFI_SUCCESS; | |
| LIST_ENTRY *CurrentLink; | |
| MMC_HOST_INSTANCE *MmcHostInstance; | |
| MMC_TRACE ("MmcDriverBindingStop()"); | |
| // For each MMC instance | |
| CurrentLink = mMmcHostPool.ForwardLink; | |
| while (CurrentLink != NULL && CurrentLink != &mMmcHostPool && (Status == EFI_SUCCESS)) { | |
| MmcHostInstance = MMC_HOST_INSTANCE_FROM_LINK (CurrentLink); | |
| ASSERT (MmcHostInstance != NULL); | |
| // Close gEmbeddedMmcHostProtocolGuid | |
| Status = gBS->CloseProtocol ( | |
| Controller, | |
| &gEmbeddedMmcHostProtocolGuid, | |
| This->DriverBindingHandle, | |
| Controller | |
| ); | |
| // Remove MMC Host Instance from the pool | |
| RemoveMmcHost (MmcHostInstance); | |
| // Destroy MmcHostInstance | |
| DestroyMmcHostInstance (MmcHostInstance); | |
| } | |
| return Status; | |
| } | |
| VOID | |
| EFIAPI | |
| CheckCardsCallback ( | |
| IN EFI_EVENT Event, | |
| IN VOID *Context | |
| ) | |
| { | |
| LIST_ENTRY *CurrentLink; | |
| MMC_HOST_INSTANCE *MmcHostInstance; | |
| EFI_STATUS Status; | |
| CurrentLink = mMmcHostPool.ForwardLink; | |
| while (CurrentLink != NULL && CurrentLink != &mMmcHostPool) { | |
| MmcHostInstance = MMC_HOST_INSTANCE_FROM_LINK (CurrentLink); | |
| ASSERT (MmcHostInstance != NULL); | |
| if (MmcHostInstance->MmcHost->IsCardPresent (MmcHostInstance->MmcHost) == !MmcHostInstance->Initialized) { | |
| MmcHostInstance->State = MmcHwInitializationState; | |
| MmcHostInstance->BlockIo.Media->MediaPresent = !MmcHostInstance->Initialized; | |
| MmcHostInstance->Initialized = !MmcHostInstance->Initialized; | |
| if (MmcHostInstance->BlockIo.Media->MediaPresent) { | |
| InitializeMmcDevice (MmcHostInstance); | |
| } | |
| Status = gBS->ReinstallProtocolInterface ( | |
| (MmcHostInstance->MmcHandle), | |
| &gEfiBlockIoProtocolGuid, | |
| &(MmcHostInstance->BlockIo), | |
| &(MmcHostInstance->BlockIo) | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| Print (L"MMC Card: Error reinstalling BlockIo interface\n"); | |
| } | |
| } | |
| CurrentLink = CurrentLink->ForwardLink; | |
| } | |
| } | |
| EFI_DRIVER_BINDING_PROTOCOL gMmcDriverBinding = { | |
| MmcDriverBindingSupported, | |
| MmcDriverBindingStart, | |
| MmcDriverBindingStop, | |
| 0xa, | |
| NULL, | |
| NULL | |
| }; | |
| /** | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| MmcDxeInitialize ( | |
| IN EFI_HANDLE ImageHandle, | |
| IN EFI_SYSTEM_TABLE *SystemTable | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| // | |
| // Initializes MMC Host pool | |
| // | |
| InitializeMmcHostPool (); | |
| // | |
| // Install driver model protocol(s). | |
| // | |
| Status = EfiLibInstallDriverBindingComponentName2 ( | |
| ImageHandle, | |
| SystemTable, | |
| &gMmcDriverBinding, | |
| ImageHandle, | |
| &gMmcComponentName, | |
| &gMmcComponentName2 | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| // Install driver diagnostics | |
| Status = gBS->InstallMultipleProtocolInterfaces ( | |
| &ImageHandle, | |
| &gEfiDriverDiagnostics2ProtocolGuid, | |
| &gMmcDriverDiagnostics2, | |
| NULL | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| // Use a timer to detect if a card has been plugged in or removed | |
| Status = gBS->CreateEvent ( | |
| EVT_NOTIFY_SIGNAL | EVT_TIMER, | |
| TPL_CALLBACK, | |
| CheckCardsCallback, | |
| NULL, | |
| &gCheckCardsEvent | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| Status = gBS->SetTimer ( | |
| gCheckCardsEvent, | |
| TimerPeriodic, | |
| (UINT64)(10*1000*200) | |
| ); // 200 ms | |
| ASSERT_EFI_ERROR (Status); | |
| return Status; | |
| } |