From: Jan Dąbroś jsd@semihalf.com
MvSdMmcPciHcDxe driver allows managing XENON controller produces EFI_SD_MMC_PASS_THRU_PROTOCOL to allow sending SD/MMC commands to specified devices from upper layer. This solution is based on SdMmcPciHcDxe from MdeModulePkg.
Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Jan Dabros jsd@semihalf.com Signed-off-by: Marcin Wojtas mw@semihalf.com --- Documentation/Marvell/UserGuide.txt | 1 + Drivers/Sd/MvSdMmcPciHcDxe.c | 1268 +++++++++++++++++++++++++++++++++++ Drivers/Sd/MvSdMmcPciHcDxe.inf | 75 +++ Drivers/Sd/XenonSdhci.c | 424 ++++++++++++ Drivers/Sd/XenonSdhci.h | 301 +++++++++ 5 files changed, 2069 insertions(+) create mode 100644 Drivers/Sd/MvSdMmcPciHcDxe.c create mode 100644 Drivers/Sd/MvSdMmcPciHcDxe.inf create mode 100644 Drivers/Sd/XenonSdhci.c create mode 100644 Drivers/Sd/XenonSdhci.h
diff --git a/Documentation/Marvell/UserGuide.txt b/Documentation/Marvell/UserGuide.txt index 5d3612e..6def7d8 100644 --- a/Documentation/Marvell/UserGuide.txt +++ b/Documentation/Marvell/UserGuide.txt @@ -13,6 +13,7 @@ Table of contents: ramdisk - PortingGuide/Ramdisk.txt reset library - PortingGuide/Reset.txt sata - PortingGuide/Sata.txt + sd/mmc - PortingGuide/PciEmulation.txt spi - PortingGuide/Spi.txt spi flash - PortingGuide/SpiFlash.txt usb - PortingGuide/PciEmulation.txt diff --git a/Drivers/Sd/MvSdMmcPciHcDxe.c b/Drivers/Sd/MvSdMmcPciHcDxe.c new file mode 100644 index 0000000..b1a4d71 --- /dev/null +++ b/Drivers/Sd/MvSdMmcPciHcDxe.c @@ -0,0 +1,1268 @@ +/** @file + This driver is used to manage SD/MMC PCI host controllers which are compliance + with SD Host Controller Simplified Specification version 3.00. + + It would expose EFI_SD_MMC_PASS_THRU_PROTOCOL for upper layer use. + + Copyright (c) 2015, Intel Corporation. All rights reserved.<BR> + Copyright (c) 2016, Marvell. All rights reserved.<BR> + + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "XenonSdhci.h" + +// +// Driver Global Variables +// +EFI_DRIVER_BINDING_PROTOCOL gSdMmcPciHcDriverBinding = { + SdMmcPciHcDriverBindingSupported, + SdMmcPciHcDriverBindingStart, + SdMmcPciHcDriverBindingStop, + 0x10, + NULL, + NULL +}; + +// +// Template for SD/MMC host controller private data. +// +SD_MMC_HC_PRIVATE_DATA gSdMmcPciHcTemplate = { + SD_MMC_HC_PRIVATE_SIGNATURE, // Signature + NULL, // ControllerHandle + NULL, // PciIo + { // PassThru + sizeof (UINT32), + SdMmcPassThruPassThru, + SdMmcPassThruGetNextSlot, + SdMmcPassThruBuildDevicePath, + SdMmcPassThruGetSlotNumber, + SdMmcPassThruResetDevice + }, + 0, // PciAttributes + 0, // PreviousSlot + NULL, // TimerEvent + NULL, // ConnectEvent + // Queue + INITIALIZE_LIST_HEAD_VARIABLE (gSdMmcPciHcTemplate.Queue), + { // Slot + {0, UnknownSlot, 0, 0}, {0, UnknownSlot, 0, 0}, {0, UnknownSlot, 0, 0}, + {0, UnknownSlot, 0, 0}, {0, UnknownSlot, 0, 0}, {0, UnknownSlot, 0, 0} + }, + { // Capability + {0}, + }, + { // MaxCurrent + 0, + }, + 0 // ControllerVersion +}; + +SD_DEVICE_PATH mSdDpTemplate = { + { + MESSAGING_DEVICE_PATH, + MSG_SD_DP, + { + (UINT8) (sizeof (SD_DEVICE_PATH)), + (UINT8) ((sizeof (SD_DEVICE_PATH)) >> 8) + } + }, + 0 +}; + +EMMC_DEVICE_PATH mEmmcDpTemplate = { + { + MESSAGING_DEVICE_PATH, + MSG_EMMC_DP, + { + (UINT8) (sizeof (EMMC_DEVICE_PATH)), + (UINT8) ((sizeof (EMMC_DEVICE_PATH)) >> 8) + } + }, + 0 +}; + +// +// Prioritized function list to detect card type. +// User could add other card detection logic here. +// +CARD_TYPE_DETECT_ROUTINE mCardTypeDetectRoutineTable[] = { + EmmcIdentification, + SdCardIdentification, + NULL +}; + +/** + The entry point for SD host controller driver, used to install this driver on the ImageHandle. + + @param[in] ImageHandle The firmware allocated handle for this driver image. + @param[in] SystemTable Pointer to the EFI system table. + + @retval EFI_SUCCESS Driver loaded. + @retval other Driver not loaded. + +**/ +EFI_STATUS +EFIAPI +InitializeSdMmcPciHcDxe ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gSdMmcPciHcDriverBinding, + ImageHandle, + &gSdMmcPciHcComponentName, + &gSdMmcPciHcComponentName2 + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} + +/** + Call back function when the timer event is signaled. + + @param[in] Event The Event this notify function registered to. + @param[in] Context Pointer to the context data registered to the + Event. + +**/ +VOID +EFIAPI +ProcessAsyncTaskList ( + IN EFI_EVENT Event, + IN VOID* Context + ) +{ + SD_MMC_HC_PRIVATE_DATA *Private; + LIST_ENTRY *Link; + SD_MMC_HC_TRB *Trb; + EFI_STATUS Status; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet; + BOOLEAN InfiniteWait; + EFI_EVENT TrbEvent; + + Private = (SD_MMC_HC_PRIVATE_DATA*)Context; + + // + // Check if the first entry in the async I/O queue is done or not. + // + Status = EFI_SUCCESS; + Trb = NULL; + Link = GetFirstNode (&Private->Queue); + if (!IsNull (&Private->Queue, Link)) { + Trb = SD_MMC_HC_TRB_FROM_THIS (Link); + if (Private->Slot[Trb->Slot].MediaPresent == FALSE) { + Status = EFI_NO_MEDIA; + goto Done; + } + if (!Trb->Started) { + // + // Check whether the Cmd/data line is ready for transfer. + // + Status = SdMmcCheckTrbEnv (Private, Trb); + if (!EFI_ERROR (Status)) { + Trb->Started = TRUE; + Status = SdMmcExecTrb (Private, Trb); + if (EFI_ERROR (Status)) { + goto Done; + } + } else { + goto Done; + } + } + Status = SdMmcCheckTrbResult (Private, Trb); + } + +Done: + if ((Trb != NULL) && (Status == EFI_NOT_READY)) { + Packet = Trb->Packet; + if (Packet->Timeout == 0) { + InfiniteWait = TRUE; + } else { + InfiniteWait = FALSE; + } + if ((!InfiniteWait) && (Trb->Timeout-- == 0)) { + RemoveEntryList (Link); + Trb->Packet->TransactionStatus = EFI_TIMEOUT; + TrbEvent = Trb->Event; + SdMmcFreeTrb (Trb); + DEBUG ((EFI_D_VERBOSE, "ProcessAsyncTaskList(): Signal Event %p EFI_TIMEOUT\n", TrbEvent)); + gBS->SignalEvent (TrbEvent); + return; + } + } + if ((Trb != NULL) && (Status != EFI_NOT_READY)) { + RemoveEntryList (Link); + Trb->Packet->TransactionStatus = Status; + TrbEvent = Trb->Event; + SdMmcFreeTrb (Trb); + DEBUG ((EFI_D_VERBOSE, "ProcessAsyncTaskList(): Signal Event %p with %r\n", TrbEvent, Status)); + gBS->SignalEvent (TrbEvent); + } + return; +} + +/** + Sd removable device enumeration callback function when the timer event is signaled. + + @param[in] Event The Event this notify function registered to. + @param[in] Context Pointer to the context data registered to the + Event. + +**/ +VOID +EFIAPI +SdMmcPciHcEnumerateDevice ( + IN EFI_EVENT Event, + IN VOID* Context + ) +{ + SD_MMC_HC_PRIVATE_DATA *Private; + EFI_STATUS Status; + UINT8 Slot; + BOOLEAN MediaPresent; + UINT32 RoutineNum; + CARD_TYPE_DETECT_ROUTINE *Routine; + UINTN Index; + LIST_ENTRY *Link; + LIST_ENTRY *NextLink; + SD_MMC_HC_TRB *Trb; + + Private = (SD_MMC_HC_PRIVATE_DATA*)Context; + + for (Slot = 0; Slot < SD_MMC_HC_MAX_SLOT; Slot++) { + if ((Private->Slot[Slot].Enable) && (Private->Slot[Slot].SlotType == RemovableSlot)) { + Status = SdMmcHcCardDetect (Private->PciIo, Slot, &MediaPresent); + if ((Status == EFI_MEDIA_CHANGED) && (MediaPresent == FALSE)) { + DEBUG ((EFI_D_INFO, "SdMmcPciHcEnumerateDevice: device disconnected at slot %d of pci %p\n", Slot, Private->PciIo)); + Private->Slot[Slot].MediaPresent = FALSE; + // + // Signal all async task events at the slot with EFI_NO_MEDIA status. + // + for (Link = GetFirstNode (&Private->Queue); + !IsNull (&Private->Queue, Link); + Link = NextLink) { + NextLink = GetNextNode (&Private->Queue, Link); + Trb = SD_MMC_HC_TRB_FROM_THIS (Link); + if (Trb->Slot == Slot) { + RemoveEntryList (Link); + Trb->Packet->TransactionStatus = EFI_NO_MEDIA; + gBS->SignalEvent (Trb->Event); + SdMmcFreeTrb (Trb); + } + } + // + // Notify the upper layer the connect state change through ReinstallProtocolInterface. + // + gBS->ReinstallProtocolInterface ( + Private->ControllerHandle, + &gEfiSdMmcPassThruProtocolGuid, + &Private->PassThru, + &Private->PassThru + ); + } + if ((Status == EFI_MEDIA_CHANGED) && (MediaPresent == TRUE)) { + DEBUG ((EFI_D_INFO, "SdMmcPciHcEnumerateDevice: device connected at slot %d of pci %p\n", Slot, Private->PciIo)); + // + // Reinitialize slot and restart identification process for the new attached device + // + Status = SdMmcHcInitHost (Private->PciIo, Slot, Private->Capability[Slot]); + if (EFI_ERROR (Status)) { + continue; + } + + Private->Slot[Slot].MediaPresent = TRUE; + RoutineNum = sizeof (mCardTypeDetectRoutineTable) / sizeof (CARD_TYPE_DETECT_ROUTINE); + for (Index = 0; Index < RoutineNum; Index++) { + Routine = &mCardTypeDetectRoutineTable[Index]; + if (*Routine != NULL) { + Status = (*Routine) (Private, Slot); + if (!EFI_ERROR (Status)) { + break; + } + } + } + + // + // Notify the upper layer the connect state change through ReinstallProtocolInterface. + // + gBS->ReinstallProtocolInterface ( + Private->ControllerHandle, + &gEfiSdMmcPassThruProtocolGuid, + &Private->PassThru, + &Private->PassThru + ); + } + } + } + + return; +} +/** + Tests to see if this driver supports a given controller. If a child device is provided, + it further tests to see if this driver supports creating a handle for the specified child device. + + This function checks to see if the driver specified by This supports the device specified by + ControllerHandle. Drivers will typically use the device path attached to + ControllerHandle and/or the services from the bus I/O abstraction attached to + ControllerHandle to determine if the driver supports ControllerHandle. This function + may be called many times during platform initialization. In order to reduce boot times, the tests + performed by this function must be very small, and take as little time as possible to execute. This + function must not change the state of any hardware devices, and this function must be aware that the + device specified by ControllerHandle may already be managed by the same driver or a + different driver. This function must match its calls to AllocatePages() with FreePages(), + AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol(). + Since ControllerHandle may have been previously started by the same driver, if a protocol is + already in the opened state, then it must not be closed with CloseProtocol(). This is required + to guarantee the state of ControllerHandle is not modified by this function. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to test. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For bus drivers, if this parameter is not NULL, then + the bus driver must determine if the bus controller specified + by ControllerHandle and the child controller specified + by RemainingDevicePath are both supported by this + bus driver. + + @retval EFI_SUCCESS The device specified by ControllerHandle and + RemainingDevicePath is supported by the driver specified by This. + @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by the driver + specified by This. + @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by a different + driver or an application that requires exclusive access. + Currently not implemented. + @retval EFI_UNSUPPORTED The device specified by ControllerHandle and + RemainingDevicePath is not supported by the driver specified by This. +**/ +EFI_STATUS +EFIAPI +SdMmcPciHcDriverBindingSupported ( + 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_PCI_IO_PROTOCOL *PciIo; + PCI_TYPE00 PciData; + + PciIo = NULL; + ParentDevicePath = NULL; + + // + // SdPciHcDxe is a device driver, and should ingore the + // "RemainingDevicePath" according to EFI spec. + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + (VOID *) &ParentDevicePath, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + // + // EFI_ALREADY_STARTED is also an error. + // + return Status; + } + // + // Close the protocol because we don't use it here. + // + gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + // + // Now test the EfiPciIoProtocol. + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Now further check the PCI header: Base class (offset 0x08) and + // Sub Class (offset 0x05). This controller should be an SD/MMC PCI + // Host Controller. + // + Status = PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint8, + 0, + sizeof (PciData), + &PciData + ); + if (EFI_ERROR (Status)) { + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + return EFI_UNSUPPORTED; + } + // + // Since we already got the PciData, we can close protocol to avoid to carry it + // on for multiple exit points. + // + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + // + // Examine SD PCI Host Controller PCI Configuration table fields. + // + if ((PciData.Hdr.ClassCode[2] == PCI_CLASS_SYSTEM_PERIPHERAL) && + (PciData.Hdr.ClassCode[1] == PCI_SUBCLASS_SD_HOST_CONTROLLER) && + ((PciData.Hdr.ClassCode[0] == 0x00) || (PciData.Hdr.ClassCode[0] == 0x01))) { + return EFI_SUCCESS; + } + + return EFI_UNSUPPORTED; +} + +/** + Starts a device controller or a bus controller. + + The Start() function is designed to be invoked from the EFI boot service ConnectController(). + As a result, much of the error checking on the parameters to Start() has been moved into this + common boot service. It is legal to call Start() from other locations, + but the following calling restrictions must be followed or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE. + 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned + EFI_DEVICE_PATH_PROTOCOL. + 3. Prior to calling Start(), the Supported() function for the driver specified by This must + have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to start. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For a bus driver, if this parameter is NULL, then handles + for all the children of Controller are created by this driver. + If this parameter is not NULL and the first Device Path Node is + not the End of Device Path Node, then only the handle for the + child device specified by the first Device Path Node of + RemainingDevicePath is created by this driver. + If the first Device Path Node of RemainingDevicePath is + the End of Device Path Node, no child handle is created by this + driver. + + @retval EFI_SUCCESS The device was started. + @retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval Others The driver failded to start the device. + +**/ +EFI_STATUS +EFIAPI +SdMmcPciHcDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + SD_MMC_HC_PRIVATE_DATA *Private; + EFI_PCI_IO_PROTOCOL *PciIo; + UINT64 Supports; + UINT64 PciAttributes; + UINT8 Slot; + UINT8 Index; + CARD_TYPE_DETECT_ROUTINE *Routine; + UINT32 RoutineNum; + + DEBUG ((EFI_D_INFO, "SdMmcPciHcDriverBindingStart: Start\n")); + + // + // Open PCI I/O Protocol and save pointer to open protocol + // in private data area. + // + PciIo = NULL; + Status = gBS->OpenProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Enable the SD Host Controller MMIO space + // + Private = NULL; + Status = PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationGet, + 0, + &PciAttributes + ); + + if (EFI_ERROR (Status)) { + goto Done; + } + + Status = PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationSupported, + 0, + &Supports + ); + + if (!EFI_ERROR (Status)) { + Supports &= (UINT64)EFI_PCI_DEVICE_ENABLE; + Status = PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationEnable, + Supports, + NULL + ); + } else { + goto Done; + } + + Private = AllocateCopyPool (sizeof (SD_MMC_HC_PRIVATE_DATA), &gSdMmcPciHcTemplate); + if (Private == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + + Private->ControllerHandle = Controller; + Private->PciIo = PciIo; + Private->PciAttributes = PciAttributes; + InitializeListHead (&Private->Queue); + + XenonReadVersion (PciIo, &Private->ControllerVersion); + + XenonSetFifo (PciIo); + + XenonSetAcg (PciIo, FALSE); + + // Hyperion has only one port + XenonSetSlot (PciIo, XENON_MMC_SLOT_ID_HYPERION, TRUE); + + XenonSetPower (PciIo, MMC_VDD_32_33, eMMC_VCCQ_3_3V, XENON_MMC_MODE_SD_SDIO); + + XenonSetClk (PciIo, Private, XENON_MMC_MAX_CLK); + + XenonSetPhy (PciIo, MMC_TIMING_UHS_SDR50); + + XenonConfigureInterrupts (PciIo); + + // Enable parallel transfer + XenonSetParallelTransfer (PciIo, XENON_MMC_SLOT_ID_HYPERION, TRUE); + + XenonSetTuning (PciIo, XENON_MMC_SLOT_ID_HYPERION, FALSE); + + XenonSetAcg (PciIo, TRUE); + + for (Slot = 0; Slot < (XENON_MMC_SLOT_ID_HYPERION + 1); Slot++) { + Private->Slot[Slot].Enable = TRUE; + + Status = SdMmcHcGetCapability (PciIo, Slot, &Private->Capability[Slot]); + if (EFI_ERROR (Status)) { + continue; + } + DumpCapabilityReg (Slot, &Private->Capability[Slot]); + + Status = SdMmcHcGetMaxCurrent (PciIo, Slot, &Private->MaxCurrent[Slot]); + if (EFI_ERROR (Status)) { + continue; + } + + Private->Slot[Slot].SlotType = Private->Capability[Slot].SlotType; + if ((Private->Slot[Slot].SlotType != RemovableSlot) && (Private->Slot[Slot].SlotType != EmbeddedSlot)) { + DEBUG ((EFI_D_INFO, "SdMmcPciHcDxe doesn't support the slot type [%d]!!!\n", Private->Slot[Slot].SlotType)); + continue; + } + + // + // Reset the specified slot of the SD/MMC Pci Host Controller + // + Status = SdMmcHcReset (PciIo, Slot); + if (EFI_ERROR (Status)) { + continue; + } + + Status = SdMmcHcInitHost (PciIo, Slot, Private->Capability[Slot]); + if (EFI_ERROR (Status)) { + continue; + } + + Private->Slot[Slot].MediaPresent = TRUE; + RoutineNum = sizeof (mCardTypeDetectRoutineTable) / sizeof (CARD_TYPE_DETECT_ROUTINE); + for (Index = 0; Index < RoutineNum; Index++) { + Routine = &mCardTypeDetectRoutineTable[Index]; + if (*Routine != NULL) { + Status = (*Routine) (Private, Slot); + if (!EFI_ERROR (Status)) { + break; + } + } + } + } + + // + // Start the asynchronous I/O monitor + // + Status = gBS->CreateEvent ( + EVT_TIMER | EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + ProcessAsyncTaskList, + Private, + &Private->TimerEvent + ); + if (EFI_ERROR (Status)) { + goto Done; + } + + Status = gBS->SetTimer (Private->TimerEvent, TimerPeriodic, SD_MMC_HC_ASYNC_TIMER); + if (EFI_ERROR (Status)) { + goto Done; + } + + // + // Start the Sd removable device connection enumeration + // + Status = gBS->CreateEvent ( + EVT_TIMER | EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + SdMmcPciHcEnumerateDevice, + Private, + &Private->ConnectEvent + ); + if (EFI_ERROR (Status)) { + goto Done; + } + + Status = gBS->SetTimer (Private->ConnectEvent, TimerPeriodic, SD_MMC_HC_ENUM_TIMER); + if (EFI_ERROR (Status)) { + goto Done; + } + + Status = gBS->InstallMultipleProtocolInterfaces ( + &Controller, + &gEfiSdMmcPassThruProtocolGuid, + &(Private->PassThru), + NULL + ); + + DEBUG ((EFI_D_INFO, "SdMmcPciHcDriverBindingStart: %r End on %x\n", Status, Controller)); + +Done: + if (EFI_ERROR (Status)) { + if ((Private != NULL) && (Private->PciAttributes != 0)) { + // + // Restore original PCI attributes + // + PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationSet, + Private->PciAttributes, + NULL + ); + } + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + if ((Private != NULL) && (Private->TimerEvent != NULL)) { + gBS->CloseEvent (Private->TimerEvent); + } + + if ((Private != NULL) && (Private->ConnectEvent != NULL)) { + gBS->CloseEvent (Private->ConnectEvent); + } + + if (Private != NULL) { + FreePool (Private); + } + } + + return Status; +} + +/** + Stops a device controller or a bus controller. + + The Stop() function is designed to be invoked from the EFI boot service DisconnectController(). + As a result, much of the error checking on the parameters to Stop() has been moved + into this common boot service. It is legal to call Stop() from other locations, + but the following calling restrictions must be followed or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this + same driver's Start() function. + 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid + EFI_HANDLE. In addition, all of these handles must have been created in this driver's + Start() function, and the Start() function must have called OpenProtocol() on + ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle A handle to the device being stopped. The handle must + support a bus specific I/O protocol for the driver + to use to stop the device. + @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +SdMmcPciHcDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_STATUS Status; + EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru; + SD_MMC_HC_PRIVATE_DATA *Private; + EFI_PCI_IO_PROTOCOL *PciIo; + LIST_ENTRY *Link; + LIST_ENTRY *NextLink; + SD_MMC_HC_TRB *Trb; + + DEBUG ((EFI_D_INFO, "SdMmcPciHcDriverBindingStop: Start\n")); + + Status = gBS->OpenProtocol ( + Controller, + &gEfiSdMmcPassThruProtocolGuid, + (VOID**) &PassThru, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Private = SD_MMC_HC_PRIVATE_FROM_THIS (PassThru); + // + // Close Non-Blocking timer and free Task list. + // + if (Private->TimerEvent != NULL) { + gBS->CloseEvent (Private->TimerEvent); + Private->TimerEvent = NULL; + } + if (Private->ConnectEvent != NULL) { + gBS->CloseEvent (Private->ConnectEvent); + Private->ConnectEvent = NULL; + } + // + // As the timer is closed, there is no needs to use TPL lock to + // protect the critical region "queue". + // + for (Link = GetFirstNode (&Private->Queue); + !IsNull (&Private->Queue, Link); + Link = NextLink) { + NextLink = GetNextNode (&Private->Queue, Link); + RemoveEntryList (Link); + Trb = SD_MMC_HC_TRB_FROM_THIS (Link); + Trb->Packet->TransactionStatus = EFI_ABORTED; + gBS->SignalEvent (Trb->Event); + SdMmcFreeTrb (Trb); + } + + // + // Uninstall Block I/O protocol from the device handle + // + Status = gBS->UninstallProtocolInterface ( + Controller, + &gEfiSdMmcPassThruProtocolGuid, + &(Private->PassThru) + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + // + // Restore original PCI attributes + // + PciIo = Private->PciIo; + Status = PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationSet, + Private->PciAttributes, + NULL + ); + ASSERT_EFI_ERROR (Status); + + FreePool (Private); + + DEBUG ((EFI_D_INFO, "SdMmcPciHcDriverBindingStop: End with %r\n", Status)); + + return Status; +} + +/** + Sends SD command to an SD card that is attached to the SD controller. + + The PassThru() function sends the SD command specified by Packet to the SD card + specified by Slot. + + If Packet is successfully sent to the SD card, then EFI_SUCCESS is returned. + + If a device error occurs while sending the Packet, then EFI_DEVICE_ERROR is returned. + + If Slot is not in a valid range for the SD controller, then EFI_INVALID_PARAMETER + is returned. + + If Packet defines a data command but both InDataBuffer and OutDataBuffer are NULL, + EFI_INVALID_PARAMETER is returned. + + @param[in] This A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[in,out] Packet A pointer to the SD command data structure. + @param[in] Event If Event is NULL, blocking I/O is performed. If Event is + not NULL, then nonblocking I/O is performed, and Event + will be signaled when the Packet completes. + + @retval EFI_SUCCESS The SD Command Packet was sent by the host. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to send the SD + command Packet. + @retval EFI_INVALID_PARAMETER Packet, Slot, or the contents of the Packet is invalid. + @retval EFI_INVALID_PARAMETER Packet defines a data command but both InDataBuffer and + OutDataBuffer are NULL. + @retval EFI_NO_MEDIA SD Device not present in the Slot. + @retval EFI_UNSUPPORTED The command described by the SD Command Packet is not + supported by the host controller. + @retval EFI_BAD_BUFFER_SIZE The InTransferLength or OutTransferLength exceeds the + limit supported by SD card ( i.e. if the number of bytes + exceed the Last LBA). + +**/ +EFI_STATUS +EFIAPI +SdMmcPassThruPassThru ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *This, + IN UINT8 Slot, + IN OUT EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet, + IN EFI_EVENT Event OPTIONAL + ) +{ + EFI_STATUS Status; + SD_MMC_HC_PRIVATE_DATA *Private; + SD_MMC_HC_TRB *Trb; + EFI_TPL OldTpl; + + if ((This == NULL) || (Packet == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if ((Packet->SdMmcCmdBlk == NULL) || (Packet->SdMmcStatusBlk == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if ((Packet->OutDataBuffer == NULL) && (Packet->OutTransferLength != 0)) { + return EFI_INVALID_PARAMETER; + } + + if ((Packet->InDataBuffer == NULL) && (Packet->InTransferLength != 0)) { + return EFI_INVALID_PARAMETER; + } + + Private = SD_MMC_HC_PRIVATE_FROM_THIS (This); + + if (!Private->Slot[Slot].Enable) { + return EFI_INVALID_PARAMETER; + } + + if (!Private->Slot[Slot].MediaPresent) { + return EFI_NO_MEDIA; + } + + Trb = SdMmcCreateTrb (Private, Slot, Packet, Event); + if (Trb == NULL) { + return EFI_OUT_OF_RESOURCES; + } + // + // Immediately return for async I/O. + // + if (Event != NULL) { + return EFI_SUCCESS; + } + + // + // Wait async I/O list is empty before execute sync I/O operation. + // + while (TRUE) { + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + if (IsListEmpty (&Private->Queue)) { + gBS->RestoreTPL (OldTpl); + break; + } + gBS->RestoreTPL (OldTpl); + } + + Status = SdMmcWaitTrbEnv (Private, Trb); + if (EFI_ERROR (Status)) { + goto Done; + } + + Status = SdMmcExecTrb (Private, Trb); + if (EFI_ERROR (Status)) { + goto Done; + } + + Status = SdMmcWaitTrbResult (Private, Trb); + if (EFI_ERROR (Status)) { + goto Done; + } + +Done: + if ((Trb != NULL) && (Trb->AdmaDesc != NULL)) { + FreePages (Trb->AdmaDesc, Trb->AdmaPages); + } + + if (Trb != NULL) { + FreePool (Trb); + } + + return Status; +} + +/** + Used to retrieve next slot numbers supported by the SD controller. The function + returns information about all available slots (populated or not-populated). + + The GetNextSlot() function retrieves the next slot number on an SD controller. + If on input Slot is 0xFF, then the slot number of the first slot on the SD controller + is returned. + + If Slot is a slot number that was returned on a previous call to GetNextSlot(), then + the slot number of the next slot on the SD controller is returned. + + If Slot is not 0xFF and Slot was not returned on a previous call to GetNextSlot(), + EFI_INVALID_PARAMETER is returned. + + If Slot is the slot number of the last slot on the SD controller, then EFI_NOT_FOUND + is returned. + + @param[in] This A pointer to the EFI_SD_MMMC_PASS_THRU_PROTOCOL instance. + @param[in,out] Slot On input, a pointer to a slot number on the SD controller. + On output, a pointer to the next slot number on the SD controller. + An input value of 0xFF retrieves the first slot number on the SD + controller. + + @retval EFI_SUCCESS The next slot number on the SD controller was returned in Slot. + @retval EFI_NOT_FOUND There are no more slots on this SD controller. + @retval EFI_INVALID_PARAMETER Slot is not 0xFF and Slot was not returned on a previous call + to GetNextSlot(). + +**/ +EFI_STATUS +EFIAPI +SdMmcPassThruGetNextSlot ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *This, + IN OUT UINT8 *Slot + ) +{ + SD_MMC_HC_PRIVATE_DATA *Private; + UINT8 Index; + + if ((This == NULL) || (Slot == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Private = SD_MMC_HC_PRIVATE_FROM_THIS (This); + + if (*Slot == 0xFF) { + for (Index = 0; Index < SD_MMC_HC_MAX_SLOT; Index++) { + if (Private->Slot[Index].Enable) { + *Slot = Index; + Private->PreviousSlot = Index; + return EFI_SUCCESS; + } + } + return EFI_NOT_FOUND; + } else if (*Slot == Private->PreviousSlot) { + for (Index = *Slot + 1; Index < SD_MMC_HC_MAX_SLOT; Index++) { + if (Private->Slot[Index].Enable) { + *Slot = Index; + Private->PreviousSlot = Index; + return EFI_SUCCESS; + } + } + return EFI_NOT_FOUND; + } else { + return EFI_INVALID_PARAMETER; + } +} + +/** + Used to allocate and build a device path node for an SD card on the SD controller. + + The BuildDevicePath() function allocates and builds a single device node for the SD + card specified by Slot. + + If the SD card specified by Slot is not present on the SD controller, then EFI_NOT_FOUND + is returned. + + If DevicePath is NULL, then EFI_INVALID_PARAMETER is returned. + + If there are not enough resources to allocate the device path node, then EFI_OUT_OF_RESOURCES + is returned. + + Otherwise, DevicePath is allocated with the boot service AllocatePool(), the contents of + DevicePath are initialized to describe the SD card specified by Slot, and EFI_SUCCESS is + returned. + + @param[in] This A pointer to the EFI_SD_MMMC_PASS_THRU_PROTOCOL instance. + @param[in] Slot Specifies the slot number of the SD card for which a device + path node is to be allocated and built. + @param[in,out] DevicePath A pointer to a single device path node that describes the SD + card specified by Slot. This function is responsible for + allocating the buffer DevicePath with the boot service + AllocatePool(). It is the caller's responsibility to free + DevicePath when the caller is finished with DevicePath. + + @retval EFI_SUCCESS The device path node that describes the SD card specified by + Slot was allocated and returned in DevicePath. + @retval EFI_NOT_FOUND The SD card specified by Slot does not exist on the SD controller. + @retval EFI_INVALID_PARAMETER DevicePath is NULL. + @retval EFI_OUT_OF_RESOURCES There are not enough resources to allocate DevicePath. + +**/ +EFI_STATUS +EFIAPI +SdMmcPassThruBuildDevicePath ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *This, + IN UINT8 Slot, + IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath + ) +{ + SD_MMC_HC_PRIVATE_DATA *Private; + SD_DEVICE_PATH *SdNode; + EMMC_DEVICE_PATH *EmmcNode; + + if ((This == NULL) || (DevicePath == NULL) || (Slot >= SD_MMC_HC_MAX_SLOT)) { + return EFI_INVALID_PARAMETER; + } + + Private = SD_MMC_HC_PRIVATE_FROM_THIS (This); + + if ((!Private->Slot[Slot].Enable) || (!Private->Slot[Slot].MediaPresent)) { + return EFI_NOT_FOUND; + } + + if (Private->Slot[Slot].CardType == SdCardType) { + SdNode = AllocateCopyPool (sizeof (SD_DEVICE_PATH), &mSdDpTemplate); + if (SdNode == NULL) { + return EFI_OUT_OF_RESOURCES; + } + SdNode->SlotNumber = Slot; + + *DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) SdNode; + } else if (Private->Slot[Slot].CardType == EmmcCardType) { + EmmcNode = AllocateCopyPool (sizeof (EMMC_DEVICE_PATH), &mEmmcDpTemplate); + if (EmmcNode == NULL) { + return EFI_OUT_OF_RESOURCES; + } + EmmcNode->SlotNumber = Slot; + + *DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) EmmcNode; + } else { + // + // Currently we only support SD and EMMC two device nodes. + // + return EFI_NOT_FOUND; + } + + return EFI_SUCCESS; +} + +/** + This function retrieves an SD card slot number based on the input device path. + + The GetSlotNumber() function retrieves slot number for the SD card specified by + the DevicePath node. If DevicePath is NULL, EFI_INVALID_PARAMETER is returned. + + If DevicePath is not a device path node type that the SD Pass Thru driver supports, + EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL instance. + @param[in] DevicePath A pointer to the device path node that describes a SD + card on the SD controller. + @param[out] Slot On return, points to the slot number of an SD card on + the SD controller. + + @retval EFI_SUCCESS SD card slot number is returned in Slot. + @retval EFI_INVALID_PARAMETER Slot or DevicePath is NULL. + @retval EFI_UNSUPPORTED DevicePath is not a device path node type that the SD + Pass Thru driver supports. + +**/ +EFI_STATUS +EFIAPI +SdMmcPassThruGetSlotNumber ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *This, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, + OUT UINT8 *Slot + ) +{ + SD_MMC_HC_PRIVATE_DATA *Private; + SD_DEVICE_PATH *SdNode; + EMMC_DEVICE_PATH *EmmcNode; + UINT8 SlotNumber; + + if ((This == NULL) || (DevicePath == NULL) || (Slot == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Private = SD_MMC_HC_PRIVATE_FROM_THIS (This); + + // + // Check whether the DevicePath belongs to SD_DEVICE_PATH or EMMC_DEVICE_PATH + // + if ((DevicePath->Type != MESSAGING_DEVICE_PATH) || + ((DevicePath->SubType != MSG_SD_DP) && + (DevicePath->SubType != MSG_EMMC_DP)) || + (DevicePathNodeLength(DevicePath) != sizeof(SD_DEVICE_PATH)) || + (DevicePathNodeLength(DevicePath) != sizeof(EMMC_DEVICE_PATH))) { + return EFI_UNSUPPORTED; + } + + if (DevicePath->SubType == MSG_SD_DP) { + SdNode = (SD_DEVICE_PATH *) DevicePath; + SlotNumber = SdNode->SlotNumber; + } else { + EmmcNode = (EMMC_DEVICE_PATH *) DevicePath; + SlotNumber = EmmcNode->SlotNumber; + } + + if (SlotNumber >= SD_MMC_HC_MAX_SLOT) { + return EFI_NOT_FOUND; + } + + if (Private->Slot[SlotNumber].Enable) { + *Slot = SlotNumber; + return EFI_SUCCESS; + } else { + return EFI_NOT_FOUND; + } +} + +/** + Resets an SD card that is connected to the SD controller. + + The ResetDevice() function resets the SD card specified by Slot. + + If this SD controller does not support a device reset operation, EFI_UNSUPPORTED is + returned. + + If Slot is not in a valid slot number for this SD controller, EFI_INVALID_PARAMETER + is returned. + + If the device reset operation is completed, EFI_SUCCESS is returned. + + @param[in] This A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL instance. + @param[in] Slot Specifies the slot number of the SD card to be reset. + + @retval EFI_SUCCESS The SD card specified by Slot was reset. + @retval EFI_UNSUPPORTED The SD controller does not support a device reset operation. + @retval EFI_INVALID_PARAMETER Slot number is invalid. + @retval EFI_NO_MEDIA SD Device not present in the Slot. + @retval EFI_DEVICE_ERROR The reset command failed due to a device error + +**/ +EFI_STATUS +EFIAPI +SdMmcPassThruResetDevice ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *This, + IN UINT8 Slot + ) +{ + SD_MMC_HC_PRIVATE_DATA *Private; + LIST_ENTRY *Link; + LIST_ENTRY *NextLink; + SD_MMC_HC_TRB *Trb; + EFI_TPL OldTpl; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Private = SD_MMC_HC_PRIVATE_FROM_THIS (This); + + if (!Private->Slot[Slot].Enable) { + return EFI_INVALID_PARAMETER; + } + + if (!Private->Slot[Slot].MediaPresent) { + return EFI_NO_MEDIA; + } + // + // Free all async I/O requests in the queue + // + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + for (Link = GetFirstNode (&Private->Queue); + !IsNull (&Private->Queue, Link); + Link = NextLink) { + NextLink = GetNextNode (&Private->Queue, Link); + RemoveEntryList (Link); + Trb = SD_MMC_HC_TRB_FROM_THIS (Link); + Trb->Packet->TransactionStatus = EFI_ABORTED; + gBS->SignalEvent (Trb->Event); + SdMmcFreeTrb (Trb); + } + + gBS->RestoreTPL (OldTpl); + + return EFI_SUCCESS; +} + diff --git a/Drivers/Sd/MvSdMmcPciHcDxe.inf b/Drivers/Sd/MvSdMmcPciHcDxe.inf new file mode 100644 index 0000000..e52188f --- /dev/null +++ b/Drivers/Sd/MvSdMmcPciHcDxe.inf @@ -0,0 +1,75 @@ +#/******************************************************************************* +#Copyright (C) 2016 Marvell International Ltd. +# +#Marvell BSD License Option +# +#If you received this File from Marvell, you may opt to use, redistribute and/or +#modify this File under the following licensing terms. +#Redistribution and use in source and binary forms, with or without modification, +#are permitted provided that the following conditions are met: +# +#* Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +#* Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +#* Neither the name of Marvell nor the names of its contributors may be +# used to endorse or promote products derived from this software without +# specific prior written permission. +# +#THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +#ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +#WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +#DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +#ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +#(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +#LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +#ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +#(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +#SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +#*******************************************************************************/ + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = MvSdMmcPciHcDxe + MODULE_UNI_FILE = ../../../MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdMmcPciHcDxe.uni + FILE_GUID = 5b5b83b8-52b8-4d02-b92b-c84a4dfb539d + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = InitializeSdMmcPciHcDxe + +[Sources] + ../../../MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdMmcPciHcDxe.h + MvSdMmcPciHcDxe.c + ../../../MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/EmmcDevice.c + ../../../MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdDevice.c + ../../../MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdMmcPciHci.h + ../../../MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdMmcPciHci.c + ../../../MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/ComponentName.c + XenonSdhci.c + XenonSdhci.h + +[Packages] + MdePkg/MdePkg.dec + +[LibraryClasses] + DevicePathLib + UefiBootServicesTableLib + UefiRuntimeServicesTableLib + MemoryAllocationLib + BaseMemoryLib + UefiLib + BaseLib + UefiDriverEntryPoint + DebugLib + +[Protocols] + gEfiDevicePathProtocolGuid ## TO_START + gEfiPciIoProtocolGuid ## TO_START + gEfiSdMmcPassThruProtocolGuid ## BY_START + +[UserExtensions.TianoCore."ExtraFiles"] + SdMmcPciHcDxeExtra.uni diff --git a/Drivers/Sd/XenonSdhci.c b/Drivers/Sd/XenonSdhci.c new file mode 100644 index 0000000..a6f891c --- /dev/null +++ b/Drivers/Sd/XenonSdhci.c @@ -0,0 +1,424 @@ +/******************************************************************************* +Copyright (C) 2016 Marvell International Ltd. + +Marvell BSD License Option + +If you received this File from Marvell, you may opt to use, redistribute and/or +modify this File under the following licensing terms. +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Marvell nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*******************************************************************************/ + +#include "XenonSdhci.h" + +VOID +XenonReadVersion ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + OUT UINT32 *ControllerVersion + ) +{ + + SdMmcHcRwMmio (PciIo, SD_BAR_INDEX,SD_MMC_HC_CTRL_VER, TRUE, 2, ControllerVersion); +} + +VOID +XenonSetFifo ( + IN EFI_PCI_IO_PROTOCOL *PciIo + ) +{ + UINTN Data = 0x315; + + SdMmcHcRwMmio (PciIo, SD_BAR_INDEX, SDHC_SLOT_FIFO_CTRL, FALSE, 4, &Data); +} + +// Auto Clock Gating +VOID +XenonSetAcg ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN BOOLEAN Enable + ) +{ + UINT32 Var; + + SdMmcHcRwMmio (PciIo, SD_BAR_INDEX, SDHC_SYS_OP_CTRL, TRUE, 4, &Var); + + if (Enable) { + Var |= AUTO_CLKGATE_DISABLE_MASK; + } else { + Var |= ~AUTO_CLKGATE_DISABLE_MASK; + } + + SdMmcHcRwMmio (PciIo, SD_BAR_INDEX, SDHC_SYS_OP_CTRL, FALSE, 4, &Var); +} + +VOID +XenonSetSlot ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 Slot, + IN BOOLEAN Enable + ) +{ + UINT32 Var; + + Var = SdMmcHcRwMmio (PciIo, SD_BAR_INDEX, SDHC_SYS_OP_CTRL, TRUE, 4, &Var); + if (Enable) + Var |= ((0x1 << Slot) << SLOT_ENABLE_SHIFT); + else + Var &= ~((0x1 << Slot) << SLOT_ENABLE_SHIFT); + + SdMmcHcRwMmio (PciIo, SD_BAR_INDEX, SDHC_SYS_OP_CTRL, FALSE, 4, &Var); +} + +STATIC +VOID +XenonSetSdio ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINTN Voltage + ) +{ + + return; +} + +VOID +XenonSetPower ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT32 Vcc, + IN UINT32 Vccq, + IN UINT8 Mode + ) +{ + UINT8 Pwr = 0; + UINT32 Ctrl = 0; + + // Set VCC + switch (Vcc) { + case MMC_VDD_165_195: + Pwr = SDHCI_POWER_180; + if (Mode == XENON_MMC_MODE_SD_SDIO) + XenonSetSdio (PciIo, 0); + break; + case MMC_VDD_29_30: + case MMC_VDD_30_31: + Pwr = SDHCI_POWER_300; + if (Mode == XENON_MMC_MODE_SD_SDIO) + XenonSetSdio (PciIo, 1); + break; + case MMC_VDD_32_33: + case MMC_VDD_33_34: + Pwr = SDHCI_POWER_330; + if (Mode == XENON_MMC_MODE_SD_SDIO) + XenonSetSdio (PciIo, 1); + break; + default: + DEBUG((DEBUG_ERROR, "Does not support power mode(0x%X)\n", Vcc)); + break; + } + + if (Pwr == 0) { + SdMmcHcRwMmio (PciIo, SD_BAR_INDEX, SD_MMC_HC_POWER_CTRL, FALSE, 1, &Pwr); + return; + } + + Pwr |= SDHCI_POWER_ON; + + SdMmcHcRwMmio (PciIo, SD_BAR_INDEX,SD_MMC_HC_POWER_CTRL, FALSE, 1, &Pwr); + + // Set VCCQ + SdMmcHcRwMmio (PciIo, SD_BAR_INDEX, SDHC_SLOT_eMMC_CTRL, TRUE, 4, &Ctrl); + Ctrl |= Vccq; + SdMmcHcRwMmio (PciIo, SD_BAR_INDEX, SDHC_SLOT_eMMC_CTRL, FALSE, 4, &Ctrl); +} + +UINTN +XenonSetClk ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN SD_MMC_HC_PRIVATE_DATA *Private, + IN UINT32 Clock + ) +{ + UINT32 Div; + UINT32 Clk; + UINT32 Timeout; + UINT16 Value = 0; + + SdMmcHcRwMmio (PciIo, SD_BAR_INDEX, SD_MMC_HC_CLOCK_CTRL, FALSE, 2, &Value); + + if (Clock == 0) + return 0; + + if (Private->ControllerVersion >= SDHCI_SPEC_300) { + // Version 3.00 Divisors must be a multiple of 2 + if (XENON_MMC_MAX_CLK <= Clock) { + Div = 1; + } else { + for (Div = 2; Div < SDHCI_MAX_DIV_SPEC_300; Div += 2) { + if ((XENON_MMC_MAX_CLK / Div) <= Clock) + break; + } + } + } else { + // Version 2.00 Divisors must be a power of 2 + for (Div = 1; Div < SDHCI_MAX_DIV_SPEC_200; Div *= 2) { + if ((XENON_MMC_MAX_CLK / Div) <= Clock) + break; + } + } + Div >>= 1; + + Clk = (Div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT; + Clk |= ((Div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN) + << SDHCI_DIVIDER_HI_SHIFT; + Clk |= SDHCI_CLOCK_INT_EN; + + SdMmcHcRwMmio (PciIo, SD_BAR_INDEX, SD_MMC_HC_CLOCK_CTRL, FALSE, 2, &Clk); + + // Wait max 20 ms + Timeout = 200; + + do { + SdMmcHcRwMmio (PciIo, SD_BAR_INDEX, SD_MMC_HC_CLOCK_CTRL, TRUE, 2, &Clk); + if (Timeout == 0) { + DEBUG((DEBUG_ERROR, "SD/MMC: Internal Clock never stabilised\n")); + return -1; + } + Timeout--; + gBS->Stall (1); + } while (!(Clk & SDHCI_CLOCK_INT_STABLE)); + + Clk |= SDHCI_CLOCK_CARD_EN; + SdMmcHcRwMmio (PciIo, SD_BAR_INDEX, SD_MMC_HC_CLOCK_CTRL, FALSE, 2, &Clk); + + return 0; +} + +STATIC +VOID +XenonPhyInit ( + IN EFI_PCI_IO_PROTOCOL *PciIo + ) +{ + UINT32 Var, Wait, Time; + UINT32 Clock = XENON_MMC_MAX_CLK; + UINT16 ClkCtrl; + + // Need to disable the clock to set EMMC_PHY_TIMING_ADJUST register + SdMmcHcRwMmio (PciIo, SD_BAR_INDEX, SD_MMC_HC_CLOCK_CTRL, TRUE, 2, &ClkCtrl); + ClkCtrl &= ~(SDHCI_CLOCK_CARD_EN | SDHCI_CLOCK_INT_EN); + SdMmcHcRwMmio (PciIo, SD_BAR_INDEX, SD_MMC_HC_CLOCK_CTRL, FALSE, 2, &ClkCtrl); + + // Enable QSP PHASE SELECT + SdMmcHcRwMmio (PciIo, SD_BAR_INDEX, EMMC_PHY_TIMING_ADJUST, TRUE, 4, &Var); + Var |= SAMPL_INV_QSP_PHASE_SELECT; + SdMmcHcRwMmio (PciIo, SD_BAR_INDEX, EMMC_PHY_TIMING_ADJUST, FALSE, 4, &Var); + + // Enable internal clock + SdMmcHcRwMmio (PciIo, SD_BAR_INDEX, SD_MMC_HC_CLOCK_CTRL, TRUE, 2, &ClkCtrl); + ClkCtrl |= SDHCI_CLOCK_INT_EN; + SdMmcHcRwMmio (PciIo, SD_BAR_INDEX, SD_MMC_HC_CLOCK_CTRL, FALSE, 2, &ClkCtrl); + + // + // Poll for host MMC PHY clock init to be stable + // Wait up to 10ms + // + Time = 100; + while (Time--) { + SdMmcHcRwMmio (PciIo, SD_BAR_INDEX, EMMC_PHY_TIMING_ADJUST, TRUE, 4, &Var); + if (Var & SDHCI_CLOCK_INT_STABLE) + break; + + gBS->Stall (1); + } + if (Time <= 0) { + DEBUG((DEBUG_ERROR, "Failed to enable MMC internal clock in Time\n")); + return; + } + + // Enable bus clock + SdMmcHcRwMmio (PciIo, SD_BAR_INDEX, SD_MMC_HC_CLOCK_CTRL, TRUE, 2, &ClkCtrl); + ClkCtrl |= SDHCI_CLOCK_CARD_EN; + SdMmcHcRwMmio (PciIo, SD_BAR_INDEX, SD_MMC_HC_CLOCK_CTRL, FALSE, 2, &ClkCtrl); + + // Delay 200us to Wait for the completion of bus clock + gBS->Stall (200); + + // Init PHY + SdMmcHcRwMmio (PciIo, SD_BAR_INDEX, EMMC_PHY_TIMING_ADJUST, TRUE, 4, &Var); + Var |= PHY_INITIALIZAION; + SdMmcHcRwMmio (PciIo, SD_BAR_INDEX, EMMC_PHY_TIMING_ADJUST, FALSE, 4, &Var); + + // Add duration of FC_SYNC_RST + Wait = ((Var >> FC_SYNC_RST_DURATION_SHIFT) & FC_SYNC_RST_DURATION_MASK); + // Add interval between FC_SYNC_EN and FC_SYNC_RST + Wait += ((Var >> FC_SYNC_RST_EN_DURATION_SHIFT) & FC_SYNC_RST_EN_DURATION_MASK); + // Add duration of asserting FC_SYNC_EN + Wait += ((Var >> FC_SYNC_EN_DURATION_SHIFT) & FC_SYNC_EN_DURATION_MASK); + // Add duration of Waiting for PHY + Wait += ((Var >> WAIT_CYCLE_BEFORE_USING_SHIFT) & WAIT_CYCLE_BEFORE_USING_MASK); + // + // According to Moyang, 4 addtional bus clock and 4 AXI bus clock are required + // left shift 20 bits + Wait += 8; + Wait <<= 20; + + if (Clock == 0) + // Use the possibly slowest bus frequency value + Clock = 100000; + // Get the Wait Time in unit of ms + Wait = Wait / Clock; + Wait++; + + // + // Poll for host eMMC PHY init to complete + // Wait up to 10ms + // + Time = 100; + while (Time--) { + Var = SdMmcHcRwMmio (PciIo, SD_BAR_INDEX, EMMC_PHY_TIMING_ADJUST, TRUE, 4, &Var); + Var &= PHY_INITIALIZAION; + if (!Var) + break; + + // Wait for host eMMC PHY init to complete + gBS->Stall (1); + } + if (Time <= 0) { + DEBUG((DEBUG_ERROR, "Sd/Mmc: Failed to init MMC PHY in Time\n")); + return; + } + + return; +} + +VOID +XenonSetPhy ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + UINT8 Timing + ) +{ + UINT32 Var; + + // Setup pad, set bit[30], bit[28] and bits[26:24] + SdMmcHcRwMmio (PciIo, SD_BAR_INDEX, EMMC_PHY_PAD_CONTROL, TRUE, 4, &Var); + Var |= (AUTO_RECEN_CTRL | OEN_QSN | FC_QSP_RECEN | FC_CMD_RECEN | FC_DQ_RECEN); + SdMmcHcRwMmio (PciIo, SD_BAR_INDEX, EMMC_PHY_PAD_CONTROL, FALSE, 4, &Var); + + // + // If Timing belongs to high speed, set bit[17] of + // EMMC_PHY_TIMING_ADJUST register + // + if ((Timing == MMC_TIMING_MMC_HS400) || + (Timing == MMC_TIMING_MMC_HS200) || + (Timing == MMC_TIMING_UHS_SDR50) || + (Timing == MMC_TIMING_UHS_SDR104) || + (Timing == MMC_TIMING_UHS_DDR50) || + (Timing == MMC_TIMING_UHS_SDR25)) { + SdMmcHcRwMmio(PciIo, SD_BAR_INDEX, EMMC_PHY_TIMING_ADJUST, TRUE, 4, &Var); + + Var |= OUTPUT_QSN_PHASE_SELECT; + SdMmcHcRwMmio(PciIo, SD_BAR_INDEX, EMMC_PHY_TIMING_ADJUST, FALSE, 4, &Var); + } + + SdMmcHcRwMmio(PciIo, SD_BAR_INDEX, EMMC_PHY_FUNC_CONTROL, TRUE, 4, &Var); + Var |= (DQ_DDR_MODE_MASK << DQ_DDR_MODE_SHIFT) | CMD_DDR_MODE; + SdMmcHcRwMmio(PciIo, SD_BAR_INDEX, EMMC_PHY_FUNC_CONTROL, FALSE, 4, &Var); + + if (Timing == MMC_TIMING_MMC_HS400) { + SdMmcHcRwMmio(PciIo, SD_BAR_INDEX, EMMC_PHY_FUNC_CONTROL, TRUE, 4, &Var); + Var &= ~DQ_ASYNC_MODE; + SdMmcHcRwMmio(PciIo, SD_BAR_INDEX, EMMC_PHY_FUNC_CONTROL, FALSE, 4, &Var); + + Var = LOGIC_TIMING_VALUE; + SdMmcHcRwMmio(PciIo, SD_BAR_INDEX, EMMC_LOGIC_TIMING_ADJUST, FALSE, 4, &Var); + } + + XenonPhyInit (PciIo); +} + +VOID +XenonConfigureInterrupts ( + IN EFI_PCI_IO_PROTOCOL *PciIo + ) +{ + UINT32 Var; + + // Clear interrupt status + Var = 0xFFFFFFFF; + SdMmcHcRwMmio (PciIo, SD_BAR_INDEX, SD_MMC_HC_NOR_INT_STS, FALSE, 4, &Var); + SdMmcHcRwMmio (PciIo, SD_BAR_INDEX, SD_MMC_HC_NOR_INT_STS, FALSE, 4, &Var); + + // Enable only interrupts served by the SD controller + Var = 0xFFFFFEBF; + SdMmcHcRwMmio (PciIo, SD_BAR_INDEX, SD_MMC_HC_NOR_INT_STS_EN, FALSE, 4, &Var); + + // Mask all sdhci interrupt sources + Var = 0xFFFFFEFF; + SdMmcHcRwMmio (PciIo, SD_BAR_INDEX, SD_MMC_HC_NOR_INT_SIG_EN, FALSE, 4, &Var); +} + +// Enable Parallel Transfer Mode +VOID +XenonSetParallelTransfer ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 Slot, + IN BOOLEAN Enable + ) +{ + UINT32 Var; + + SdMmcHcRwMmio(PciIo, SD_BAR_INDEX, SDHC_SYS_EXT_OP_CTRL, TRUE, 4, &Var); + if (Enable) + Var |= (0x1 << Slot); + else + Var &= ~(0x1 << Slot); + SdMmcHcRwMmio(PciIo, SD_BAR_INDEX, SDHC_SYS_EXT_OP_CTRL, FALSE, 4, &Var); +} + +VOID +XenonSetTuning ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 Slot, + IN BOOLEAN Enable + ) +{ + UINT32 Var; + + // Set the Re-Tuning Request functionality + SdMmcHcRwMmio(PciIo, SD_BAR_INDEX, SDHC_SLOT_RETUNING_REQ_CTRL, TRUE, 4, &Var); + if (Enable) + Var |= RETUNING_COMPATIBLE; + else + Var &= ~RETUNING_COMPATIBLE; + SdMmcHcRwMmio(PciIo, SD_BAR_INDEX, SDHC_SYS_EXT_OP_CTRL, FALSE, 4, &Var); + + // Set the Re-tuning Event Signal Enable + SdMmcHcRwMmio(PciIo, SD_BAR_INDEX, SDHCI_SIGNAL_ENABLE, TRUE, 4, &Var); + if (Enable) + Var |= SDHCI_RETUNE_EVT_INTSIG; + else + Var &= ~SDHCI_RETUNE_EVT_INTSIG; + SdMmcHcRwMmio(PciIo, SD_BAR_INDEX, SDHC_SYS_EXT_OP_CTRL, FALSE, 4, &Var); +} diff --git a/Drivers/Sd/XenonSdhci.h b/Drivers/Sd/XenonSdhci.h new file mode 100644 index 0000000..00c1ad9 --- /dev/null +++ b/Drivers/Sd/XenonSdhci.h @@ -0,0 +1,301 @@ +/******************************************************************************* +Copyright (C) 2016 Marvell International Ltd. + +Marvell BSD License Option + +If you received this File from Marvell, you may opt to use, redistribute and/or +modify this File under the following licensing terms. +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Marvell nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*******************************************************************************/ + +#include "../../../MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdMmcPciHcDxe.h" + +#define SD_BAR_INDEX 0 + +/* Register Offset of SD Host Controller SOCP self-defined register */ + +#define SDHC_IPID 0x0100 +#define SDHC_SYS_CFG_INFO 0x0104 +#define SLOT_TYPE_SDIO_SHIFT 24 +#define SLOT_TYPE_EMMC_MASK 0xff +#define SLOT_TYPE_EMMC_SHIFT 16 +#define SLOT_TYPE_SD_SDIO_MMC_MASK 0xff +#define SLOT_TYPE_SD_SDIO_MMC_SHIFT 8 + +#define SDHC_SYS_OP_CTRL 0x0108 +#define AUTO_CLKGATE_DISABLE_MASK (0x1<<20) +#define SDCLK_IDLEOFF_ENABLE_SHIFT 8 +#define SLOT_ENABLE_SHIFT 0 + +#define SDHC_SYS_EXT_OP_CTRL 0x010c +#define SDHC_TEST_OUT 0x0110 +#define SDHC_TESTOUT_MUXSEL 0x0114 + +#define SDHC_SLOT_EXT_INT_STATUS 0x0120 +#define SDHC_SLOT_EXT_ERR_STATUS 0x0122 +#define SDHC_SLOT_EXT_INT_STATUS_EN 0x0124 +#define SDHC_SLOT_EXT_ERR_STATUS_EN 0x0126 + +#define SDHC_SLOT_OP_STATUS_CTRL 0x0128 +#define TUNING_PROG_FIXED_DELAY_MASK 0x7ff +#define FORCE_SEL_INVERSE_CLK_SHIFT 11 + +#define SDHC_SLOT_FIFO_CTRL 0x012c + +#define SDHC_SLOT_eMMC_CTRL 0x0130 +#define ENABLE_DATA_STROBE_SHIFT 24 +#define SET_EMMC_RSTN_SHIFT 16 +#define eMMC_VCCQ_MASK 0x3 +#define eMMC_VCCQ_1_8V 0x1 +#define eMMC_VCCQ_1_2V 0x2 +#define eMMC_VCCQ_3_3V 0x3 + +#define SDHC_SLOT_OUTPUT_DLY_CTRL 0x0134 +#define SDHC_SLOT_DCM_CTRL 0x0137 + +#define SDHC_SLOT_DLL_CTRL 0x0138 +#define SELECT_DEF_DLL 0x1 + +#define SDHC_SLOT_DLL_PHASE_SEL 0x013c +#define DLL_UPDATE_STROBE 7 + +#define SDHC_SLOT_STROBE_DLY_CTRL 0x0140 +#define STROBE_DELAY_FIXED_MASK 0xffff + +#define SDHC_SLOT_RETUNING_REQ_CTRL 0x0144 +/* retuning compatible */ +#define RETUNING_COMPATIBLE 0x1 + +#define SDHC_SLOT_AUTO_RETUNING_CTRL 0x0148 +#define ENABLE_AUTO_RETUNING 0x1 + +#define SDHC_SLOT_EXT_PRESENT_STATE 0x014c +#define SDHC_SLOT_DLL_CUR_DLY_VAL 0x0150 +#define SDHC_SLOT_TUNING_CUR_DLY_VAL 0x0154 +#define SDHC_SLOT_STROBE_CUR_DLY_VAL 0x0158 +#define SDHC_SLOT_SUB_CMD_STRL 0x015c + +#define SDHC_SLOT_CQ_TASK_INFO 0x0160 + +#define SDHC_SLOT_TUNING_DEBUG_INFO 0x01f0 +#define SDHC_SLOT_DATAIN_DEBUG_INFO 0x01f4 + +#define XENON_MMC_MAX_CLK (400000000) + +#define XENON_MMC_CMD_MAX_TIMEOUT 3200 +#define XENON_MMC_CMD_DEFAULT_TIMEOUT 100 + +/* Tuning Parameter */ +#define TMR_RETUN_NO_PRESENT 0xf +#define XENON_MAX_TUN_COUNT 0xb + +#define EMMC_PHY_REG_BASE 0x170 +#define EMMC_PHY_TIMING_ADJUST EMMC_PHY_REG_BASE +#define OUTPUT_QSN_PHASE_SELECT (1 << 17) +#define SAMPL_INV_QSP_PHASE_SELECT (1 << 18) +#define SAMPL_INV_QSP_PHASE_SELECT_SHIFT 18 +#define PHY_INITIALIZAION (1 << 31) +#define WAIT_CYCLE_BEFORE_USING_MASK 0xf +#define WAIT_CYCLE_BEFORE_USING_SHIFT 12 +#define FC_SYNC_EN_DURATION_MASK 0xf +#define FC_SYNC_EN_DURATION_SHIFT 8 +#define FC_SYNC_RST_EN_DURATION_MASK 0xf +#define FC_SYNC_RST_EN_DURATION_SHIFT 4 +#define FC_SYNC_RST_DURATION_MASK 0xf +#define FC_SYNC_RST_DURATION_SHIFT 0 + +#define EMMC_PHY_FUNC_CONTROL (EMMC_PHY_REG_BASE + 0x4) +#define DQ_ASYNC_MODE (1 << 4) +#define DQ_DDR_MODE_SHIFT 8 +#define DQ_DDR_MODE_MASK 0xff +#define CMD_DDR_MODE (1 << 16) + +#define EMMC_PHY_PAD_CONTROL (EMMC_PHY_REG_BASE + 0x8) +#define REC_EN_SHIFT 24 +#define REC_EN_MASK 0xf +#define FC_DQ_RECEN (1 << 24) +#define FC_CMD_RECEN (1 << 25) +#define FC_QSP_RECEN (1 << 26) +#define FC_QSN_RECEN (1 << 27) +#define OEN_QSN (1 << 28) +#define AUTO_RECEN_CTRL (1 << 30) + +#define EMMC_PHY_PAD_CONTROL1 (EMMC_PHY_REG_BASE + 0xc) +#define EMMC_PHY_PAD_CONTROL2 (EMMC_PHY_REG_BASE + 0x10) +#define EMMC_PHY_DLL_CONTROL (EMMC_PHY_REG_BASE + 0x14) +#define DLL_DELAY_TEST_LOWER_SHIFT 8 +#define DLL_DELAY_TEST_LOWER_MASK 0xff +#define DLL_BYPASS_EN 0x1 + +#define EMMC_LOGIC_TIMING_ADJUST (EMMC_PHY_REG_BASE + 0x18) +#define EMMC_LOGIC_TIMING_ADJUST_LOW (EMMC_PHY_REG_BASE + 0x1c) + +#define LOGIC_TIMING_VALUE 0x5a54 /* Recommend by HW team */ + +/* Hyperion only have one slot 0 */ +#define XENON_MMC_SLOT_ID_HYPERION (0) + +#define MMC_TIMING_LEGACY 0 +#define MMC_TIMING_MMC_HS 1 +#define MMC_TIMING_SD_HS 2 +#define MMC_TIMING_UHS_SDR12 3 +#define MMC_TIMING_UHS_SDR25 4 +#define MMC_TIMING_UHS_SDR50 5 +#define MMC_TIMING_UHS_SDR104 6 +#define MMC_TIMING_UHS_DDR50 7 +#define MMC_TIMING_MMC_HS200 8 +#define MMC_TIMING_MMC_HS400 10 + +/* Data time out default value 0xE: TMCLK x 227 */ +#define DATA_TIMEOUT_DEF_VAL 0xe + +/* SDMA start address should allign with 0x8, align mask 0x7 */ +#define DMA_START_ADDR_ALIGN_MASK 0x7 + +#define SDHCI_RETUNE_EVT_INTSIG 0x00001000 + +/* MMC modes */ +#define XENON_MMC_MODE_EMMC 0 +#define XENON_MMC_MODE_SD_SDIO 1 + +/* MMC Voltage */ +#define MMC_VDD_165_195 0x00000080 /* VDD voltage 1.65 - 1.95 */ +#define MMC_VDD_20_21 0x00000100 /* VDD voltage 2.0 ~ 2.1 */ +#define MMC_VDD_21_22 0x00000200 /* VDD voltage 2.1 ~ 2.2 */ +#define MMC_VDD_22_23 0x00000400 /* VDD voltage 2.2 ~ 2.3 */ +#define MMC_VDD_23_24 0x00000800 /* VDD voltage 2.3 ~ 2.4 */ +#define MMC_VDD_24_25 0x00001000 /* VDD voltage 2.4 ~ 2.5 */ +#define MMC_VDD_25_26 0x00002000 /* VDD voltage 2.5 ~ 2.6 */ +#define MMC_VDD_26_27 0x00004000 /* VDD voltage 2.6 ~ 2.7 */ +#define MMC_VDD_27_28 0x00008000 /* VDD voltage 2.7 ~ 2.8 */ +#define MMC_VDD_28_29 0x00010000 /* VDD voltage 2.8 ~ 2.9 */ +#define MMC_VDD_29_30 0x00020000 /* VDD voltage 2.9 ~ 3.0 */ +#define MMC_VDD_30_31 0x00040000 /* VDD voltage 3.0 ~ 3.1 */ +#define MMC_VDD_31_32 0x00080000 /* VDD voltage 3.1 ~ 3.2 */ +#define MMC_VDD_32_33 0x00100000 /* VDD voltage 3.2 ~ 3.3 */ +#define MMC_VDD_33_34 0x00200000 /* VDD voltage 3.3 ~ 3.4 */ +#define MMC_VDD_34_35 0x00400000 /* VDD voltage 3.4 ~ 3.5 */ +#define MMC_VDD_35_36 0x00800000 /* VDD voltage 3.5 ~ 3.6 */ + +/* SDHCI FLAGS */ +#define SDHCI_POWER_ON 0x01 +#define SDHCI_POWER_180 0x0A +#define SDHCI_POWER_300 0x0C +#define SDHCI_POWER_330 0x0E + +#define SDHCI_DIVIDER_SHIFT 8 +#define SDHCI_DIVIDER_HI_SHIFT 6 +#define SDHCI_DIV_MASK 0xFF +#define SDHCI_DIV_MASK_LEN 8 +#define SDHCI_DIV_HI_MASK 0x300 +#define SDHCI_CLOCK_CARD_EN 0x0004 +#define SDHCI_CLOCK_INT_STABLE 0x0002 +#define SDHCI_CLOCK_INT_EN 0x0001 + +#define SDHCI_VENDOR_VER_MASK 0xFF00 +#define SDHCI_VENDOR_VER_SHIFT 8 +#define SDHCI_SPEC_VER_MASK 0x00FF +#define SDHCI_SPEC_VER_SHIFT 0 +#define SDHCI_SPEC_100 0 +#define SDHCI_SPEC_200 1 +#define SDHCI_SPEC_300 2 + +#define SDHCI_SIGNAL_ENABLE 0x38 + +#define SDHCI_MAX_DIV_SPEC_200 256 +#define SDHCI_MAX_DIV_SPEC_300 2046 + +VOID +XenonReadVersion ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + OUT UINT32 *ControllerVersion + ); + +VOID +XenonSetFifo ( + IN EFI_PCI_IO_PROTOCOL *PciIo + ); + +VOID +XenonSetSlot ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 Slot, + IN BOOLEAN Enable + ); + +VOID +XenonSetAcg ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN BOOLEAN Enable + ); + +VOID +XenonSetSlot ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 Slot, + IN BOOLEAN Enable + ); + +VOID +XenonSetPower ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT32 Vcc, + IN UINT32 Vccq, + IN UINT8 Mode + ); + +UINTN +XenonSetClk ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN SD_MMC_HC_PRIVATE_DATA *Private, + IN UINT32 Clock + ); + +VOID +XenonSetPhy ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + UINT8 Timing + ); + +VOID +XenonSetTuning ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 Slot, + IN BOOLEAN Enable + ); + +VOID +XenonConfigureInterrupts ( + IN EFI_PCI_IO_PROTOCOL *PciIo + ); + +VOID +XenonSetParallelTransfer ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 Slot, + IN BOOLEAN Enable + );