mirror of https://github.com/acidanthera/audk.git
1424 lines
48 KiB
C
1424 lines
48 KiB
C
/** @file
|
|
This driver is used to manage SD/MMC PCI host controllers which are compliance
|
|
with SD Host Controller Simplified Specification version 3.00 plus the 64-bit
|
|
System Addressing support in SD Host Controller Simplified Specification version
|
|
4.20.
|
|
|
|
It would expose EFI_SD_MMC_PASS_THRU_PROTOCOL for upper layer use.
|
|
|
|
Copyright (c) 2018-2019, NVIDIA CORPORATION. All rights reserved.
|
|
Copyright (c) 2015 - 2020, Intel Corporation. All rights reserved.<BR>
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
|
|
**/
|
|
|
|
#include "SdMmcPciHcDxe.h"
|
|
|
|
EDKII_SD_MMC_OVERRIDE *mOverride;
|
|
|
|
//
|
|
// Driver Global Variables
|
|
//
|
|
EFI_DRIVER_BINDING_PROTOCOL gSdMmcPciHcDriverBinding = {
|
|
SdMmcPciHcDriverBindingSupported,
|
|
SdMmcPciHcDriverBindingStart,
|
|
SdMmcPciHcDriverBindingStop,
|
|
0x10,
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
#define SLOT_INIT_TEMPLATE {0, UnknownSlot, 0, 0, 0, 0, \
|
|
{EDKII_SD_MMC_BUS_WIDTH_IGNORE,\
|
|
EDKII_SD_MMC_CLOCK_FREQ_IGNORE,\
|
|
{EDKII_SD_MMC_DRIVER_STRENGTH_IGNORE}}}
|
|
|
|
//
|
|
// 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
|
|
SLOT_INIT_TEMPLATE,
|
|
SLOT_INIT_TEMPLATE,
|
|
SLOT_INIT_TEMPLATE,
|
|
SLOT_INIT_TEMPLATE,
|
|
SLOT_INIT_TEMPLATE,
|
|
SLOT_INIT_TEMPLATE
|
|
},
|
|
{ // 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) {
|
|
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 ((DEBUG_VERBOSE, "ProcessAsyncTaskList(): Signal Event %p EFI_TIMEOUT\n", TrbEvent));
|
|
gBS->SignalEvent (TrbEvent);
|
|
return;
|
|
}
|
|
} else if ((Trb != NULL) && (Status == EFI_CRC_ERROR) && (Trb->Retries > 0)) {
|
|
Trb->Retries--;
|
|
Trb->Started = FALSE;
|
|
} else if ((Trb != NULL)) {
|
|
RemoveEntryList (Link);
|
|
Trb->Packet->TransactionStatus = Status;
|
|
TrbEvent = Trb->Event;
|
|
SdMmcFreeTrb (Trb);
|
|
DEBUG ((DEBUG_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;
|
|
EFI_TPL OldTpl;
|
|
|
|
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) {
|
|
DEBUG ((DEBUG_INFO, "SdMmcPciHcEnumerateDevice: device disconnected at slot %d of pci %p\n", Slot, Private->PciIo));
|
|
Private->Slot[Slot].MediaPresent = FALSE;
|
|
Private->Slot[Slot].Initialized = FALSE;
|
|
//
|
|
// Signal all async task events at the slot with EFI_NO_MEDIA status.
|
|
//
|
|
OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
|
|
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);
|
|
}
|
|
}
|
|
gBS->RestoreTPL (OldTpl);
|
|
//
|
|
// 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) {
|
|
DEBUG ((DEBUG_INFO, "SdMmcPciHcEnumerateDevice: device connected at slot %d of pci %p\n", Slot, Private->PciIo));
|
|
//
|
|
// Reset the specified slot of the SD/MMC Pci Host Controller
|
|
//
|
|
Status = SdMmcHcReset (Private, Slot);
|
|
if (EFI_ERROR (Status)) {
|
|
continue;
|
|
}
|
|
//
|
|
// Reinitialize slot and restart identification process for the new attached device
|
|
//
|
|
Status = SdMmcHcInitHost (Private, Slot);
|
|
if (EFI_ERROR (Status)) {
|
|
continue;
|
|
}
|
|
|
|
Private->Slot[Slot].MediaPresent = TRUE;
|
|
Private->Slot[Slot].Initialized = 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;
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// This card doesn't get initialized correctly.
|
|
//
|
|
if (Index == RoutineNum) {
|
|
Private->Slot[Slot].Initialized = FALSE;
|
|
}
|
|
|
|
//
|
|
// 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 SlotNum;
|
|
UINT8 FirstBar;
|
|
UINT8 Slot;
|
|
UINT8 Index;
|
|
CARD_TYPE_DETECT_ROUTINE *Routine;
|
|
UINT32 RoutineNum;
|
|
BOOLEAN MediaPresent;
|
|
BOOLEAN Support64BitDma;
|
|
|
|
DEBUG ((DEBUG_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);
|
|
|
|
//
|
|
// Get SD/MMC Pci Host Controller Slot info
|
|
//
|
|
Status = SdMmcHcGetSlotInfo (PciIo, &FirstBar, &SlotNum);
|
|
if (EFI_ERROR (Status)) {
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// Attempt to locate the singleton instance of the SD/MMC override protocol,
|
|
// which implements platform specific workarounds for non-standard SDHCI
|
|
// implementations.
|
|
//
|
|
if (mOverride == NULL) {
|
|
Status = gBS->LocateProtocol (&gEdkiiSdMmcOverrideProtocolGuid, NULL,
|
|
(VOID **)&mOverride);
|
|
if (!EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_INFO, "%a: found SD/MMC override protocol\n",
|
|
__FUNCTION__));
|
|
}
|
|
}
|
|
|
|
Support64BitDma = TRUE;
|
|
for (Slot = FirstBar; Slot < (FirstBar + SlotNum); Slot++) {
|
|
Private->Slot[Slot].Enable = TRUE;
|
|
//
|
|
// Get SD/MMC Pci Host Controller Version
|
|
//
|
|
Status = SdMmcHcGetControllerVersion (PciIo, Slot, &Private->ControllerVersion[Slot]);
|
|
if (EFI_ERROR (Status)) {
|
|
continue;
|
|
}
|
|
|
|
Status = SdMmcHcGetCapability (PciIo, Slot, &Private->Capability[Slot]);
|
|
if (EFI_ERROR (Status)) {
|
|
continue;
|
|
}
|
|
|
|
Private->BaseClkFreq[Slot] = Private->Capability[Slot].BaseClkFreq;
|
|
|
|
if (mOverride != NULL) {
|
|
if (mOverride->Capability != NULL) {
|
|
Status = mOverride->Capability (
|
|
Controller,
|
|
Slot,
|
|
&Private->Capability[Slot],
|
|
&Private->BaseClkFreq[Slot]
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_WARN, "%a: Failed to override capability - %r\n",
|
|
__FUNCTION__, Status));
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (mOverride->NotifyPhase != NULL) {
|
|
Status = mOverride->NotifyPhase (
|
|
Controller,
|
|
Slot,
|
|
EdkiiSdMmcGetOperatingParam,
|
|
(VOID*)&Private->Slot[Slot].OperatingParameters
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_WARN, "%a: Failed to get operating parameters, using defaults\n", __FUNCTION__));
|
|
}
|
|
}
|
|
}
|
|
|
|
DumpCapabilityReg (Slot, &Private->Capability[Slot]);
|
|
DEBUG ((
|
|
DEBUG_INFO,
|
|
"Slot[%d] Base Clock Frequency: %dMHz\n",
|
|
Slot,
|
|
Private->BaseClkFreq[Slot]
|
|
));
|
|
|
|
//
|
|
// If any of the slots does not support 64b system bus
|
|
// do not enable 64b DMA in the PCI layer.
|
|
//
|
|
if ((Private->ControllerVersion[Slot] == SD_MMC_HC_CTRL_VER_300 &&
|
|
Private->Capability[Slot].SysBus64V3 == 0) ||
|
|
(Private->ControllerVersion[Slot] == SD_MMC_HC_CTRL_VER_400 &&
|
|
Private->Capability[Slot].SysBus64V3 == 0) ||
|
|
(Private->ControllerVersion[Slot] >= SD_MMC_HC_CTRL_VER_410 &&
|
|
Private->Capability[Slot].SysBus64V4 == 0)) {
|
|
Support64BitDma = FALSE;
|
|
}
|
|
|
|
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 ((DEBUG_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 (Private, Slot);
|
|
if (EFI_ERROR (Status)) {
|
|
continue;
|
|
}
|
|
//
|
|
// Check whether there is a SD/MMC card attached
|
|
//
|
|
if (Private->Slot[Slot].SlotType == RemovableSlot) {
|
|
Status = SdMmcHcCardDetect (PciIo, Slot, &MediaPresent);
|
|
if (EFI_ERROR (Status) && (Status != EFI_MEDIA_CHANGED)) {
|
|
continue;
|
|
} else if (!MediaPresent) {
|
|
DEBUG ((
|
|
DEBUG_INFO,
|
|
"SdMmcHcCardDetect: No device attached in Slot[%d]!!!\n",
|
|
Slot
|
|
));
|
|
continue;
|
|
}
|
|
}
|
|
|
|
Status = SdMmcHcInitHost (Private, Slot);
|
|
if (EFI_ERROR (Status)) {
|
|
continue;
|
|
}
|
|
|
|
Private->Slot[Slot].MediaPresent = TRUE;
|
|
Private->Slot[Slot].Initialized = 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;
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// This card doesn't get initialized correctly.
|
|
//
|
|
if (Index == RoutineNum) {
|
|
Private->Slot[Slot].Initialized = FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Enable 64-bit DMA support in the PCI layer if this controller
|
|
// supports it.
|
|
//
|
|
if (Support64BitDma) {
|
|
Status = PciIo->Attributes (
|
|
PciIo,
|
|
EfiPciIoAttributeOperationEnable,
|
|
EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE,
|
|
NULL
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_WARN, "SdMmcPciHcDriverBindingStart: failed to enable 64-bit DMA (%r)\n", Status));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Start the asynchronous I/O monitor
|
|
//
|
|
Status = gBS->CreateEvent (
|
|
EVT_TIMER | EVT_NOTIFY_SIGNAL,
|
|
TPL_NOTIFY,
|
|
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 ((DEBUG_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 ((DEBUG_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 ((DEBUG_INFO, "SdMmcPciHcDriverBindingStop: End with %r\n", Status));
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Execute TRB synchronously.
|
|
|
|
@param[in] Private Pointer to driver private data.
|
|
@param[in] Trb Pointer to TRB to execute.
|
|
|
|
@retval EFI_SUCCESS TRB executed successfully.
|
|
@retval Other TRB failed.
|
|
**/
|
|
EFI_STATUS
|
|
SdMmcPassThruExecSyncTrb (
|
|
IN SD_MMC_HC_PRIVATE_DATA *Private,
|
|
IN SD_MMC_HC_TRB *Trb
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_TPL OldTpl;
|
|
|
|
//
|
|
// Wait async I/O list is empty before execute sync I/O operation.
|
|
//
|
|
while (TRUE) {
|
|
OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
|
|
if (IsListEmpty (&Private->Queue)) {
|
|
gBS->RestoreTPL (OldTpl);
|
|
break;
|
|
}
|
|
gBS->RestoreTPL (OldTpl);
|
|
}
|
|
|
|
while (Trb->Retries) {
|
|
Status = SdMmcWaitTrbEnv (Private, Trb);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = SdMmcExecTrb (Private, Trb);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = SdMmcWaitTrbResult (Private, Trb);
|
|
if (Status == EFI_CRC_ERROR) {
|
|
Trb->Retries--;
|
|
} else {
|
|
return 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;
|
|
|
|
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;
|
|
}
|
|
|
|
if (!Private->Slot[Slot].Initialized) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
Status = SdMmcPassThruExecSyncTrb (Private, Trb);
|
|
|
|
SdMmcFreeTrb (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;
|
|
}
|
|
|
|
if (!Private->Slot[Slot].Initialized) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
//
|
|
// Free all async I/O requests in the queue
|
|
//
|
|
OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
|
|
|
|
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;
|
|
}
|
|
|