/*++ Copyright (c) 2015 Minoca Corp. All rights reserved. Module Name: sdrk32.c Abstract: This module implements the SD/MMC driver for Rk32xx SoCs. The Rockchip SD controller is based on the Synopsis DesignWare controller. Author: Chris Stevens 29-Jul-2015 Environment: Kernel --*/ // // ------------------------------------------------------------------- Includes // #include #include #include "sdrk32.h" // // --------------------------------------------------------------------- Macros // // // These macros read and write SD controller registers. // #define SD_DWC_READ_REGISTER(_Device, _Register) \ HlReadRegister32((_Device)->ControllerBase + (_Register)) #define SD_DWC_WRITE_REGISTER(_Device, _Register, _Value) \ HlWriteRegister32((_Device)->ControllerBase + (_Register), (_Value)) #define SD_RK32_READ_CRU(_Device, _Register) \ HlReadRegister32((_Device)->Cru + (_Register)) #define SD_RK32_WRITE_CRU(_Device, _Register, _Value) \ HlWriteRegister32((_Device)->Cru + (_Register), (_Value)) // // ---------------------------------------------------------------- Definitions // // // ------------------------------------------------------ Data Type Definitions // // // ----------------------------------------------- Internal Function Prototypes // KSTATUS SdRk32AddDevice ( PVOID Driver, PSTR DeviceId, PSTR ClassId, PSTR CompatibleIds, PVOID DeviceToken ); VOID SdRk32DispatchStateChange ( PIRP Irp, PVOID DeviceContext, PVOID IrpContext ); VOID SdRk32DispatchOpen ( PIRP Irp, PVOID DeviceContext, PVOID IrpContext ); VOID SdRk32DispatchClose ( PIRP Irp, PVOID DeviceContext, PVOID IrpContext ); VOID SdRk32DispatchIo ( PIRP Irp, PVOID DeviceContext, PVOID IrpContext ); VOID SdRk32DispatchSystemControl ( PIRP Irp, PVOID DeviceContext, PVOID IrpContext ); VOID SdRk32ParentDispatchStateChange ( PIRP Irp, PSD_RK32_CONTEXT Context, PVOID IrpContext ); VOID SdRk32ChildDispatchStateChange ( PIRP Irp, PSD_RK32_CHILD Child, PVOID IrpContext ); KSTATUS SdRk32ParentProcessResourceRequirements ( PIRP Irp, PSD_RK32_CONTEXT Device ); KSTATUS SdRk32ParentStartDevice ( PIRP Irp, PSD_RK32_CONTEXT Device ); KSTATUS SdRk32ParentQueryChildren ( PIRP Irp, PSD_RK32_CONTEXT Device ); KSTATUS SdRk32HardResetController ( PSD_RK32_CONTEXT Device ); KSTATUS SdRk32InitializeVendorResource ( PSD_RK32_CONTEXT Device, PRESOURCE_ALLOCATION Resource ); INTERRUPT_STATUS SdRk32InterruptService ( PVOID Context ); INTERRUPT_STATUS SdRk32InterruptServiceDispatch ( PVOID Context ); INTERRUPT_STATUS SdRk32CardInterruptWorker ( PVOID Context ); VOID SdRk32DmaCompletion ( PSD_CONTROLLER Controller, PVOID Context, UINTN BytesTransferred, KSTATUS Status ); PSD_RK32_CHILD SdRk32pCreateChild ( PSD_RK32_CONTEXT Device ); VOID SdRk32pDestroyChild ( PSD_RK32_CHILD Child ); VOID SdRk32pChildAddReference ( PSD_RK32_CHILD Child ); VOID SdRk32pChildReleaseReference ( PSD_RK32_CHILD Child ); KSTATUS SdRk32ChildBlockIoReset ( PVOID DiskToken ); KSTATUS SdRk32ChildBlockIoRead ( PVOID DiskToken, PIO_BUFFER IoBuffer, ULONGLONG BlockAddress, UINTN BlockCount, PUINTN BlocksCompleted ); KSTATUS SdRk32ChildBlockIoWrite ( PVOID DiskToken, PIO_BUFFER IoBuffer, ULONGLONG BlockAddress, UINTN BlockCount, PUINTN BlocksCompleted ); KSTATUS SdRk32PerformIoPolled ( PIRP_READ_WRITE IrpReadWrite, PSD_RK32_CHILD Child, BOOL Write, BOOL LockRequired ); KSTATUS SdRk32InitializeDma ( PSD_RK32_CONTEXT Device ); VOID SdRk32BlockIoDma ( PSD_RK32_CONTEXT Device, ULONGLONG BlockOffset, UINTN BlockCount, PIO_BUFFER IoBuffer, UINTN IoBufferOffset, BOOL Write, PSD_IO_COMPLETION_ROUTINE CompletionRoutine, PVOID CompletionContext ); KSTATUS SdRk32InitializeController ( PSD_CONTROLLER Controller, PVOID Context, ULONG Phase ); KSTATUS SdRk32ResetController ( PSD_CONTROLLER Controller, PVOID Context, ULONG Flags ); KSTATUS SdRk32SendCommand ( PSD_CONTROLLER Controller, PVOID Context, PSD_COMMAND Command ); KSTATUS SdRk32GetSetBusWidth ( PSD_CONTROLLER Controller, PVOID Context, BOOL Set ); KSTATUS SdRk32GetSetClockSpeed ( PSD_CONTROLLER Controller, PVOID Context, BOOL Set ); KSTATUS SdRk32GetSetVoltage ( PSD_CONTROLLER Controller, PVOID Context, BOOL Set ); KSTATUS SdRk32ReadData ( PSD_CONTROLLER Controller, PVOID Context, PVOID Data, ULONG Size ); KSTATUS SdRk32WriteData ( PSD_CONTROLLER Controller, PVOID Context, PVOID Data, ULONG Size ); KSTATUS SdRk32SetClockSpeed ( PSD_RK32_CONTEXT Device, SD_CLOCK_SPEED ClockSpeed ); VOID SdRk32SetDmaInterrupts ( PSD_CONTROLLER Controller, PSD_RK32_CONTEXT Device, BOOL Enable, ULONG BufferSize ); KSTATUS SdRk32UpdateClock ( PSD_RK32_CONTEXT Device ); VOID SdRk32Rk808InterfaceNotificationCallback ( PVOID Context, PDEVICE Device, PVOID InterfaceBuffer, ULONG InterfaceBufferSize, BOOL Arrival ); KSTATUS SdRk32SetRegulatorVoltage ( PSD_RK32_CONTEXT Device, BOOL LowVoltage ); // // -------------------------------------------------------------------- Globals // PDRIVER SdRk32Driver = NULL; UUID SdRk32DiskInterfaceUuid = UUID_DISK_INTERFACE; UUID SdRk32VendorResourceUuid = SD_RK32_VENDOR_RESOURCE_UUID; UUID SdRk32Rk808InterfaceUuid = UUID_RK808_INTERFACE; DISK_INTERFACE SdRk32DiskInterfaceTemplate = { DISK_INTERFACE_VERSION, NULL, 0, 0, NULL, SdRk32ChildBlockIoReset, SdRk32ChildBlockIoRead, SdRk32ChildBlockIoWrite }; SD_FUNCTION_TABLE SdRk32FunctionTable = { SdRk32InitializeController, SdRk32ResetController, SdRk32SendCommand, SdRk32GetSetBusWidth, SdRk32GetSetClockSpeed, SdRk32GetSetVoltage, NULL, NULL, NULL, NULL }; // // ------------------------------------------------------------------ Functions // KSTATUS DriverEntry ( PDRIVER Driver ) /*++ Routine Description: This routine is the entry point for the SD/MMC driver. It registers its other dispatch functions, and performs driver-wide initialization. Arguments: Driver - Supplies a pointer to the driver object. Return Value: STATUS_SUCCESS on success. Failure code on error. --*/ { DRIVER_FUNCTION_TABLE FunctionTable; KSTATUS Status; SdRk32Driver = Driver; RtlZeroMemory(&FunctionTable, sizeof(DRIVER_FUNCTION_TABLE)); FunctionTable.Version = DRIVER_FUNCTION_TABLE_VERSION; FunctionTable.AddDevice = SdRk32AddDevice; FunctionTable.DispatchStateChange = SdRk32DispatchStateChange; FunctionTable.DispatchOpen = SdRk32DispatchOpen; FunctionTable.DispatchClose = SdRk32DispatchClose; FunctionTable.DispatchIo = SdRk32DispatchIo; FunctionTable.DispatchSystemControl = SdRk32DispatchSystemControl; Status = IoRegisterDriverFunctions(Driver, &FunctionTable); return Status; } KSTATUS SdRk32AddDevice ( PVOID Driver, PSTR DeviceId, PSTR ClassId, PSTR CompatibleIds, PVOID DeviceToken ) /*++ Routine Description: This routine is called when a device is detected for which the SD/MMC driver acts as the function driver. The driver will attach itself to the stack. Arguments: Driver - Supplies a pointer to the driver being called. DeviceId - Supplies a pointer to a string with the device ID. ClassId - Supplies a pointer to a string containing the device's class ID. CompatibleIds - Supplies a pointer to a string containing device IDs that would be compatible with this device. DeviceToken - Supplies an opaque token that the driver can use to identify the device in the system. This token should be used when attaching to the stack. Return Value: STATUS_SUCCESS on success. Failure code if the driver was unsuccessful in attaching itself. --*/ { PSD_RK32_CONTEXT Context; KSTATUS Status; // // Allocate non-paged pool because this device could be the paging device. // Context = MmAllocateNonPagedPool(sizeof(SD_RK32_CONTEXT), SD_ALLOCATION_TAG); if (Context == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto AddDeviceEnd; } RtlZeroMemory(Context, sizeof(SD_RK32_CONTEXT)); Context->Type = SdRk32Parent; Context->InterruptVector = -1ULL; Context->CardInterruptVector = -1ULL; Context->InterruptHandle = INVALID_HANDLE; Context->CardInterruptHandle = INVALID_HANDLE; Context->OsDevice = DeviceToken; KeInitializeSpinLock(&(Context->DpcLock)); Context->Lock = KeCreateQueuedLock(); if (Context->Lock == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto AddDeviceEnd; } Status = IoAttachDriverToDevice(Driver, DeviceToken, Context); if (!KSUCCESS(Status)) { goto AddDeviceEnd; } AddDeviceEnd: if (!KSUCCESS(Status)) { if (Context != NULL) { MmFreeNonPagedPool(Context); } } return Status; } VOID SdRk32DispatchStateChange ( PIRP Irp, PVOID DeviceContext, PVOID IrpContext ) /*++ Routine Description: This routine handles State Change IRPs. Arguments: Irp - Supplies a pointer to the I/O request packet. DeviceContext - Supplies the context pointer supplied by the driver when it attached itself to the driver stack. Presumably this pointer contains driver-specific device context. IrpContext - Supplies the context pointer supplied by the driver when the IRP was created. Return Value: None. --*/ { PSD_RK32_CONTEXT Context; ASSERT(Irp->MajorCode == IrpMajorStateChange); Context = DeviceContext; switch (Context->Type) { case SdRk32Parent: SdRk32ParentDispatchStateChange(Irp, Context, IrpContext); break; case SdRk32Child: SdRk32ChildDispatchStateChange(Irp, (PSD_RK32_CHILD)Context, IrpContext); break; default: ASSERT(FALSE); break; } return; } VOID SdRk32DispatchOpen ( PIRP Irp, PVOID DeviceContext, PVOID IrpContext ) /*++ Routine Description: This routine handles Open IRPs. Arguments: Irp - Supplies a pointer to the I/O request packet. DeviceContext - Supplies the context pointer supplied by the driver when it attached itself to the driver stack. Presumably this pointer contains driver-specific device context. IrpContext - Supplies the context pointer supplied by the driver when the IRP was created. Return Value: None. --*/ { PSD_RK32_CHILD Child; Child = DeviceContext; // // Only the child can be opened or closed. // if (Child->Type != SdRk32Child) { return; } SdRk32pChildAddReference(Child); IoCompleteIrp(SdRk32Driver, Irp, STATUS_SUCCESS); return; } VOID SdRk32DispatchClose ( PIRP Irp, PVOID DeviceContext, PVOID IrpContext ) /*++ Routine Description: This routine handles Close IRPs. Arguments: Irp - Supplies a pointer to the I/O request packet. DeviceContext - Supplies the context pointer supplied by the driver when it attached itself to the driver stack. Presumably this pointer contains driver-specific device context. IrpContext - Supplies the context pointer supplied by the driver when the IRP was created. Return Value: None. --*/ { PSD_RK32_CHILD Child; Child = DeviceContext; if (Child->Type != SdRk32Child) { return; } SdRk32pChildReleaseReference(Child); IoCompleteIrp(SdRk32Driver, Irp, STATUS_SUCCESS); return; } VOID SdRk32DispatchIo ( PIRP Irp, PVOID DeviceContext, PVOID IrpContext ) /*++ Routine Description: This routine handles I/O IRPs. Arguments: Irp - Supplies a pointer to the I/O request packet. DeviceContext - Supplies the context pointer supplied by the driver when it attached itself to the driver stack. Presumably this pointer contains driver-specific device context. IrpContext - Supplies the context pointer supplied by the driver when the IRP was created. Return Value: None. --*/ { UINTN BlockCount; ULONGLONG BlockOffset; UINTN BytesToComplete; PSD_RK32_CHILD Child; BOOL CompleteIrp; PSD_CONTROLLER Controller; IO_OFFSET IoOffset; ULONG IrpReadWriteFlags; KSTATUS IrpStatus; KSTATUS Status; ULONG Value; BOOL Write; ASSERT(KeGetRunLevel() == RunLevelLow); Child = DeviceContext; Controller = Child->Controller; if (Child->Type != SdRk32Child) { ASSERT(FALSE); return; } CompleteIrp = TRUE; Write = FALSE; if (Irp->MinorCode == IrpMinorIoWrite) { Write = TRUE; } // // Polled I/O is shared by a few code paths and prepares the IRP for I/O // further down the stack. It should also only be hit in the down direction // path as it always completes the IRP. // if ((Child->Flags & SD_RK32_CHILD_FLAG_DMA_SUPPORTED) == 0) { ASSERT(Irp->Direction == IrpDown); CompleteIrp = TRUE; Status = SdRk32PerformIoPolled(&(Irp->U.ReadWrite), Child, Write, TRUE); goto DispatchIoEnd; } // // Set the IRP read/write flags for the preparation and completion steps. // IrpReadWriteFlags = IRP_READ_WRITE_FLAG_DMA; if (Write != FALSE) { IrpReadWriteFlags |= IRP_READ_WRITE_FLAG_WRITE; } if (Irp->Direction == IrpDown) { Controller->Try = 0; } // // If the IRP is on the way up, then clean up after the DMA as this IRP is // still sitting in the channel. An IRP going up is already complete. // if (Irp->Direction == IrpUp) { ASSERT(Irp == Child->Irp); // // Disable DMA mode. // Value = SD_DWC_READ_REGISTER(Child->Parent, SdDwcControl); Value &= ~SD_DWC_CONTROL_USE_INTERNAL_DMAC; SD_DWC_WRITE_REGISTER(Child->Parent, SdDwcControl, Value); // // If the IO went badly, try to recover and make another attempt. // IrpStatus = IoGetIrpStatus(Irp); if (!KSUCCESS(IrpStatus)) { Status = SdErrorRecovery(Controller); if (!KSUCCESS(Status)) { IrpStatus = Status; IoUpdateIrpStatus(Irp, IrpStatus); } // // Do not make further attempts if the media is gone or enough // attempts have been made. // if (((Controller->Flags & SD_CONTROLLER_FLAG_MEDIA_CHANGED) != 0) || ((Controller->Flags & SD_CONTROLLER_FLAG_MEDIA_PRESENT) == 0) || (Controller->Try >= SD_MAX_IO_RETRIES)) { IrpStatus = STATUS_SUCCESS; } else { Controller->Try += 1; } } // // Release the hold on the controller and complete any buffer // operations related to the completed transfer. // Child->Irp = NULL; KeReleaseQueuedLock(Child->ControllerLock); Status = IoCompleteReadWriteIrp(&(Irp->U.ReadWrite), IrpReadWriteFlags); if (!KSUCCESS(Status)) { IoUpdateIrpStatus(Irp, Status); } // // Potentially return the completed IRP. // if (KSUCCESS(IrpStatus)) { CompleteIrp = FALSE; goto DispatchIoEnd; } } // // Start the DMA on the way down. // BytesToComplete = Irp->U.ReadWrite.IoSizeInBytes; IoOffset = Irp->U.ReadWrite.IoOffset; Irp->U.ReadWrite.IoBytesCompleted = 0; ASSERT(Irp->U.ReadWrite.IoBuffer != NULL); ASSERT((Child->BlockCount != 0) && (Child->BlockShift != 0)); ASSERT(IS_ALIGNED(IoOffset, 1 << Child->BlockShift) != FALSE); ASSERT(IS_ALIGNED(BytesToComplete, 1 << Child->BlockShift) != FALSE); // // Before acquiring the controller's lock and starting the DMA, prepare // the I/O context for SD (i.e. it must use physical addresses that // are less than 4GB and be sector size aligned). // Status = IoPrepareReadWriteIrp(&(Irp->U.ReadWrite), 1 << Child->BlockShift, 0, MAX_ULONG, IrpReadWriteFlags); if (!KSUCCESS(Status)) { goto DispatchIoEnd; } // // Lock the controller to serialize access to the hardware. // KeAcquireQueuedLock(Child->ControllerLock); if (((Controller->Flags & SD_CONTROLLER_FLAG_MEDIA_PRESENT) == 0) || ((Controller->Flags & SD_CONTROLLER_FLAG_MEDIA_CHANGED) != 0)) { Status = STATUS_NO_MEDIA; if ((Controller->Flags & SD_CONTROLLER_FLAG_MEDIA_CHANGED) != 0) { Status = STATUS_MEDIA_CHANGED; } KeReleaseQueuedLock(Child->ControllerLock); IoCompleteReadWriteIrp(&(Irp->U.ReadWrite), IrpReadWriteFlags); goto DispatchIoEnd; } // // If it's DMA, just send it on through. // Irp->U.ReadWrite.NewIoOffset = IoOffset; Child->Irp = Irp; BlockOffset = IoOffset >> Child->BlockShift; BlockCount = BytesToComplete >> Child->BlockShift; CompleteIrp = FALSE; IoPendIrp(SdRk32Driver, Irp); // // Set the controller into DMA mode. // Value = SD_DWC_READ_REGISTER(Child->Parent, SdDwcControl); if ((Value & SD_DWC_CONTROL_USE_INTERNAL_DMAC) == 0) { Value |= SD_DWC_CONTROL_USE_INTERNAL_DMAC; SD_DWC_WRITE_REGISTER(Child->Parent, SdDwcControl, Value); } // // Make sure the system isn't trying to do I/O off the end of the // disk. // ASSERT(BlockOffset < Child->BlockCount); ASSERT(BlockCount >= 1); // // If it's a multiblock command, send CMD23 first if possible. // ASSERT((Controller->IoCompletionRoutine == NULL) && (Controller->IoCompletionContext == NULL) && (Controller->IoRequestSize == 0)); Controller->SendStop = FALSE; if (BlockCount > 1) { Controller->IoCompletionRoutine = SdRk32DmaCompletion; Controller->IoCompletionContext = Child; Status = SdSendBlockCount(Controller, BlockCount, Write, TRUE); if (KSUCCESS(Status)) { goto DispatchIoEnd; } else { Controller->SendStop = TRUE; Controller->IoCompletionRoutine = NULL; Controller->IoCompletionContext = NULL; if (Status == STATUS_NOT_SUPPORTED) { Status = STATUS_SUCCESS; } else { CompleteIrp = TRUE; KeReleaseQueuedLock(Child->ControllerLock); goto DispatchIoEnd; } } } SdRk32BlockIoDma(Child->Parent, BlockOffset, BlockCount, Irp->U.ReadWrite.IoBuffer, 0, Write, SdRk32DmaCompletion, Child); // // DMA transfers are self perpetuating, so after kicking off this // first transfer, return. This returns with the lock held because // I/O is still in progress. // ASSERT(KeIsQueuedLockHeld(Child->ControllerLock) != FALSE); ASSERT(CompleteIrp == FALSE); DispatchIoEnd: if (CompleteIrp != FALSE) { IoCompleteIrp(SdRk32Driver, Irp, Status); } return; } VOID SdRk32DispatchSystemControl ( PIRP Irp, PVOID DeviceContext, PVOID IrpContext ) /*++ Routine Description: This routine handles System Control IRPs. Arguments: Irp - Supplies a pointer to the I/O request packet. DeviceContext - Supplies the context pointer supplied by the driver when it attached itself to the driver stack. Presumably this pointer contains driver-specific device context. IrpContext - Supplies the context pointer supplied by the driver when the IRP was created. Return Value: None. --*/ { PSD_RK32_CHILD Child; PVOID Context; PSYSTEM_CONTROL_FILE_OPERATION FileOperation; PSYSTEM_CONTROL_LOOKUP Lookup; PFILE_PROPERTIES Properties; ULONGLONG PropertiesFileSize; KSTATUS Status; Context = Irp->U.SystemControl.SystemContext; Child = DeviceContext; // // Only child devices are supported. // if (Child->Type != SdRk32Child) { ASSERT(Child->Type == SdRk32Parent); return; } switch (Irp->MinorCode) { case IrpMinorSystemControlLookup: Lookup = (PSYSTEM_CONTROL_LOOKUP)Context; Status = STATUS_PATH_NOT_FOUND; if (Lookup->Root != FALSE) { // // Enable opening of the root as a single file. // Properties = &(Lookup->Properties); Properties->FileId = 0; Properties->Type = IoObjectBlockDevice; Properties->HardLinkCount = 1; Properties->BlockCount = Child->BlockCount; Properties->BlockSize = 1 << Child->BlockShift; WRITE_INT64_SYNC(&(Properties->FileSize), Child->BlockCount << Child->BlockShift); Status = STATUS_SUCCESS; } IoCompleteIrp(SdRk32Driver, Irp, Status); break; // // Writes to the disk's properties are not allowed. Fail if the data // has changed. // case IrpMinorSystemControlWriteFileProperties: FileOperation = (PSYSTEM_CONTROL_FILE_OPERATION)Context; Properties = FileOperation->FileProperties; READ_INT64_SYNC(&(Properties->FileSize), &PropertiesFileSize); if ((Properties->FileId != 0) || (Properties->Type != IoObjectBlockDevice) || (Properties->HardLinkCount != 1) || (Properties->BlockSize != (1 << Child->BlockShift)) || (Properties->BlockCount != Child->BlockCount) || (PropertiesFileSize != (Child->BlockCount << Child->BlockShift))) { Status = STATUS_NOT_SUPPORTED; } else { Status = STATUS_SUCCESS; } IoCompleteIrp(SdRk32Driver, Irp, Status); break; // // Do not support hard disk device truncation. // case IrpMinorSystemControlTruncate: IoCompleteIrp(SdRk32Driver, Irp, STATUS_NOT_SUPPORTED); break; // // Gather and return device information. // case IrpMinorSystemControlDeviceInformation: break; case IrpMinorSystemControlSynchronize: IoCompleteIrp(SdRk32Driver, Irp, STATUS_SUCCESS); break; // // Ignore everything unrecognized. // default: ASSERT(FALSE); break; } return; } VOID SdRk32ParentDispatchStateChange ( PIRP Irp, PSD_RK32_CONTEXT Context, PVOID IrpContext ) /*++ Routine Description: This routine handles State Change IRPs for a parent device. Arguments: Irp - Supplies a pointer to the I/O request packet. Context - Supplies a pointer to the controller information. IrpContext - Supplies the context pointer supplied by the driver when the IRP was created. Return Value: None. --*/ { KSTATUS Status; if (Irp->Direction == IrpUp) { if (!KSUCCESS(IoGetIrpStatus(Irp))) { return; } switch (Irp->MinorCode) { case IrpMinorQueryResources: Status = SdRk32ParentProcessResourceRequirements(Irp, Context); if (!KSUCCESS(Status)) { IoCompleteIrp(SdRk32Driver, Irp, Status); } break; case IrpMinorStartDevice: Status = SdRk32ParentStartDevice(Irp, Context); if (!KSUCCESS(Status)) { IoCompleteIrp(SdRk32Driver, Irp, Status); } break; case IrpMinorQueryChildren: Status = SdRk32ParentQueryChildren(Irp, Context); if (!KSUCCESS(Status)) { IoCompleteIrp(SdRk32Driver, Irp, Status); } break; default: break; } } return; } VOID SdRk32ChildDispatchStateChange ( PIRP Irp, PSD_RK32_CHILD Child, PVOID IrpContext ) /*++ Routine Description: This routine handles State Change IRPs for a parent device. Arguments: Irp - Supplies a pointer to the I/O request packet. Child - Supplies a pointer to the child device information. IrpContext - Supplies the context pointer supplied by the driver when the IRP was created. Return Value: None. --*/ { BOOL CompleteIrp; KSTATUS Status; ASSERT(Irp->MajorCode == IrpMajorStateChange); // // The IRP is on its way down the stack. Do most processing here. // if (Irp->Direction == IrpDown) { Status = STATUS_NOT_SUPPORTED; CompleteIrp = TRUE; switch (Irp->MinorCode) { case IrpMinorQueryResources: Status = STATUS_SUCCESS; break; case IrpMinorStartDevice: Status = STATUS_SUCCESS; if (Child->DiskInterface.DiskToken == NULL) { RtlCopyMemory(&(Child->DiskInterface), &SdRk32DiskInterfaceTemplate, sizeof(DISK_INTERFACE)); Child->DiskInterface.BlockSize = 1 << Child->BlockShift; Child->DiskInterface.BlockCount = Child->BlockCount; Child->DiskInterface.DiskToken = Child; Status = IoCreateInterface(&SdRk32DiskInterfaceUuid, Child->Device, &(Child->DiskInterface), sizeof(DISK_INTERFACE)); if (!KSUCCESS(Status)) { Child->DiskInterface.DiskToken = NULL; } } break; case IrpMinorQueryChildren: Irp->U.QueryChildren.Children = NULL; Irp->U.QueryChildren.ChildCount = 0; Status = STATUS_SUCCESS; break; case IrpMinorQueryInterface: break; case IrpMinorRemoveDevice: if (Child->DiskInterface.DiskToken != NULL) { Status = IoDestroyInterface(&SdRk32DiskInterfaceUuid, Child->Device, &(Child->DiskInterface)); ASSERT(KSUCCESS(Status)); Child->DiskInterface.DiskToken = NULL; } SdRk32pChildReleaseReference(Child); Status = STATUS_SUCCESS; break; // // Pass all other IRPs down. // default: CompleteIrp = FALSE; break; } // // Complete the IRP unless there's a reason not to. // if (CompleteIrp != FALSE) { IoCompleteIrp(SdRk32Driver, Irp, Status); } // // The IRP is completed and is on its way back up. // } else { ASSERT(Irp->Direction == IrpUp); } return; } KSTATUS SdRk32ParentProcessResourceRequirements ( PIRP Irp, PSD_RK32_CONTEXT Device ) /*++ Routine Description: This routine filters through the resource requirements presented by the bus for a SD RK32xx Host controller. It adds an interrupt vector requirement for any interrupt line requested. Arguments: Irp - Supplies a pointer to the I/O request packet. Device - Supplies a pointer to this device. Return Value: Status code. --*/ { PRESOURCE_CONFIGURATION_LIST Requirements; KSTATUS Status; RESOURCE_REQUIREMENT VectorRequirement; ASSERT((Irp->MajorCode == IrpMajorStateChange) && (Irp->MinorCode == IrpMinorQueryResources)); // // Initialize a nice interrupt vector requirement in preparation. // RtlZeroMemory(&VectorRequirement, sizeof(RESOURCE_REQUIREMENT)); VectorRequirement.Type = ResourceTypeInterruptVector; VectorRequirement.Minimum = 0; VectorRequirement.Maximum = -1; VectorRequirement.Length = 1; // // Loop through all configuration lists, creating a vector for each line. // Requirements = Irp->U.QueryResources.ResourceRequirements; Status = IoCreateAndAddInterruptVectorsForLines(Requirements, &VectorRequirement); if (!KSUCCESS(Status)) { goto ProcessResourceRequirementsEnd; } ProcessResourceRequirementsEnd: return Status; } KSTATUS SdRk32ParentStartDevice ( PIRP Irp, PSD_RK32_CONTEXT Device ) /*++ Routine Description: This routine starts up the RK32xx SD controller. Arguments: Irp - Supplies a pointer to the I/O request packet. Device - Supplies a pointer to this SD RK32xx device. Return Value: Status code. --*/ { PRESOURCE_ALLOCATION Allocation; PRESOURCE_ALLOCATION_LIST AllocationList; IO_CONNECT_INTERRUPT_PARAMETERS Connect; PRESOURCE_ALLOCATION ControllerBase; PRESOURCE_ALLOCATION LineAllocation; SD_INITIALIZATION_BLOCK Parameters; KSTATUS Status; PRESOURCE_ALLOCATION VendorResource; ControllerBase = NULL; VendorResource = NULL; // // Loop through the allocated resources to get the controller base and the // interrupt. // AllocationList = Irp->U.StartDevice.ProcessorLocalResources; Allocation = IoGetNextResourceAllocation(AllocationList, NULL); while (Allocation != NULL) { // // If the resource is an interrupt vector, then it should have an // owning interrupt line allocation. // if (Allocation->Type == ResourceTypeInterruptVector) { ASSERT(Allocation->OwningAllocation != NULL); // // Save the line and vector number. The first one is the main // interrupt, the second one is the card detect interrupt. // LineAllocation = Allocation->OwningAllocation; if (Device->InterruptVector == -1ULL) { Device->InterruptLine = LineAllocation->Allocation; Device->InterruptVector = Allocation->Allocation; } else { ASSERT(Device->CardInterruptVector == -1ULL); Device->CardInterruptLine = LineAllocation->Allocation; Device->CardInterruptVector = Allocation->Allocation; } } else if (Allocation->Type == ResourceTypePhysicalAddressSpace) { ASSERT(ControllerBase == NULL); ControllerBase = Allocation; } else if (Allocation->Type == ResourceTypeVendorSpecific) { ASSERT(VendorResource == NULL); VendorResource = Allocation; } // // Get the next allocation in the list. // Allocation = IoGetNextResourceAllocation(AllocationList, Allocation); } // // Fail to start if the controller base was not found. // if ((ControllerBase == NULL) || (ControllerBase->Length < SD_RK32_CONTROLLER_LENGTH) || (VendorResource == NULL)) { Status = STATUS_INVALID_CONFIGURATION; goto StartDeviceEnd; } // // Initialize RK32xx specific stuff. // if (Device->ControllerBase == NULL) { Device->PhysicalAddress = ControllerBase->Allocation; Device->ControllerBase = MmMapPhysicalAddress( ControllerBase->Allocation, ControllerBase->Length, TRUE, FALSE, TRUE); if (Device->ControllerBase == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto StartDeviceEnd; } } Status = SdRk32InitializeVendorResource(Device, VendorResource); if (!KSUCCESS(Status)) { RtlDebugPrint("SdRk32InitializeVendorResource Failed: %x\n", Status); goto StartDeviceEnd; } Status = SdRk32HardResetController(Device); if (Status == STATUS_NO_MEDIA) { Status = STATUS_SUCCESS; goto StartDeviceEnd; } else if (!KSUCCESS(Status)) { RtlDebugPrint("SdRk32ResetController Failed: %x\n", Status); goto StartDeviceEnd; } // // Initialize the standard SD controller. // if (Device->Controller == NULL) { RtlZeroMemory(&Parameters, sizeof(SD_INITIALIZATION_BLOCK)); Parameters.Voltages = SD_VOLTAGE_32_33 | SD_VOLTAGE_33_34; Parameters.HostCapabilities = SD_MODE_4BIT | SD_MODE_HIGH_SPEED | SD_MODE_AUTO_CMD12 | SD_MODE_CMD23; Parameters.FundamentalClock = Device->FundamentalClock; Parameters.ConsumerContext = Device; Parameters.OsDevice = Device->OsDevice; RtlCopyMemory(&(Parameters.FunctionTable), &SdRk32FunctionTable, sizeof(SD_FUNCTION_TABLE)); Device->Controller = SdCreateController(&Parameters); if (Device->Controller == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto StartDeviceEnd; } } // // Attempt to connect the interrupt before initializing the controller. The // initialization process may trigger some interrupts. // if (Device->InterruptHandle == INVALID_HANDLE) { RtlZeroMemory(&Connect, sizeof(IO_CONNECT_INTERRUPT_PARAMETERS)); Connect.Version = IO_CONNECT_INTERRUPT_PARAMETERS_VERSION; Connect.Device = Irp->Device; Connect.LineNumber = Device->InterruptLine; Connect.Vector = Device->InterruptVector; Connect.InterruptServiceRoutine = SdRk32InterruptService; Connect.DispatchServiceRoutine = SdRk32InterruptServiceDispatch; Connect.Context = Device; Connect.Interrupt = &(Device->InterruptHandle); Status = IoConnectInterrupt(&Connect); if (!KSUCCESS(Status)) { goto StartDeviceEnd; } Device->Controller->InterruptHandle = Device->InterruptHandle; } // // Also wire up the card detect interrupt if it's present. // if ((Device->CardInterruptHandle == INVALID_HANDLE) && (Device->CardInterruptVector != -1ULL)) { RtlZeroMemory(&Connect, sizeof(IO_CONNECT_INTERRUPT_PARAMETERS)); Connect.Version = IO_CONNECT_INTERRUPT_PARAMETERS_VERSION; Connect.Device = Irp->Device; Connect.LineNumber = Device->CardInterruptLine; Connect.Vector = Device->CardInterruptVector; Connect.LowLevelServiceRoutine = SdRk32CardInterruptWorker; Connect.Context = Device; Connect.Interrupt = &(Device->InterruptHandle); Status = IoConnectInterrupt(&Connect); if (!KSUCCESS(Status)) { goto StartDeviceEnd; } } Status = STATUS_SUCCESS; StartDeviceEnd: if (!KSUCCESS(Status)) { if (Device->InterruptHandle != INVALID_HANDLE) { IoDisconnectInterrupt(Device->InterruptHandle); Device->InterruptHandle = INVALID_HANDLE; Device->Controller->InterruptHandle = INVALID_HANDLE; } if (Device->CardInterruptHandle != INVALID_HANDLE) { IoDisconnectInterrupt(Device->CardInterruptHandle); } if (Device->Controller != NULL) { SdDestroyController(Device->Controller); Device->Controller = NULL; } } return Status; } KSTATUS SdRk32ParentQueryChildren ( PIRP Irp, PSD_RK32_CONTEXT Device ) /*++ Routine Description: This routine potentially enumerates the disk device for the SD RK32xx controller. Arguments: Irp - Supplies a pointer to the I/O request packet. Device - Supplies a pointer to this device. Return Value: Status code. --*/ { ULONG BlockSize; PSTR DeviceId; ULONG FlagsMask; PSD_RK32_CHILD NewChild; ULONG OldFlags; KSTATUS Status; NewChild = NULL; // // Check to see if any changes to the children are pending. // FlagsMask = SD_CONTROLLER_FLAG_INSERTION_PENDING | SD_CONTROLLER_FLAG_REMOVAL_PENDING; OldFlags = RtlAtomicAnd32(&(Device->Controller->Flags), ~FlagsMask); // // If either a removal or insertion is pending, clean out the old child. // In practice, not all removals interrupt, meaning that two insertions can // arrive in a row. // if ((OldFlags & FlagsMask) != 0) { if (Device->Child != NULL) { KeAcquireQueuedLock(Device->Lock); RtlAtomicAnd32(&(Device->Child->Controller->Flags), ~SD_CONTROLLER_FLAG_MEDIA_PRESENT); KeReleaseQueuedLock(Device->Lock); Device->Child = NULL; } } // // If an insertion is pending, try to enumerate the child. // if ((OldFlags & SD_CONTROLLER_FLAG_INSERTION_PENDING) != 0) { ASSERT(Device->Child == NULL); RtlAtomicAnd32(&(Device->Controller->Flags), ~SD_CONTROLLER_FLAG_MEDIA_CHANGED); Status = SdInitializeController(Device->Controller, FALSE); if (!KSUCCESS(Status)) { if (Status == STATUS_TIMEOUT) { Status = STATUS_SUCCESS; } else { RtlDebugPrint("SdInitializeController failed: %x\n", Status); } goto ParentQueryChildrenEnd; } NewChild = SdRk32pCreateChild(Device); if (NewChild == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto ParentQueryChildrenEnd; } BlockSize = 0; Status = SdGetMediaParameters(NewChild->Controller, &(NewChild->BlockCount), &BlockSize); if (!KSUCCESS(Status)) { if (Status == STATUS_NO_MEDIA) { Status = STATUS_SUCCESS; } goto ParentQueryChildrenEnd; } ASSERT(POWER_OF_2(BlockSize) != FALSE); NewChild->BlockShift = RtlCountTrailingZeros32(BlockSize); // // Try to enable DMA, but it's okay if it doesn't succeed. // Status = SdRk32InitializeDma(Device); if (KSUCCESS(Status)) { NewChild->Flags |= SD_RK32_CHILD_FLAG_DMA_SUPPORTED; } else if (Status == STATUS_NO_MEDIA) { Status = STATUS_SUCCESS; goto ParentQueryChildrenEnd; } DeviceId = SD_MMC_DEVICE_ID; if (SD_IS_CARD_SD(Device->Controller)) { DeviceId = SD_CARD_DEVICE_ID; } Status = IoCreateDevice(SdRk32Driver, NewChild, Irp->Device, DeviceId, DISK_CLASS_ID, NULL, &(NewChild->Device)); if (!KSUCCESS(Status)) { return Status; } Device->Child = NewChild; NewChild = NULL; } // // If there's no child present, don't enumerate it. // if (Device->Child == NULL) { return STATUS_SUCCESS; } ASSERT((Device->Child != NULL) && (Device->Child->Device != NULL)); // // Enumerate the one child. // Status = IoMergeChildArrays(Irp, &(Device->Child->Device), 1, SD_ALLOCATION_TAG); ParentQueryChildrenEnd: if (NewChild != NULL) { ASSERT(NewChild->Device == NULL); SdRk32pChildReleaseReference(NewChild); } return Status; } KSTATUS SdRk32HardResetController ( PSD_RK32_CONTEXT Device ) /*++ Routine Description: This routine hard resets the RK32xx SD controller and card. Arguments: Device - Supplies a pointer to this SD RK32xx device. Return Value: Status code. --*/ { ULONGLONG Frequency; ULONG ResetMask; KSTATUS Status; ULONGLONG Timeout; ULONG Value; Frequency = HlQueryTimeCounterFrequency(); // // First perform a hardware reset on the SD card. // SD_DWC_WRITE_REGISTER(Device, SdDwcPower, SD_DWC_POWER_DISABLE); SD_DWC_WRITE_REGISTER(Device, SdDwcResetN, SD_DWC_RESET_ENABLE); HlBusySpin(5000); SD_DWC_WRITE_REGISTER(Device, SdDwcPower, SD_DWC_POWER_ENABLE); SD_DWC_WRITE_REGISTER(Device, SdDwcResetN, 0); HlBusySpin(1000); // // Perform a complete controller reset and wait for it to complete. // ResetMask = SD_DWC_CONTROL_FIFO_RESET | SD_DWC_CONTROL_DMA_RESET | SD_DWC_CONTROL_CONTROLLER_RESET; SD_DWC_WRITE_REGISTER(Device, SdDwcControl, ResetMask); Status = STATUS_TIMEOUT; Timeout = KeGetRecentTimeCounter() + (Frequency * SD_RK32_TIMEOUT); do { Value = SD_DWC_READ_REGISTER(Device, SdDwcControl); if ((Value & ResetMask) == 0) { Status = STATUS_SUCCESS; break; } } while (KeGetRecentTimeCounter() <= Timeout); if (!KSUCCESS(Status)) { goto HardResetControllerEnd; } // // Reset the internal DMA. // Value = SD_DWC_READ_REGISTER(Device, SdDwcBusMode); Value |= SD_DWC_BUS_MODE_INTERNAL_DMA_RESET; SD_DWC_WRITE_REGISTER(Device, SdDwcBusMode, Value); Status = STATUS_TIMEOUT; Timeout = KeGetRecentTimeCounter() + (Frequency * SD_RK32_TIMEOUT); do { Value = SD_DWC_READ_REGISTER(Device, SdDwcBusMode); if ((Value & SD_DWC_BUS_MODE_INTERNAL_DMA_RESET) == 0) { Status = STATUS_SUCCESS; break; } } while (KeGetRecentTimeCounter() <= Timeout); if (!KSUCCESS(Status)) { return Status; } Status = SdRk32SetRegulatorVoltage(Device, FALSE); if (!KSUCCESS(Status)) { return Status; } // // Clear interrupts. // SD_DWC_WRITE_REGISTER(Device, SdDwcInterruptStatus, SD_DWC_INTERRUPT_STATUS_ALL_MASK); // // Set 3v3 volts in the UHS register. // SD_DWC_WRITE_REGISTER(Device, SdDwcUhs, SD_DWC_UHS_VOLTAGE_3V3); // // Set the clock to 400kHz in preparation for sending CMD0 with the // initialization bit set. // Status = SdRk32SetClockSpeed(Device, SdClock400kHz); if (!KSUCCESS(Status)) { goto HardResetControllerEnd; } // // Reset the card by sending the CMD0 reset command with the initialization // bit set. // Value = SD_DWC_COMMAND_START | SD_DWC_COMMAND_USE_HOLD_REGISTER | SD_DWC_COMMAND_SEND_INITIALIZATION; SD_DWC_WRITE_REGISTER(Device, SdDwcCommand, Value); // // Wait for the command to complete. // Status = STATUS_TIMEOUT; Timeout = KeGetRecentTimeCounter() + (Frequency * SD_RK32_TIMEOUT); do { Value = SD_DWC_READ_REGISTER(Device, SdDwcCommand); if ((Value & SD_DWC_COMMAND_START) == 0) { Status = STATUS_SUCCESS; break; } } while (KeGetRecentTimeCounter() <= Timeout); if (!KSUCCESS(Status)) { goto HardResetControllerEnd; } Status = STATUS_TIMEOUT; Timeout = KeGetRecentTimeCounter() + (Frequency * SD_RK32_TIMEOUT); do { Value = SD_DWC_READ_REGISTER(Device, SdDwcInterruptStatus); if (Value != 0) { if ((Value & SD_DWC_INTERRUPT_STATUS_COMMAND_DONE) != 0) { Status = STATUS_SUCCESS; } else if ((Value & SD_DWC_INTERRUPT_STATUS_ERROR_RESPONSE_TIMEOUT) != 0) { Status = STATUS_NO_MEDIA; } else { Status = STATUS_DEVICE_IO_ERROR; } SD_DWC_WRITE_REGISTER(Device, SdDwcInterruptStatus, Value); break; } } while (KeGetRecentTimeCounter() <= Timeout); if (!KSUCCESS(Status)) { goto HardResetControllerEnd; } HardResetControllerEnd: return Status; } KSTATUS SdRk32InitializeVendorResource ( PSD_RK32_CONTEXT Device, PRESOURCE_ALLOCATION Resource ) /*++ Routine Description: This routine gets the fundamental clock frequency to use for the SD controller. It initializes the device context with the value. Arguments: Device - Supplies a pointer to the SD RK32 device. Resource - Supplies a pointer to the vendor specific resource data. Return Value: Status code. --*/ { PSD_RK32_VENDOR_RESOURCE Data; KSTATUS Status; Data = Resource->Data; if (Resource->DataSize < sizeof(SD_RK32_VENDOR_RESOURCE)) { return STATUS_DATA_LENGTH_MISMATCH; } if (RtlAreUuidsEqual(&(Data->Uuid), &SdRk32VendorResourceUuid) == FALSE) { return STATUS_INVALID_CONFIGURATION; } Device->FundamentalClock = Data->FundamentalClock; Device->Ldo = Data->Ldo; if (Device->Ldo != 0) { Status = IoRegisterForInterfaceNotifications( &SdRk32Rk808InterfaceUuid, SdRk32Rk808InterfaceNotificationCallback, NULL, Device, TRUE); if (!KSUCCESS(Status)) { goto InitializeVendorResourceEnd; } } // // Eventually, this should be handled by a more official clock manager. // Device->Cru = MmMapPhysicalAddress(Data->Cru, MmPageSize(), TRUE, FALSE, TRUE); if (Device->Cru == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto InitializeVendorResourceEnd; } Device->VendorData = Data; Status = STATUS_SUCCESS; InitializeVendorResourceEnd: return Status; } INTERRUPT_STATUS SdRk32InterruptService ( PVOID Context ) /*++ Routine Description: This routine implements the RK32xx SD interrupt service routine. Arguments: Context - Supplies the context pointer given to the system when the interrupt was connected. In this case, this points to the RK32xx SD controller. Return Value: Interrupt status. --*/ { PSD_CONTROLLER Controller; PSD_RK32_CONTEXT Device; ULONG MaskedStatus; Device = (PSD_RK32_CONTEXT)Context; MaskedStatus = SD_DWC_READ_REGISTER(Device, SdDwcMaskedInterruptStatus); if (MaskedStatus == 0) { return InterruptStatusNotClaimed; } Controller = Device->Controller; SD_DWC_WRITE_REGISTER(Device, SdDwcInterruptStatus, MaskedStatus); RtlAtomicOr32(&(Controller->PendingStatusBits), MaskedStatus); return InterruptStatusClaimed; } INTERRUPT_STATUS SdRk32InterruptServiceDispatch ( PVOID Context ) /*++ Routine Description: This routine implements the RK32xx SD dispatch level interrupt service routine. Arguments: Context - Supplies the context pointer given to the system when the interrupt was connected. In this case, this points to the RK32xx SD controller. Return Value: Interrupt status. --*/ { UINTN BytesCompleted; PVOID CompletionContext; PSD_IO_COMPLETION_ROUTINE CompletionRoutine; PSD_CONTROLLER Controller; PSD_RK32_CONTEXT Device; BOOL Inserted; ULONG PendingBits; BOOL Removed; KSTATUS Status; Device = (PSD_RK32_CONTEXT)Context; Controller = Device->Controller; PendingBits = RtlAtomicExchange32(&(Controller->PendingStatusBits), 0); if (PendingBits == 0) { return InterruptStatusNotClaimed; } // // Process a media change. // Status = STATUS_DEVICE_IO_ERROR; Inserted = FALSE; Removed = FALSE; if ((PendingBits & SD_DWC_INTERRUPT_STATUS_CARD_DETECT) != 0) { // // TODO: Hanndle RK32xx SD/MMC insertion and removal. // ASSERT(FALSE); } KeAcquireSpinLock(&(Device->DpcLock)); // // Process the I/O completion. The only other interrupt bits that are sent // to the DPC are the error bits and the transfer complete bit. // if ((PendingBits & SD_DWC_INTERRUPT_ERROR_MASK) != 0) { Status = STATUS_DEVICE_IO_ERROR; if ((Controller->Flags & SD_CONTROLLER_FLAG_MEDIA_CHANGED) != 0) { Inserted = TRUE; Removed = TRUE; } else if ((Controller->Flags & SD_CONTROLLER_FLAG_MEDIA_PRESENT) == 0) { Removed = TRUE; } } else if ((PendingBits & SD_DWC_INTERRUPT_STATUS_DATA_TRANSFER_OVER) != 0) { Status = STATUS_SUCCESS; } else if ((PendingBits & SD_DWC_INTERRUPT_STATUS_COMMAND_DONE) != 0) { Status = STATUS_SUCCESS; } if (Controller->IoCompletionRoutine != NULL) { CompletionRoutine = Controller->IoCompletionRoutine; CompletionContext = Controller->IoCompletionContext; BytesCompleted = Controller->IoRequestSize; Controller->IoCompletionRoutine = NULL; Controller->IoCompletionContext = NULL; Controller->IoRequestSize = 0; CompletionRoutine(Controller, CompletionContext, BytesCompleted, Status); } if (((Inserted != FALSE) || (Removed != FALSE)) && (Controller->FunctionTable.MediaChangeCallback != NULL)) { Controller->FunctionTable.MediaChangeCallback( Controller, Controller->ConsumerContext, Removed, Inserted); } KeReleaseSpinLock(&(Device->DpcLock)); return InterruptStatusClaimed; } INTERRUPT_STATUS SdRk32CardInterruptWorker ( PVOID Context ) /*++ Routine Description: This routine implements the RK32xx SD low level card detect interrupt service work routine. Arguments: Context - Supplies the context pointer given to the system when the interrupt was connected. In this case, this points to the RK32xx SD controller. Return Value: Interrupt status. --*/ { PSD_RK32_CONTEXT Device; Device = (PSD_RK32_CONTEXT)Context; if (Device->Controller->FunctionTable.MediaChangeCallback != NULL) { Device->Controller->FunctionTable.MediaChangeCallback( Device->Controller, NULL, TRUE, TRUE); } return InterruptStatusClaimed; } VOID SdRk32DmaCompletion ( PSD_CONTROLLER Controller, PVOID Context, UINTN BytesTransferred, KSTATUS Status ) /*++ Routine Description: This routine is called by the SD library when a DMA transfer completes. This routine is called from a DPC and, as a result, can get called back at dispatch level. Arguments: Controller - Supplies a pointer to the controller. Context - Supplies a context pointer passed to the library when the DMA request was issued. BytesTransferred - Supplies the number of bytes transferred in the request. Status - Supplies the status code representing the completion of the I/O. Return Value: None. --*/ { UINTN BlockCount; ULONGLONG BlockOffset; PSD_RK32_CHILD Child; IO_OFFSET IoOffset; UINTN IoSize; PIRP Irp; BOOL Write; Child = Context; Irp = Child->Irp; ASSERT(Irp != NULL); if (!KSUCCESS(Status)) { RtlDebugPrint("SD RK32xx Failed %x %I64x %x: %x\n", Irp->MinorCode, Irp->U.ReadWrite.IoOffset, Irp->U.ReadWrite.IoSizeInBytes, Status); BytesTransferred = 0; IoCompleteIrp(SdRk32Driver, Irp, Status); return; } Irp->U.ReadWrite.IoBytesCompleted += BytesTransferred; Irp->U.ReadWrite.NewIoOffset += BytesTransferred; // // If this transfer's over, potentially send a stop. If that's done or // not needed, complete the IRP. // if (Irp->U.ReadWrite.IoBytesCompleted == Irp->U.ReadWrite.IoSizeInBytes) { if ((Controller->SendStop != FALSE) && ((Controller->HostCapabilities & SD_MODE_AUTO_CMD12) == 0)) { Controller->SendStop = FALSE; Child->Controller->IoCompletionRoutine = SdRk32DmaCompletion; Child->Controller->IoCompletionContext = Child; Status = SdSendStop(Controller, TRUE, TRUE); if (!KSUCCESS(Status)) { IoCompleteIrp(SdRk32Driver, Irp, Status); } } else { IoCompleteIrp(SdRk32Driver, Irp, Status); } return; } IoOffset = Irp->U.ReadWrite.IoOffset + Irp->U.ReadWrite.IoBytesCompleted; BlockOffset = IoOffset >> Child->BlockShift; IoSize = Irp->U.ReadWrite.IoSizeInBytes - Irp->U.ReadWrite.IoBytesCompleted; BlockCount = IoSize >> Child->BlockShift; Write = FALSE; if (Irp->MinorCode == IrpMinorIoWrite) { Write = TRUE; } SdRk32BlockIoDma(Child->Parent, BlockOffset, BlockCount, Irp->U.ReadWrite.IoBuffer, Irp->U.ReadWrite.IoBytesCompleted, Write, SdRk32DmaCompletion, Child); return; } PSD_RK32_CHILD SdRk32pCreateChild ( PSD_RK32_CONTEXT Device ) /*++ Routine Description: This routine creates an SD child context. Arguments: Device - Supplies a pointer to the parent device to which the child belongs. Return Value: Returns a pointer to the new child on success or NULL on failure. --*/ { PSD_RK32_CHILD Child; Child = MmAllocateNonPagedPool(sizeof(SD_RK32_CHILD), SD_ALLOCATION_TAG); if (Child == NULL) { return NULL; } RtlZeroMemory(Child, sizeof(SD_RK32_CHILD)); Child->Type = SdRk32Child; Child->Parent = Device; Child->Controller = Device->Controller; Child->ControllerLock = Device->Lock; Child->ReferenceCount = 1; return Child; } VOID SdRk32pDestroyChild ( PSD_RK32_CHILD Child ) /*++ Routine Description: This routine destroys the given SD child device. Arguments: Child - Supplies a pointer to the SD child device to destroy. Return Value: None. --*/ { ASSERT(Child->DiskInterface.DiskToken == NULL); ASSERT(Child->Irp == NULL); MmFreeNonPagedPool(Child); return; } VOID SdRk32pChildAddReference ( PSD_RK32_CHILD Child ) /*++ Routine Description: This routine adds a reference to SD child device. Arguments: Child - Supplies a pointer to the SD child device. Return Value: None. --*/ { ULONG OldReferenceCount; OldReferenceCount = RtlAtomicAdd32(&(Child->ReferenceCount), 1); ASSERT((OldReferenceCount != 0) && (OldReferenceCount < 0x10000000)); return; } VOID SdRk32pChildReleaseReference ( PSD_RK32_CHILD Child ) /*++ Routine Description: This routine releases a reference from the SD child. Arguments: Child - Supplies a pointer to the SD child. Return Value: None. --*/ { ULONG OldReferenceCount; OldReferenceCount = RtlAtomicAdd32(&(Child->ReferenceCount), (ULONG)-1); ASSERT((OldReferenceCount != 0) && (OldReferenceCount < 0x10000000)); if (OldReferenceCount == 1) { SdRk32pDestroyChild(Child); } return; } KSTATUS SdRk32ChildBlockIoReset ( PVOID DiskToken ) /*++ Routine Description: This routine must be called immediately before using the block read and write routines in order to allow the disk to reset any I/O channels in preparation for imminent block I/O. This routine is called at high run level. Arguments: DiskToken - Supplies an opaque token for the disk. The appropriate token is retrieved by querying the disk device information. Return Value: Status code. --*/ { PSD_RK32_CHILD Child; KSTATUS Status; ULONG Value; ASSERT(KeGetRunLevel() == RunLevelHigh); Child = (PSD_RK32_CHILD)DiskToken; // // Put the SD controller into critical execution mode. // SdSetCriticalMode(Child->Controller, TRUE); // // Abort any current transaction that might have been left incomplete // when the crash occurred. // Status = SdAbortTransaction(Child->Controller, FALSE); if (!KSUCCESS(Status)) { return Status; } // // Make sure the controller is not stuck in DMA transfer mode. // // // Make sure DMA mode is disabled. // Value = SD_DWC_READ_REGISTER(Child->Parent, SdDwcControl); if ((Value & SD_DWC_CONTROL_USE_INTERNAL_DMAC) != 0) { Value &= ~SD_DWC_CONTROL_USE_INTERNAL_DMAC; SD_DWC_WRITE_REGISTER(Child->Parent, SdDwcControl, Value); } return Status; } KSTATUS SdRk32ChildBlockIoRead ( PVOID DiskToken, PIO_BUFFER IoBuffer, ULONGLONG BlockAddress, UINTN BlockCount, PUINTN BlocksCompleted ) /*++ Routine Description: This routine reads the block contents from the disk into the given I/O buffer using polled I/O. It does so without acquiring any locks or allocating any resources, as this routine is used for crash dump support when the system is in a very fragile state. This routine must be called at high level. Arguments: DiskToken - Supplies an opaque token for the disk. The appropriate token is retrieved by querying the disk device information. IoBuffer - Supplies a pointer to the I/O buffer where the data will be read. BlockAddress - Supplies the block index to read (for physical disk, this is the LBA). BlockCount - Supplies the number of blocks to read. BlocksCompleted - Supplies a pointer that receives the total number of blocks read. Return Value: Status code. --*/ { PSD_RK32_CHILD Child; IRP_READ_WRITE IrpReadWrite; KSTATUS Status; ASSERT(KeGetRunLevel() == RunLevelHigh); Child = (PSD_RK32_CHILD)DiskToken; IrpReadWrite.IoBuffer = IoBuffer; IrpReadWrite.IoOffset = BlockAddress << Child->BlockShift; IrpReadWrite.IoSizeInBytes = BlockCount << Child->BlockShift; // // As this read routine is meant for critical code paths (crash dump), // indicate that the channel should not be locked when performing the I/O. // It may be that some other thread holds the lock, which would cause a // dead lock as all other processors and threads are likely frozen. // Status = SdRk32PerformIoPolled(&IrpReadWrite, Child, FALSE, FALSE); *BlocksCompleted = IrpReadWrite.IoBytesCompleted >> Child->BlockShift; return Status; } KSTATUS SdRk32ChildBlockIoWrite ( PVOID DiskToken, PIO_BUFFER IoBuffer, ULONGLONG BlockAddress, UINTN BlockCount, PUINTN BlocksCompleted ) /*++ Routine Description: This routine writes the contents of the given I/O buffer to the disk using polled I/O. It does so without acquiring any locks or allocating any resources, as this routine is used for crash dump support when the system is in a very fragile state. This routine must be called at high level. Arguments: DiskToken - Supplies an opaque token for the disk. The appropriate token is retrieved by querying the disk device information. IoBuffer - Supplies a pointer to the I/O buffer containing the data to write. BlockAddress - Supplies the block index to write to (for physical disk, this is the LBA). BlockCount - Supplies the number of blocks to write. BlocksCompleted - Supplies a pointer that receives the total number of blocks written. Return Value: Status code. --*/ { PSD_RK32_CHILD Child; IRP_READ_WRITE IrpReadWrite; KSTATUS Status; ASSERT(KeGetRunLevel() == RunLevelHigh); Child = (PSD_RK32_CHILD)DiskToken; IrpReadWrite.IoBuffer = IoBuffer; IrpReadWrite.IoOffset = BlockAddress << Child->BlockShift; IrpReadWrite.IoSizeInBytes = BlockCount << Child->BlockShift; // // As this write routine is meant for critical code paths (crash dump), // indicate that the channel should not be locked when performing the I/O. // It may be that some other thread holds the lock, which would cause a // dead lock as all other processors and threads are likely frozen. // Status = SdRk32PerformIoPolled(&IrpReadWrite, Child, TRUE, FALSE); *BlocksCompleted = IrpReadWrite.IoBytesCompleted >> Child->BlockShift; return Status; } KSTATUS SdRk32PerformIoPolled ( PIRP_READ_WRITE IrpReadWrite, PSD_RK32_CHILD Child, BOOL Write, BOOL LockRequired ) /*++ Routine Description: This routine performs polled I/O data transfers. Arguments: IrpReadWrite - Supplies a pointer to the IRP read/write context. Child - Supplies a pointer to the SD child device. Write - Supplies a boolean indicating if this is a read operation (TRUE) or a write operation (FALSE). LockRequired - Supplies a boolean indicating if this operation requires the child's controller lock to be acquired (TRUE) or not (FALSE). Return Value: None. --*/ { UINTN BlockCount; ULONGLONG BlockOffset; UINTN BytesRemaining; UINTN BytesThisRound; KSTATUS CompletionStatus; PSD_CONTROLLER Controller; PIO_BUFFER_FRAGMENT Fragment; UINTN FragmentIndex; UINTN FragmentOffset; PIO_BUFFER IoBuffer; UINTN IoBufferOffset; ULONG IrpReadWriteFlags; BOOL LockHeld; BOOL ReadWriteIrpPrepared; KSTATUS Status; PVOID VirtualAddress; IrpReadWrite->IoBytesCompleted = 0; LockHeld = FALSE; ReadWriteIrpPrepared = FALSE; ASSERT(IrpReadWrite->IoBuffer != NULL); ASSERT(Child->Type == SdRk32Child); ASSERT((Child->BlockCount != 0) && (Child->BlockShift != 0)); Controller = Child->Controller; // // Validate the supplied I/O buffer is aligned and big enough. // IrpReadWriteFlags = IRP_READ_WRITE_FLAG_POLLED; if (Write != FALSE) { IrpReadWriteFlags |= IRP_READ_WRITE_FLAG_WRITE; } Status = IoPrepareReadWriteIrp(IrpReadWrite, 1 << Child->BlockShift, 0, MAX_ULONGLONG, IrpReadWriteFlags); if (!KSUCCESS(Status)) { goto PerformBlockIoPolledEnd; } ReadWriteIrpPrepared = TRUE; // // Make sure the I/O buffer is mapped before use. SD depends on the buffer // being mapped. // IoBuffer = IrpReadWrite->IoBuffer; Status = MmMapIoBuffer(IoBuffer, FALSE, FALSE, FALSE); if (!KSUCCESS(Status)) { goto PerformBlockIoPolledEnd; } // // Find the starting fragment based on the current offset. // IoBufferOffset = MmGetIoBufferCurrentOffset(IoBuffer); FragmentIndex = 0; FragmentOffset = 0; while (IoBufferOffset != 0) { ASSERT(FragmentIndex < IoBuffer->FragmentCount); Fragment = &(IoBuffer->Fragment[FragmentIndex]); if (IoBufferOffset < Fragment->Size) { FragmentOffset = IoBufferOffset; break; } IoBufferOffset -= Fragment->Size; FragmentIndex += 1; } if (LockRequired != FALSE) { KeAcquireQueuedLock(Child->ControllerLock); LockHeld = TRUE; } if (((Controller->Flags & SD_CONTROLLER_FLAG_MEDIA_PRESENT) == 0) || ((Controller->Flags & SD_CONTROLLER_FLAG_MEDIA_CHANGED) != 0)) { Status = STATUS_NO_MEDIA; if ((Controller->Flags & SD_CONTROLLER_FLAG_MEDIA_CHANGED) != 0) { Status = STATUS_MEDIA_CHANGED; } goto PerformBlockIoPolledEnd; } // // Loop reading in or writing out each fragment in the I/O buffer. // BytesRemaining = IrpReadWrite->IoSizeInBytes; ASSERT(IS_ALIGNED(BytesRemaining, 1 << Child->BlockShift) != FALSE); ASSERT(IS_ALIGNED(IrpReadWrite->IoOffset, 1 << Child->BlockShift) != FALSE); BlockOffset = IrpReadWrite->IoOffset >> Child->BlockShift; while (BytesRemaining != 0) { ASSERT(FragmentIndex < IoBuffer->FragmentCount); Fragment = &(IoBuffer->Fragment[FragmentIndex]); VirtualAddress = Fragment->VirtualAddress + FragmentOffset; BytesThisRound = Fragment->Size - FragmentOffset; if (BytesRemaining < BytesThisRound) { BytesThisRound = BytesRemaining; } ASSERT(IS_ALIGNED(BytesThisRound, (1 << Child->BlockShift)) != FALSE); BlockCount = BytesThisRound >> Child->BlockShift; // // Make sure the system isn't trying to do I/O off the end of the disk. // ASSERT(BlockOffset < Child->BlockCount); ASSERT(BlockCount >= 1); Status = SdBlockIoPolled(Controller, BlockOffset, BlockCount, VirtualAddress, Write); if (!KSUCCESS(Status)) { goto PerformBlockIoPolledEnd; } BlockOffset += BlockCount; BytesRemaining -= BytesThisRound; IrpReadWrite->IoBytesCompleted += BytesThisRound; FragmentOffset += BytesThisRound; if (FragmentOffset >= Fragment->Size) { FragmentIndex += 1; FragmentOffset = 0; } } Status = STATUS_SUCCESS; PerformBlockIoPolledEnd: if (LockHeld != FALSE) { KeReleaseQueuedLock(Child->ControllerLock); } if (ReadWriteIrpPrepared != FALSE) { CompletionStatus = IoCompleteReadWriteIrp(IrpReadWrite, IrpReadWriteFlags); if (!KSUCCESS(CompletionStatus) && KSUCCESS(Status)) { Status = CompletionStatus; } } IrpReadWrite->NewIoOffset = IrpReadWrite->IoOffset + IrpReadWrite->IoBytesCompleted; return Status; } KSTATUS SdRk32InitializeDma ( PSD_RK32_CONTEXT Device ) /*++ Routine Description: This routine initializes DMA support in the RK32 host controller. Arguments: Device - Supplies a pointer to the RK32xx SD device. Return Value: Status code. --*/ { PSD_CONTROLLER Controller; PSD_DWC_DMA_DESCRIPTOR Descriptor; ULONG IoBufferFlags; ULONG Value; Controller = Device->Controller; if ((Controller->Flags & SD_CONTROLLER_FLAG_MEDIA_CHANGED) != 0) { return STATUS_MEDIA_CHANGED; } else if ((Controller->Flags & SD_CONTROLLER_FLAG_MEDIA_PRESENT) == 0) { return STATUS_NO_MEDIA; } if ((Controller->HostCapabilities & SD_MODE_AUTO_CMD12) == 0) { return STATUS_NOT_SUPPORTED; } // // Create the DMA descriptor table if not already done. // if (Controller->DmaDescriptorTable == NULL) { IoBufferFlags = IO_BUFFER_FLAG_PHYSICALLY_CONTIGUOUS | IO_BUFFER_FLAG_MAP_NON_CACHED; Controller->DmaDescriptorTable = MmAllocateNonPagedIoBuffer( 0, MAX_ULONG, 4, SD_RK32_DMA_DESCRIPTOR_TABLE_SIZE, IoBufferFlags); if (Controller->DmaDescriptorTable == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } ASSERT(Controller->DmaDescriptorTable->FragmentCount == 1); } Descriptor = Controller->DmaDescriptorTable->Fragment[0].VirtualAddress; RtlZeroMemory(Descriptor, SD_RK32_DMA_DESCRIPTOR_TABLE_SIZE); // // Enable DMA in the control register. // Value = SD_DWC_READ_REGISTER(Device, SdDwcControl); Value |= SD_DWC_CONTROL_DMA_ENABLE; SD_DWC_WRITE_REGISTER(Device, SdDwcControl, Value); // // Read it to make sure the write stuck. // Value = SD_DWC_READ_REGISTER(Device, SdDwcControl); if ((Value & SD_DWC_CONTROL_DMA_ENABLE) == 0) { return STATUS_NOT_SUPPORTED; } // // Enable internal DMA in the bus mode register. // Value = SD_DWC_READ_REGISTER(Device, SdDwcBusMode); Value |= SD_DWC_BUS_MODE_IDMAC_ENABLE; SD_DWC_WRITE_REGISTER(Device, SdDwcBusMode, Value); // // Read it to make sure the write stuck. // Value = SD_DWC_READ_REGISTER(Device, SdDwcBusMode); if ((Value & SD_DWC_BUS_MODE_IDMAC_ENABLE) == 0) { return STATUS_NOT_SUPPORTED; } return STATUS_SUCCESS; } VOID SdRk32BlockIoDma ( PSD_RK32_CONTEXT Device, ULONGLONG BlockOffset, UINTN BlockCount, PIO_BUFFER IoBuffer, UINTN IoBufferOffset, BOOL Write, PSD_IO_COMPLETION_ROUTINE CompletionRoutine, PVOID CompletionContext ) /*++ Routine Description: This routine performs a block I/O read or write using standard ADMA2. Arguments: Device - Supplies a pointer to the RK32xx controller device. BlockOffset - Supplies the logical block address of the I/O. BlockCount - Supplies the number of blocks to read or write. IoBuffer - Supplies a pointer to the buffer containing the data to write or where the read data should be returned. IoBufferOffset - Supplies the offset from the beginning of the I/O buffer where this I/O should begin. This is relative to the I/O buffer's current offset. Write - Supplies a boolean indicating if this is a read operation (FALSE) or a write operation. CompletionRoutine - Supplies a pointer to a function to call when the I/O completes. CompletionContext - Supplies a context pointer to pass as a parameter to the completion routine. Return Value: None. The status of the operation is returned when the completion routine is called, which may be during the execution of this function in the case of an early failure. --*/ { ULONG BlockLength; SD_COMMAND Command; PSD_CONTROLLER Controller; ULONG DescriptorCount; PHYSICAL_ADDRESS DescriptorPhysical; UINTN DescriptorSize; PSD_DWC_DMA_DESCRIPTOR DmaDescriptor; PIO_BUFFER DmaDescriptorTable; PIO_BUFFER_FRAGMENT Fragment; UINTN FragmentIndex; UINTN FragmentOffset; PHYSICAL_ADDRESS PhysicalAddress; KSTATUS Status; ULONG TableAddress; UINTN TransferSize; UINTN TransferSizeRemaining; ULONG Value; ASSERT(BlockCount != 0); Controller = Device->Controller; if ((Controller->Flags & SD_CONTROLLER_FLAG_MEDIA_CHANGED) != 0) { Status = STATUS_MEDIA_CHANGED; goto BlockIoDmaEnd; } else if ((Controller->Flags & SD_CONTROLLER_FLAG_MEDIA_PRESENT) == 0) { Status = STATUS_NO_MEDIA; goto BlockIoDmaEnd; } if (Write != FALSE) { if (BlockCount > 1) { Command.Command = SdCommandWriteMultipleBlocks; } else { Command.Command = SdCommandWriteSingleBlock; } BlockLength = Controller->WriteBlockLength; TransferSize = BlockCount * BlockLength; ASSERT(TransferSize != 0); } else { if (BlockCount > 1) { Command.Command = SdCommandReadMultipleBlocks; } else { Command.Command = SdCommandReadSingleBlock; } BlockLength = Controller->ReadBlockLength; TransferSize = BlockCount * BlockLength; ASSERT(TransferSize != 0); } // // Get to the correct spot in the I/O buffer. // IoBufferOffset += MmGetIoBufferCurrentOffset(IoBuffer); FragmentIndex = 0; FragmentOffset = 0; while (IoBufferOffset != 0) { ASSERT(FragmentIndex < IoBuffer->FragmentCount); Fragment = &(IoBuffer->Fragment[FragmentIndex]); if (IoBufferOffset < Fragment->Size) { FragmentOffset = IoBufferOffset; break; } IoBufferOffset -= Fragment->Size; FragmentIndex += 1; } // // Do a DMA reset. // Value = SD_DWC_READ_REGISTER(Device, SdDwcControl); Value |= SD_DWC_CONTROL_DMA_RESET; SD_DWC_WRITE_REGISTER(Device, SdDwcControl, Value); do { Value = SD_DWC_READ_REGISTER(Device, SdDwcControl); } while ((Value & SD_DWC_CONTROL_DMA_RESET) != 0); Value = SD_DWC_READ_REGISTER(Device, SdDwcBusMode); Value |= SD_DWC_BUS_MODE_INTERNAL_DMA_RESET; SD_DWC_WRITE_REGISTER(Device, SdDwcBusMode, Value); // // Fill out the DMA descriptors. // DmaDescriptorTable = Controller->DmaDescriptorTable; DmaDescriptor = DmaDescriptorTable->Fragment[0].VirtualAddress; DescriptorPhysical = DmaDescriptorTable->Fragment[0].PhysicalAddress; DescriptorCount = 0; TransferSizeRemaining = TransferSize; while ((TransferSizeRemaining != 0) && (DescriptorCount < SD_RK32_DMA_DESCRIPTOR_COUNT - 1)) { ASSERT(FragmentIndex < IoBuffer->FragmentCount); Fragment = &(IoBuffer->Fragment[FragmentIndex]); // // This descriptor size is going to the the minimum of the total // remaining size, the size that can fit in a DMA descriptor, and the // remaining size of the fragment. // DescriptorSize = TransferSizeRemaining; if (DescriptorSize > SD_DWC_DMA_DESCRIPTOR_MAX_BUFFER_SIZE) { DescriptorSize = SD_DWC_DMA_DESCRIPTOR_MAX_BUFFER_SIZE; } if (DescriptorSize > (Fragment->Size - FragmentOffset)) { DescriptorSize = Fragment->Size - FragmentOffset; } TransferSizeRemaining -= DescriptorSize; PhysicalAddress = Fragment->PhysicalAddress + FragmentOffset; // // Assert that the buffer is within the first 4GB. // ASSERT(((ULONG)PhysicalAddress == PhysicalAddress) && ((ULONG)(PhysicalAddress + DescriptorSize) == PhysicalAddress + DescriptorSize)); DmaDescriptor->Address = PhysicalAddress; DmaDescriptor->Size = DescriptorSize; DmaDescriptor->Control = SD_DWC_DMA_DESCRIPTOR_CONTROL_OWN | SD_DWC_DMA_DESCRIPTOR_CONTROL_SECOND_ADDRESS_CHAINED | SD_DWC_DMA_DESCRIPTOR_CONTROL_DISABLE_INTERRUPT_ON_COMPLETION; if (DescriptorCount == 0) { DmaDescriptor->Control |= SD_DWC_DMA_DESCRIPTOR_CONTROL_FIRST_DESCRIPTOR; } DescriptorPhysical += sizeof(SD_DWC_DMA_DESCRIPTOR); DmaDescriptor->NextDescriptor = DescriptorPhysical; DmaDescriptor += 1; DescriptorCount += 1; FragmentOffset += DescriptorSize; if (FragmentOffset >= Fragment->Size) { FragmentIndex += 1; FragmentOffset = 0; } } // // Mark the last DMA descriptor as the end of the transfer. // DmaDescriptor -= 1; DmaDescriptor->Control &= ~(SD_DWC_DMA_DESCRIPTOR_CONTROL_SECOND_ADDRESS_CHAINED | SD_DWC_DMA_DESCRIPTOR_CONTROL_DISABLE_INTERRUPT_ON_COMPLETION); DmaDescriptor->Control |= SD_DWC_DMA_DESCRIPTOR_CONTROL_LAST_DESCRIPTOR; DmaDescriptor->NextDescriptor = 0; RtlMemoryBarrier(); Command.ResponseType = SD_RESPONSE_R1; if ((Controller->Flags & SD_CONTROLLER_FLAG_HIGH_CAPACITY) != 0) { Command.CommandArgument = BlockOffset; } else { Command.CommandArgument = BlockOffset * BlockLength; } ASSERT((TransferSize - TransferSizeRemaining) <= MAX_ULONG); Command.BufferSize = (ULONG)(TransferSize - TransferSizeRemaining); Command.BufferVirtual = NULL; Command.BufferPhysical = INVALID_PHYSICAL_ADDRESS; Command.Write = Write; Command.Dma = TRUE; Controller->IoCompletionRoutine = CompletionRoutine; Controller->IoCompletionContext = CompletionContext; Controller->IoRequestSize = Command.BufferSize; // // Write the table base, enable DMA, and write the poll demand to get it // moving. // TableAddress = (ULONG)(DmaDescriptorTable->Fragment[0].PhysicalAddress); SD_DWC_WRITE_REGISTER(Device, SdDwcDescriptorBaseAddress, TableAddress); Value = SD_DWC_READ_REGISTER(Device, SdDwcControl); Value |= SD_DWC_CONTROL_USE_INTERNAL_DMAC | SD_DWC_CONTROL_DMA_ENABLE; SD_DWC_WRITE_REGISTER(Device, SdDwcControl, Value); Value = SD_DWC_READ_REGISTER(Device, SdDwcBusMode); Value |= SD_DWC_BUS_MODE_IDMAC_ENABLE | SD_DWC_BUS_MODE_FIXED_BURST; SD_DWC_WRITE_REGISTER(Device, SdDwcBusMode, Value); SD_DWC_WRITE_REGISTER(Device, SdDwcPollDemand, 1); Status = Controller->FunctionTable.SendCommand(Controller, Controller->ConsumerContext, &Command); if (!KSUCCESS(Status)) { Controller->IoCompletionRoutine = NULL; Controller->IoCompletionContext = NULL; Controller->IoRequestSize = 0; goto BlockIoDmaEnd; } Status = STATUS_SUCCESS; BlockIoDmaEnd: // // If this routine failed, call the completion routine back immediately. // if (!KSUCCESS(Status)) { CompletionRoutine(Controller, CompletionContext, 0, Status); } return; } KSTATUS SdRk32InitializeController ( PSD_CONTROLLER Controller, PVOID Context, ULONG Phase ) /*++ Routine Description: This routine performs any controller specific initialization steps. Arguments: Controller - Supplies a pointer to the controller. Context - Supplies a context pointer passed to the SD/MMC library upon creation of the controller. Phase - Supplies the phase of initialization. Phase 0 happens after the initial software reset and Phase 1 happens after the bus width has been set to 1 and the speed to 400KHz. Return Value: Status code. --*/ { PSD_RK32_CONTEXT Device; ULONG Mask; KSTATUS Status; ULONG Value; ULONG Voltage; Device = (PSD_RK32_CONTEXT)Context; // // Phase 0 is an early initialization phase that happens after the // controller has been reset. It is used to gather capabilities and set // certain parameters in the hardware. // if (Phase == 0) { Mask = SD_DWC_CONTROL_FIFO_RESET | SD_DWC_CONTROL_CONTROLLER_RESET; SD_DWC_WRITE_REGISTER(Device, SdDwcControl, Mask); do { Value = SD_DWC_READ_REGISTER(Device, SdDwcControl); } while ((Value & Mask) != 0); // // Set the default burst length. // Value = (SD_DWC_BUS_MODE_BURST_LENGTH_16 << SD_DWC_BUS_MODE_BURST_LENGTH_SHIFT) | SD_DWC_BUS_MODE_FIXED_BURST; SD_DWC_WRITE_REGISTER(Device, SdDwcBusMode, Value); // // Set the default FIFO threshold. // SD_DWC_WRITE_REGISTER(Device, SdDwcFifoThreshold, SD_DWC_FIFO_THRESHOLD_DEFAULT); // // Set the default timeout. // SD_DWC_WRITE_REGISTER(Device, SdDwcTimeout, SD_DWC_TIMEOUT_DEFAULT); // // Set the voltages based on the supported values supplied when the // controller was created. // Voltage = SD_DWC_READ_REGISTER(Device, SdDwcUhs); Voltage &= ~SD_DWC_UHS_VOLTAGE_MASK; if ((Controller->Voltages & (SD_VOLTAGE_32_33 | SD_VOLTAGE_33_34)) == (SD_VOLTAGE_32_33 | SD_VOLTAGE_33_34)) { Voltage |= SD_DWC_UHS_VOLTAGE_3V3; } else if ((Controller->Voltages & (SD_VOLTAGE_165_195 | SD_VOLTAGE_18)) != 0) { Voltage |= SD_DWC_UHS_VOLTAGE_1V8; } else { Status = STATUS_DEVICE_NOT_CONNECTED; goto InitializeControllerEnd; } SD_DWC_WRITE_REGISTER(Device, SdDwcUhs, Voltage); // // Phase 1 happens right before the initialization command sequence is // about to begin. The clock and bus width have been program and the device // is just about read to go. // } else if (Phase == 1) { // // Turn on the power. // SD_DWC_WRITE_REGISTER(Device, SdDwcPower, SD_DWC_POWER_ENABLE); // // Set the interrupt mask, clear any pending state, and enable the // interrupts. // Controller->EnabledInterrupts = SD_DWC_INTERRUPT_DEFAULT_MASK; SD_DWC_WRITE_REGISTER(Device, SdDwcInterruptMask, SD_DWC_INTERRUPT_DEFAULT_MASK); SD_DWC_WRITE_REGISTER(Device, SdDwcInterruptStatus, SD_DWC_INTERRUPT_STATUS_ALL_MASK); Value = SD_DWC_READ_REGISTER(Device, SdDwcControl); Value |= SD_DWC_CONTROL_INTERRUPT_ENABLE; SD_DWC_WRITE_REGISTER(Device, SdDwcControl, Value); } Status = STATUS_SUCCESS; InitializeControllerEnd: return Status; } KSTATUS SdRk32ResetController ( PSD_CONTROLLER Controller, PVOID Context, ULONG Flags ) /*++ Routine Description: This routine performs a soft reset of the SD controller. Arguments: Controller - Supplies a pointer to the controller. Context - Supplies a context pointer passed to the SD/MMC library upon creation of the controller. Flags - Supplies a bitmask of reset flags. See SD_RESET_FLAG_* for definitions. Return Value: Status code. --*/ { ULONG BusMode; ULONG CardType; ULONG Control; PSD_RK32_CONTEXT Device; ULONGLONG Frequency; ULONG ResetMask; KSTATUS Status; ULONGLONG Timeout; ULONG Value; Device = (PSD_RK32_CONTEXT)Context; Device->InVoltageSwitch = FALSE; Frequency = HlQueryTimeCounterFrequency(); ResetMask = SD_DWC_CONTROL_FIFO_RESET | SD_DWC_CONTROL_DMA_RESET | SD_DWC_CONTROL_CONTROLLER_RESET; if ((Flags & SD_RESET_FLAG_ALL) != 0) { // // Power cycle the card. // SD_DWC_WRITE_REGISTER(Device, SdDwcPower, 0); HlBusySpin(10000); SdRk32SetRegulatorVoltage(Device, FALSE); SD_DWC_WRITE_REGISTER(Device, SdDwcPower, SD_DWC_POWER_ENABLE); HlBusySpin(10000); } CardType = SD_DWC_READ_REGISTER(Device, SdDwcCardType); Control = SD_DWC_READ_REGISTER(Device, SdDwcControl); Value = (Control | ResetMask) & ~(SD_DWC_CONTROL_DMA_ENABLE | SD_DWC_CONTROL_USE_INTERNAL_DMAC); SD_DWC_WRITE_REGISTER(Device, SdDwcControl, Value); Status = STATUS_TIMEOUT; Timeout = SdQueryTimeCounter(Controller) + (Frequency * SD_RK32_TIMEOUT); do { Value = SD_DWC_READ_REGISTER(Device, SdDwcControl); if ((Value & ResetMask) == 0) { Status = STATUS_SUCCESS; break; } } while (SdQueryTimeCounter(Controller) <= Timeout); if (!KSUCCESS(Status)) { return Status; } SD_DWC_WRITE_REGISTER(Device, SdDwcInterruptStatus, 0xFFFFFFFF); // // Wait for the DMA status to clear. // Status = STATUS_TIMEOUT; Timeout = SdQueryTimeCounter(Controller) + (Frequency * SD_RK32_TIMEOUT); do { Value = SD_DWC_READ_REGISTER(Device, SdDwcStatus); if ((Value & SD_DWC_STATUS_DMA_REQUEST) == 0) { Status = STATUS_SUCCESS; break; } } while (SdQueryTimeCounter(Controller) <= Timeout); if (!KSUCCESS(Status)) { return Status; } // // Reset the FIFO again. // ResetMask = SD_DWC_CONTROL_FIFO_RESET; Device = (PSD_RK32_CONTEXT)Context; Value = (Control | ResetMask) & ~(SD_DWC_CONTROL_DMA_ENABLE | SD_DWC_CONTROL_USE_INTERNAL_DMAC); SD_DWC_WRITE_REGISTER(Device, SdDwcControl, Value); Status = STATUS_TIMEOUT; Timeout = SdQueryTimeCounter(Controller) + (Frequency * SD_RK32_TIMEOUT); do { Value = SD_DWC_READ_REGISTER(Device, SdDwcControl); if ((Value & ResetMask) == 0) { Status = STATUS_SUCCESS; break; } } while (SdQueryTimeCounter(Controller) <= Timeout); if (!KSUCCESS(Status)) { return Status; } // // Reset the internal DMA. // BusMode = SD_DWC_READ_REGISTER(Device, SdDwcBusMode); Value = BusMode & ~(SD_DWC_BUS_MODE_FIXED_BURST | SD_DWC_BUS_MODE_IDMAC_ENABLE); Value |= SD_DWC_BUS_MODE_INTERNAL_DMA_RESET; SD_DWC_WRITE_REGISTER(Device, SdDwcBusMode, Value); Status = STATUS_TIMEOUT; Timeout = SdQueryTimeCounter(Controller) + (Frequency * SD_RK32_TIMEOUT); do { Value = SD_DWC_READ_REGISTER(Device, SdDwcBusMode); if ((Value & SD_DWC_BUS_MODE_INTERNAL_DMA_RESET) == 0) { Status = STATUS_SUCCESS; break; } } while (SdQueryTimeCounter(Controller) <= Timeout); if (!KSUCCESS(Status)) { return Status; } SD_DWC_WRITE_REGISTER(Device, SdDwcBusMode, BusMode); // // Restore the original control, and update the clock. // SD_DWC_WRITE_REGISTER(Device, SdDwcControl, Control); SD_DWC_WRITE_REGISTER(Device, SdDwcCardType, CardType); Status = SdRk32SetClockSpeed(Device, Controller->ClockSpeed); if (!KSUCCESS(Status)) { return Status; } Value = (Controller->ReadBlockLength << SD_DWC_CARD_READ_THRESHOLD_SIZE_SHIFT) | SD_DWC_CARD_READ_THRESHOLD_ENABLE; SD_DWC_WRITE_REGISTER(Device, SdDwcCardThresholdControl, Value); return STATUS_SUCCESS; } KSTATUS SdRk32SendCommand ( PSD_CONTROLLER Controller, PVOID Context, PSD_COMMAND Command ) /*++ Routine Description: This routine sends the given command to the card. Arguments: Controller - Supplies a pointer to the controller. Context - Supplies a context pointer passed to the SD/MMC library upon creation of the controller. Command - Supplies a pointer to the command parameters. Return Value: Status code. --*/ { ULONG CommandValue; PSD_RK32_CONTEXT Device; ULONG Flags; KSTATUS Status; ULONGLONG Timeout; ULONG Value; Device = (PSD_RK32_CONTEXT)Context; // // Clear any old interrupt status. // SD_DWC_WRITE_REGISTER(Device, SdDwcInterruptStatus, SD_DWC_INTERRUPT_STATUS_ALL_MASK); SdRk32SetDmaInterrupts(Controller, Device, Command->Dma, Command->BufferSize); // // If the stop command is being sent, add the flag to make sure the current // data transfer stops and that this command does not wait for the previous // data to complete. Otherwise, wait for the previous data to complete. // if ((Command->Command == SdCommandStopTransmission) && (Command->ResponseType != SD_RESPONSE_R1B)) { Flags = SD_DWC_COMMAND_STOP_ABORT; } else { Flags = SD_DWC_COMMAND_WAIT_PREVIOUS_DATA_COMPLETE; if (Command->Command == SdCommandReset) { Flags |= SD_DWC_COMMAND_SEND_INITIALIZATION; // // For the voltage switch command, disable low power clock mode and set // the required flag in the CMD register. // } else if (Command->Command == SdCommandVoltageSwitch) { Value = SD_DWC_READ_REGISTER(Device, SdDwcClockEnable); Value &= ~SD_DWC_CLOCK_ENABLE_LOW_POWER; SD_DWC_WRITE_REGISTER(Device, SdDwcClockEnable, Value); Status = SdRk32UpdateClock(Device); if (!KSUCCESS(Status)) { goto SendCommandEnd; } Device->InVoltageSwitch = TRUE; Flags |= SD_DWC_COMMAND_VOLT_SWITCH; } // // Wait for the FIFO to become empty command to complete. // Timeout = 0; Value = SD_DWC_READ_REGISTER(Device, SdDwcStatus); if ((Value & SD_DWC_STATUS_FIFO_EMPTY) == 0) { Value = SD_DWC_READ_REGISTER(Device, SdDwcControl); Value |= SD_DWC_CONTROL_FIFO_RESET; SD_DWC_WRITE_REGISTER(Device, SdDwcControl, Value); Status = STATUS_TIMEOUT; do { Value = SD_DWC_READ_REGISTER(Device, SdDwcControl); if ((Value & SD_DWC_CONTROL_FIFO_RESET) == 0) { Status = STATUS_SUCCESS; break; } else if (Timeout == 0) { Timeout = SdQueryTimeCounter(Controller) + Controller->Timeout; } } while (SdQueryTimeCounter(Controller) <= Timeout); if (!KSUCCESS(Status)) { goto SendCommandEnd; } } // // Also wait for the controller to stop being busy from the last // command. This comes into play on writes that use internal DMA. The // state machine remains busy despite the transfer completion // interrupt. // if ((Value & SD_DWC_STATUS_DATA_BUSY) != 0) { Status = STATUS_TIMEOUT; Timeout = SdQueryTimeCounter(Controller) + Controller->Timeout; do { Value = SD_DWC_READ_REGISTER(Device, SdDwcStatus); if ((Value & SD_DWC_STATUS_DATA_BUSY) == 0) { Status = STATUS_SUCCESS; break; } } while (SdQueryTimeCounter(Controller) <= Timeout); if (!KSUCCESS(Status)) { goto SendCommandEnd; } } } // // Set up the response flags. // if ((Command->ResponseType & SD_RESPONSE_PRESENT) != 0) { if ((Command->ResponseType & SD_RESPONSE_136_BIT) != 0) { Flags |= SD_DWC_COMMAND_LONG_RESPONSE; } Flags |= SD_DWC_COMMAND_RESPONSE_EXPECTED; } // // Set up the remainder of the command flags. // if ((Command->ResponseType & SD_RESPONSE_VALID_CRC) != 0) { Flags |= SD_DWC_COMMAND_CHECK_RESPONSE_CRC; } // // If there's a data buffer, program the block count. // if (Command->BufferSize != 0) { Flags |= SD_DWC_COMMAND_DATA_EXPECTED; if (Command->Write != FALSE) { Flags |= SD_DWC_COMMAND_WRITE; } else { Flags |= SD_DWC_COMMAND_READ; } // // If reading or writing multiple blocks, the block size register // should be set to the default block size and the byte count should be // a multiple of the block size. // if ((Command->Command == SdCommandReadMultipleBlocks) || (Command->Command == SdCommandWriteMultipleBlocks)) { if (((Controller->HostCapabilities & SD_MODE_AUTO_CMD12) != 0) && (Controller->SendStop != FALSE)) { Flags |= SD_DWC_COMMAND_SEND_AUTO_STOP; } SD_DWC_WRITE_REGISTER(Device, SdDwcBlockSize, SD_RK32_BLOCK_SIZE); SD_DWC_WRITE_REGISTER(Device, SdDwcByteCount, Command->BufferSize); // // Otherwise set the block size to total number of bytes to be // processed. // } else { SD_DWC_WRITE_REGISTER(Device, SdDwcBlockSize, Command->BufferSize); SD_DWC_WRITE_REGISTER(Device, SdDwcByteCount, Command->BufferSize); } } // // Internal DMA better be enabled if this is a DMA command. // ASSERT((Command->Dma == FALSE) || (Command->BufferSize == 0) || (((SD_DWC_READ_REGISTER(Device, SdDwcBusMode) & SD_DWC_BUS_MODE_IDMAC_ENABLE) != 0) && ((SD_DWC_READ_REGISTER(Device, SdDwcControl) & SD_DWC_CONTROL_USE_INTERNAL_DMAC) != 0))); SD_DWC_WRITE_REGISTER(Device, SdDwcTimeout, 0xFFFFFFFF); // // Write the command argument. // SD_DWC_WRITE_REGISTER(Device, SdDwcCommandArgument, Command->CommandArgument); // // Set the command and wait for it to be accepted. // CommandValue = (Command->Command << SD_DWC_COMMAND_INDEX_SHIFT) & SD_DWC_COMMAND_INDEX_MASK; CommandValue |= SD_DWC_COMMAND_START | SD_DWC_COMMAND_USE_HOLD_REGISTER | Flags; SD_DWC_WRITE_REGISTER(Device, SdDwcCommand, CommandValue); // // If this was a DMA command, just let it sail away. // if (Command->Dma != FALSE) { Status = STATUS_SUCCESS; goto SendCommandEnd; } ASSERT(Controller->EnabledInterrupts == SD_DWC_INTERRUPT_DEFAULT_MASK); Status = STATUS_TIMEOUT; Timeout = 0; do { Value = SD_DWC_READ_REGISTER(Device, SdDwcCommand); if ((Value & SD_DWC_COMMAND_START) == 0) { Status = STATUS_SUCCESS; break; } else if (Timeout == 0) { Timeout = SdQueryTimeCounter(Controller) + Controller->Timeout; } } while (SdQueryTimeCounter(Controller) <= Timeout); if (!KSUCCESS(Status)) { goto SendCommandEnd; } // // Check the interrupt status. Voltage switch commands set a specific // status bit, all other commands set command done. // Status = STATUS_TIMEOUT; do { Value = SD_DWC_READ_REGISTER(Device, SdDwcInterruptStatus); if (Command->Command == SdCommandVoltageSwitch) { if ((Value & SD_DWC_INTERRUPT_STATUS_VOLT_SWITCH) != 0) { Status = STATUS_SUCCESS; break; } } else if ((Value & SD_DWC_INTERRUPT_STATUS_COMMAND_DONE) != 0) { Status = STATUS_SUCCESS; break; } if (Timeout == 0) { Timeout = SdQueryTimeCounter(Controller) + Controller->Timeout; } } while (SdQueryTimeCounter(Controller) <= Timeout); if (!KSUCCESS(Status)) { goto SendCommandEnd; } if ((Value & SD_DWC_INTERRUPT_STATUS_ERROR_RESPONSE_TIMEOUT) != 0) { SD_DWC_WRITE_REGISTER(Device, SdDwcInterruptStatus, SD_DWC_INTERRUPT_STATUS_ALL_MASK); SdRk32ResetController(Controller, Context, SD_RESET_FLAG_COMMAND_LINE); Status = STATUS_TIMEOUT; goto SendCommandEnd; } else if ((Value & SD_DWC_INTERRUPT_STATUS_COMMAND_ERROR_MASK) != 0) { SD_DWC_WRITE_REGISTER(Device, SdDwcInterruptStatus, SD_DWC_INTERRUPT_STATUS_ALL_MASK); Status = STATUS_DEVICE_IO_ERROR; goto SendCommandEnd; } // // Acknowledge the completed command. // SD_DWC_WRITE_REGISTER(Device, SdDwcInterruptStatus, Value); // // Get the response if there is one. // if ((Command->ResponseType & SD_RESPONSE_PRESENT) != 0) { if ((Command->ResponseType & SD_RESPONSE_136_BIT) != 0) { Command->Response[3] = SD_DWC_READ_REGISTER(Device, SdDwcResponse0); Command->Response[2] = SD_DWC_READ_REGISTER(Device, SdDwcResponse1); Command->Response[1] = SD_DWC_READ_REGISTER(Device, SdDwcResponse2); Command->Response[0] = SD_DWC_READ_REGISTER(Device, SdDwcResponse3); if ((Controller->HostCapabilities & SD_MODE_RESPONSE136_SHIFTED) != 0) { Command->Response[0] = (Command->Response[0] << 8) | ((Command->Response[1] >> 24) & 0xFF); Command->Response[1] = (Command->Response[1] << 8) | ((Command->Response[2] >> 24) & 0xFF); Command->Response[2] = (Command->Response[2] << 8) | ((Command->Response[3] >> 24) & 0xFF); Command->Response[3] = Command->Response[3] << 8; } } else { Command->Response[0] = SD_DWC_READ_REGISTER(Device, SdDwcResponse0); } } // // Read/write the data. // if (Command->BufferSize != 0) { if (Command->Write != FALSE) { Status = SdRk32WriteData(Controller, Context, Command->BufferVirtual, Command->BufferSize); } else { Status = SdRk32ReadData(Controller, Context, Command->BufferVirtual, Command->BufferSize); } if (!KSUCCESS(Status)) { goto SendCommandEnd; } } Status = STATUS_SUCCESS; SendCommandEnd: return Status; } KSTATUS SdRk32GetSetBusWidth ( PSD_CONTROLLER Controller, PVOID Context, BOOL Set ) /*++ Routine Description: This routine gets or sets the controller's bus width. The bus width is stored in the controller structure. Arguments: Controller - Supplies a pointer to the controller. Context - Supplies a context pointer passed to the SD/MMC library upon creation of the controller. Set - Supplies a boolean indicating whether the bus width should be queried or set. Return Value: Status code. --*/ { PSD_RK32_CONTEXT Device; ULONG Value; Device = (PSD_RK32_CONTEXT)Context; if (Set != FALSE) { switch (Controller->BusWidth) { case 1: Value = SD_DWC_CARD_TYPE_1_BIT_WIDTH; break; case 4: Value = SD_DWC_CARD_TYPE_4_BIT_WIDTH; break; case 8: Value = SD_DWC_CARD_TYPE_8_BIT_WIDTH; break; default: RtlDebugPrint("SDRK32: Invalid bus width %d.\n", Controller->BusWidth); ASSERT(FALSE); return STATUS_INVALID_CONFIGURATION; } SD_DWC_WRITE_REGISTER(Device, SdDwcCardType, Value); } else { Value = SD_DWC_READ_REGISTER(Device, SdDwcCardType); if ((Value & SD_DWC_CARD_TYPE_8_BIT_WIDTH) != 0) { Controller->BusWidth = 8; } else if ((Value & SD_DWC_CARD_TYPE_4_BIT_WIDTH) != 0) { Controller->BusWidth = 4; } else { Controller->BusWidth = 1; } } return STATUS_SUCCESS; } KSTATUS SdRk32GetSetClockSpeed ( PSD_CONTROLLER Controller, PVOID Context, BOOL Set ) /*++ Routine Description: This routine gets or sets the controller's clock speed. The clock speed is stored in the controller structure. Arguments: Controller - Supplies a pointer to the controller. Context - Supplies a context pointer passed to the SD/MMC library upon creation of the controller. Set - Supplies a boolean indicating whether the clock speed should be queried (FALSE) or set (TRUE). Return Value: Status code. --*/ { PSD_RK32_CONTEXT Device; Device = (PSD_RK32_CONTEXT)Context; if (Device->FundamentalClock == 0) { return STATUS_INVALID_CONFIGURATION; } // // Getting the clock speed is not implemented as the divisor math might not // work out precisely in reverse. // if (Set == FALSE) { return STATUS_NOT_SUPPORTED; } return SdRk32SetClockSpeed(Device, Controller->ClockSpeed); } KSTATUS SdRk32GetSetVoltage ( PSD_CONTROLLER Controller, PVOID Context, BOOL Set ) /*++ Routine Description: This routine gets or sets the bus voltage. The bus voltage is stored in the controller structure. Arguments: Controller - Supplies a pointer to the controller. Context - Supplies a context pointer passed to the SD/MMC library upon creation of the controller. Set - Supplies a boolean indicating whether the bus voltage should be queried (FALSE) or set (TRUE). Return Value: Status code. --*/ { PSD_RK32_CONTEXT Device; ULONG Mask; KSTATUS Status; ULONGLONG Timeout; ULONG Value; Device = Context; if (Set == FALSE) { Value = SD_DWC_READ_REGISTER(Device, SdDwcUhs); if ((Value & SD_DWC_UHS_VOLTAGE_1V8) != 0) { Controller->CurrentVoltage = SdVoltage1V8; } else { Controller->CurrentVoltage = SdVoltage3V3; } return STATUS_SUCCESS; } // // When the standard SD library sent CMD11, the following happened inside // send command: // * Low power clocking was disabled. // * The volt switch interrupt was waited on and cleared. // Now stop the SD clock. // SD_DWC_WRITE_REGISTER(Device, SdDwcClockEnable, 0); Status = SdRk32UpdateClock(Device); if (!KSUCCESS(Status)) { goto GetSetVoltageEnd; } if (Controller->CurrentVoltage != SdVoltage1V8) { Status = SdRk32SetRegulatorVoltage(Device, FALSE); if (!KSUCCESS(Status)) { goto GetSetVoltageEnd; } HlBusySpin(10000); Value = SD_DWC_CLOCK_ENABLE_ON | SD_DWC_CLOCK_ENABLE_LOW_POWER; SD_DWC_WRITE_REGISTER(Device, SdDwcClockEnable, Value); Status = SdRk32UpdateClock(Device); if (!KSUCCESS(Status)) { goto GetSetVoltageEnd; } return STATUS_SUCCESS; } ASSERT(Device->InVoltageSwitch != FALSE); // // Switch the voltage. // Status = SdRk32SetRegulatorVoltage(Device, TRUE); if (!KSUCCESS(Status)) { goto GetSetVoltageEnd; } // // Wait at least 5ms as per spec. // HlBusySpin(10000); // // Re-enable the clock. // SD_DWC_WRITE_REGISTER(Device, SdDwcClockEnable, SD_DWC_CLOCK_ENABLE_ON); Status = SdRk32UpdateClock(Device); if (!KSUCCESS(Status)) { goto GetSetVoltageEnd; } // // Wait another millisecond as per spec. // HlBusySpin(2000); // // The controller should have generated a volt switch and command done // interrupt if DAT[3:0] went high for a millisecond. // Timeout = 0; Mask = SD_DWC_INTERRUPT_STATUS_VOLT_SWITCH | SD_DWC_INTERRUPT_STATUS_COMMAND_DONE; Status = STATUS_TIMEOUT; do { Value = SD_DWC_READ_REGISTER(Device, SdDwcInterruptStatus); if ((Value & Mask) == Mask) { Status = STATUS_SUCCESS; break; } else if (Timeout == 0) { Timeout = SdQueryTimeCounter(Controller) + HlQueryTimeCounterFrequency(); } } while (SdQueryTimeCounter(Controller) <= Timeout); SD_DWC_WRITE_REGISTER(Device, SdDwcInterruptStatus, Value); if (!KSUCCESS(Status)) { goto GetSetVoltageEnd; } Mask = SD_RESET_FLAG_COMMAND_LINE | SD_RESET_FLAG_DATA_LINE; Status = SdRk32ResetController(Controller, Device, Mask); if (!KSUCCESS(Status)) { goto GetSetVoltageEnd; } Status = STATUS_SUCCESS; GetSetVoltageEnd: Device->InVoltageSwitch = FALSE; return Status; } KSTATUS SdRk32ReadData ( PSD_CONTROLLER Controller, PVOID Context, PVOID Data, ULONG Size ) /*++ Routine Description: This routine reads polled data from the SD controller. Arguments: Controller - Supplies a pointer to the controller. Context - Supplies a context pointer passed to the SD/MMC library upon creation of the controller. Data - Supplies a pointer to the buffer where the data will be read into. Size - Supplies the size in bytes. This must be a multiple of four bytes. Return Value: Status code. --*/ { PULONG Buffer32; ULONG BusyMask; ULONG Count; ULONG DataReadyMask; BOOL DataTransferOver; PSD_RK32_CONTEXT Device; ULONG Interrupts; ULONG IoIndex; KSTATUS Status; ULONGLONG Timeout; ULONGLONG TimeoutTicks; ULONG Value; ASSERT(IS_ALIGNED(Size, sizeof(ULONG)) != FALSE); Device = (PSD_RK32_CONTEXT)Context; DataTransferOver = FALSE; Buffer32 = (PULONG)Data; Size /= sizeof(ULONG); TimeoutTicks = HlQueryTimeCounterFrequency() * SD_RK32_TIMEOUT; while (Size != 0) { // // Get the interrupt status register. // Status = STATUS_SUCCESS; Timeout = SdQueryTimeCounter(Controller) + TimeoutTicks; do { Interrupts = SD_DWC_READ_REGISTER(Device, SdDwcInterruptStatus); if (Interrupts != 0) { Status = STATUS_SUCCESS; break; } } while (SdQueryTimeCounter(Controller) <= Timeout); if (!KSUCCESS(Status)) { return Status; } // // Reset the controller if any error bits are set. // if ((Interrupts & SD_DWC_INTERRUPT_STATUS_DATA_ERROR_MASK) != 0) { SdRk32ResetController(Controller, Context, SD_RESET_FLAG_DATA_LINE); return STATUS_DEVICE_IO_ERROR; } // // Check for received data status. If data is ready, the status // register holds the number of 32-bit elements to be read. // DataReadyMask = SD_DWC_INTERRUPT_STATUS_RECEIVE_FIFO_DATA_REQUEST; if ((Interrupts & DataReadyMask) != 0) { Count = SD_DWC_READ_REGISTER(Device, SdDwcStatus); Count = (Count & SD_DWC_STATUS_FIFO_COUNT_MASK) >> SD_DWC_STATUS_FIFO_COUNT_SHIFT; if (Count > Size) { Count = Size; } for (IoIndex = 0; IoIndex < Count; IoIndex += 1) { *Buffer32 = SD_DWC_READ_REGISTER(Device, SdDwcFifoBase); Buffer32 += 1; } Size -= Count; SD_DWC_WRITE_REGISTER(Device, SdDwcInterruptStatus, DataReadyMask); } // // Check for the transfer over bit. If it is set, then read the rest of // the bytes from the FIFO. // if ((Interrupts & SD_DWC_INTERRUPT_STATUS_DATA_TRANSFER_OVER) != 0) { for (IoIndex = 0; IoIndex < Size; IoIndex += 1) { *Buffer32 = SD_DWC_READ_REGISTER(Device, SdDwcFifoBase); Buffer32 += 1; } SD_DWC_WRITE_REGISTER(Device, SdDwcInterruptStatus, SD_DWC_INTERRUPT_STATUS_DATA_TRANSFER_OVER); Size = 0; DataTransferOver = TRUE; break; } } // // If the data transfer over interrupt has not yet been seen, wait for it // to be asserted. // if (DataTransferOver == FALSE) { Status = STATUS_SUCCESS; Timeout = SdQueryTimeCounter(Controller) + TimeoutTicks; do { Interrupts = SD_DWC_READ_REGISTER(Device, SdDwcInterruptStatus); if ((Interrupts & SD_DWC_INTERRUPT_STATUS_DATA_TRANSFER_OVER) != 0) { Status = STATUS_SUCCESS; break; } } while (SdQueryTimeCounter(Controller) <= Timeout); if (!KSUCCESS(Status)) { return Status; } SD_DWC_WRITE_REGISTER(Device, SdDwcInterruptStatus, SD_DWC_INTERRUPT_STATUS_DATA_TRANSFER_OVER); } // // Wait until the state machine and data stop being busy. // BusyMask = SD_DWC_STATUS_DATA_STATE_MACHINE_BUSY | SD_DWC_STATUS_DATA_BUSY; Status = STATUS_SUCCESS; Timeout = SdQueryTimeCounter(Controller) + TimeoutTicks; do { Value = SD_DWC_READ_REGISTER(Device, SdDwcStatus); if ((Value & BusyMask) == 0) { Status = STATUS_SUCCESS; break; } } while (SdQueryTimeCounter(Controller) <= Timeout); if (!KSUCCESS(Status)) { return Status; } return STATUS_SUCCESS; } KSTATUS SdRk32WriteData ( PSD_CONTROLLER Controller, PVOID Context, PVOID Data, ULONG Size ) /*++ Routine Description: This routine writes polled data to the SD controller. Arguments: Controller - Supplies a pointer to the controller. Context - Supplies a context pointer passed to the SD/MMC library upon creation of the controller. Data - Supplies a pointer to the buffer containing the data to write. Size - Supplies the size in bytes. This must be a multiple of 4 bytes. Return Value: Status code. --*/ { PULONG Buffer32; ULONG BusyMask; ULONG Count; ULONG DataRequestMask; BOOL DataTransferOver; PSD_RK32_CONTEXT Device; ULONG Interrupts; ULONG IoIndex; KSTATUS Status; ULONGLONG Timeout; ULONGLONG TimeoutTicks; ULONG Value; ASSERT(IS_ALIGNED(Size, sizeof(ULONG)) != FALSE); Device = (PSD_RK32_CONTEXT)Context; DataTransferOver = FALSE; Buffer32 = (PULONG)Data; Size /= sizeof(ULONG); TimeoutTicks = HlQueryTimeCounterFrequency() * SD_RK32_TIMEOUT; while (Size != 0) { // // Get the interrupt status register. // Status = STATUS_SUCCESS; Timeout = SdQueryTimeCounter(Controller) + TimeoutTicks; do { Interrupts = SD_DWC_READ_REGISTER(Device, SdDwcInterruptStatus); if (Interrupts != 0) { Status = STATUS_SUCCESS; break; } } while (SdQueryTimeCounter(Controller) <= Timeout); if (!KSUCCESS(Status)) { return Status; } // // Reset the controller if any error bits are set. // if ((Interrupts & SD_DWC_INTERRUPT_STATUS_DATA_ERROR_MASK) != 0) { SdRk32ResetController(Controller, Context, SD_RESET_FLAG_DATA_LINE); return STATUS_DEVICE_IO_ERROR; } // // If the controller is ready for data to be written, the number of // 4-byte elements consumed in the FIFO is stored in the status // register. The available bytes is the total FIFO size minus that // amount. // DataRequestMask = SD_DWC_INTERRUPT_STATUS_TRANSMIT_FIFO_DATA_REQUEST; if ((Interrupts & DataRequestMask) != 0) { Count = SD_DWC_READ_REGISTER(Device, SdDwcStatus); Count = (Count & SD_DWC_STATUS_FIFO_COUNT_MASK) >> SD_DWC_STATUS_FIFO_COUNT_SHIFT; Count = (SD_DWC_FIFO_DEPTH / sizeof(ULONG)) - Count; if (Count > Size) { Count = Size; } for (IoIndex = 0; IoIndex < Count; IoIndex += 1) { SD_DWC_WRITE_REGISTER(Device, SdDwcFifoBase, *Buffer32); Buffer32 += 1; } Size -= Count; SD_DWC_WRITE_REGISTER(Device, SdDwcInterruptStatus, DataRequestMask); } // // Check for the transfer over bit. If it is set, then exit. // if ((Interrupts & SD_DWC_INTERRUPT_STATUS_DATA_TRANSFER_OVER) != 0) { SD_DWC_WRITE_REGISTER(Device, SdDwcInterruptStatus, SD_DWC_INTERRUPT_STATUS_DATA_TRANSFER_OVER); Size = 0; DataTransferOver = TRUE; break; } } // // If the data transfer over interrupt has not yet been seen, wait for it // to be asserted. // if (DataTransferOver == FALSE) { Status = STATUS_SUCCESS; Timeout = SdQueryTimeCounter(Controller) + TimeoutTicks; do { Interrupts = SD_DWC_READ_REGISTER(Device, SdDwcInterruptStatus); if ((Interrupts & SD_DWC_INTERRUPT_STATUS_DATA_TRANSFER_OVER) != 0) { Status = STATUS_SUCCESS; break; } } while (SdQueryTimeCounter(Controller) <= Timeout); if (!KSUCCESS(Status)) { return Status; } SD_DWC_WRITE_REGISTER(Device, SdDwcInterruptStatus, SD_DWC_INTERRUPT_STATUS_DATA_TRANSFER_OVER); } // // Wait until the state machine and data stop being busy. // BusyMask = SD_DWC_STATUS_DATA_STATE_MACHINE_BUSY | SD_DWC_STATUS_DATA_BUSY; Status = STATUS_SUCCESS; Timeout = SdQueryTimeCounter(Controller) + TimeoutTicks; do { Value = SD_DWC_READ_REGISTER(Device, SdDwcStatus); if ((Value & BusyMask) == 0) { Status = STATUS_SUCCESS; break; } } while (SdQueryTimeCounter(Controller) <= Timeout); if (!KSUCCESS(Status)) { return Status; } return STATUS_SUCCESS; } KSTATUS SdRk32SetClockSpeed ( PSD_RK32_CONTEXT Device, SD_CLOCK_SPEED ClockSpeed ) /*++ Routine Description: This routine sets the controller's clock speed. This function is RockChip specific. Arguments: Device - Supplies a pointer to this SD RK32xx device. ClockSpeed - Supplies the desired clock speed in Hertz. Return Value: Status code. --*/ { ULONG Divisor; ULONGLONG Frequency; ULONG InputClock; KSTATUS Status; ULONGLONG Timeout; ULONG Value; // // TODO: Figure out why the RockChip SD fails occasionally and needs to be // slowed down here. // if (ClockSpeed > SdClock25MHz) { ClockSpeed = SdClock25MHz; } if (Device->FundamentalClock == 0) { return STATUS_INVALID_CONFIGURATION; } Frequency = HlQueryTimeCounterFrequency(); // // Wait for the card to not be busy. // Status = STATUS_TIMEOUT; Timeout = KeGetRecentTimeCounter() + (Frequency * SD_RK32_TIMEOUT); do { Value = SD_DWC_READ_REGISTER(Device, SdDwcStatus); if ((Value & SD_DWC_STATUS_DATA_BUSY) == 0) { Status = STATUS_SUCCESS; break; } } while (KeGetRecentTimeCounter() <= Timeout); if (!KSUCCESS(Status)) { return Status; } // // Disable all clocks. // SD_DWC_WRITE_REGISTER(Device, SdDwcClockEnable, 0); Status = SdRk32UpdateClock(Device); if (!KSUCCESS(Status)) { return Status; } // // Use the 24MHz clock if a really slow speed is desired. // InputClock = Device->FundamentalClock; if (ClockSpeed < (InputClock / (RK32_CRU_MAX_MMC_DIVISOR + 1))) { // // Select the raw 24MHz source, and set the DesignWare divider to 1 to // divide by 2. // InputClock = RK32_SDMMC_FREQUENCY_24MHZ / 2; ASSERT(ClockSpeed <= InputClock); SD_DWC_WRITE_REGISTER(Device, SdDwcClockDivider, 1); Value = (RK32_CRU_CLOCK_SELECT_24MHZ << RK32_CRU_CLOCK_SELECT_CLOCK_SHIFT); // // Use the general PLL. // } else { SD_DWC_WRITE_REGISTER(Device, SdDwcClockDivider, 0); Value = (RK32_CRU_CLOCK_SELECT_GENERAL_PLL << RK32_CRU_CLOCK_SELECT_CLOCK_SHIFT); } Divisor = InputClock / ClockSpeed; if (InputClock / Divisor > ClockSpeed) { Divisor += 1; } ASSERT(Divisor <= RK32_CRU_MAX_MMC_DIVISOR); // // Bits 16 and up must be set for the write to take effect. This is also // why read-modify-write is not needed. // Value |= (RK32_CRU_CLOCK_SELECT_CLOCK_MASK | RK32_CRU_CLOCK_SELECT_DIVIDER_MASK) << RK32_CRU_CLOCK_SELECT_PROTECT_SHIFT; Value |= Divisor; Value <<= Device->VendorData->ClockSelectShift; SD_RK32_WRITE_CRU(Device, Device->VendorData->ClockSelectOffset, Value); SD_DWC_WRITE_REGISTER(Device, SdDwcClockSource, SD_DWC_CLOCK_SOURCE_DIVIDER_0); Status = SdRk32UpdateClock(Device); if (!KSUCCESS(Status)) { return Status; } // // Enable the clocks in lower power mode. // SD_DWC_WRITE_REGISTER(Device, SdDwcClockEnable, (SD_DWC_CLOCK_ENABLE_LOW_POWER | SD_DWC_CLOCK_ENABLE_ON)); Status = SdRk32UpdateClock(Device); if (!KSUCCESS(Status)) { return Status; } return STATUS_SUCCESS; } VOID SdRk32SetDmaInterrupts ( PSD_CONTROLLER Controller, PSD_RK32_CONTEXT Device, BOOL Enable, ULONG BufferSize ) /*++ Routine Description: This routine enables or disables interrupts necessary to perform block I/O via DMA. It is assumed that the caller has synchronized disk access on this controller and there are currently no DMA or polled operations in flight. Arguments: Controller - Supplies a pointer to the controller. Device - Supplies a pointer to the RK32xx SD device context. Enable - Supplies a boolean indicating if the DMA interrupts are to be enabled (TRUE) or disabled (FALSE). BufferSize - Supplies the length of the DMA buffer. Return Value: None. --*/ { ULONG Value; // // Enable the interrupts for transfer completion so that DMA operations // can complete asynchronously. Unless, of course, the DMA interrupts are // already enabled. // if (Enable != FALSE) { Value = Controller->EnabledInterrupts | SD_DWC_INTERRUPT_ERROR_MASK; Value &= ~(SD_DWC_INTERRUPT_MASK_DATA_TRANSFER_OVER | SD_DWC_INTERRUPT_MASK_COMMAND_DONE); if (BufferSize != 0) { Value |= SD_DWC_INTERRUPT_MASK_DATA_TRANSFER_OVER; } else { Value |= SD_DWC_INTERRUPT_MASK_COMMAND_DONE; } // // Disable the DMA interrupts so that they do not interfere with polled I/O // attempts to check the transfer status. Do nothing if the DMA interrupts // are disabled. // } else { Value = Controller->EnabledInterrupts & ~(SD_DWC_INTERRUPT_MASK_DATA_TRANSFER_OVER | SD_DWC_INTERRUPT_MASK_COMMAND_DONE | SD_DWC_INTERRUPT_ERROR_MASK); } if (Value != Controller->EnabledInterrupts) { Controller->EnabledInterrupts = Value; SD_DWC_WRITE_REGISTER(Device, SdDwcInterruptMask, Controller->EnabledInterrupts); } return; } KSTATUS SdRk32UpdateClock ( PSD_RK32_CONTEXT Device ) /*++ Routine Description: This routine performs a clock update, activating the configuration when the clock divisor, enable, or source registers are changed. Arguments: Device - Supplies a pointer to this SD RK32xx device. Return Value: Status code. --*/ { KSTATUS Status; ULONGLONG Timeout; ULONG Value; Value = SD_DWC_COMMAND_START | SD_DWC_COMMAND_UPDATE_CLOCK_REGISTERS | SD_DWC_COMMAND_WAIT_PREVIOUS_DATA_COMPLETE; if (Device->InVoltageSwitch != FALSE) { Value |= SD_DWC_COMMAND_VOLT_SWITCH; } SD_DWC_WRITE_REGISTER(Device, SdDwcCommand, Value); Status = STATUS_TIMEOUT; Timeout = KeGetRecentTimeCounter() + (HlQueryTimeCounterFrequency() * SD_RK32_TIMEOUT); do { Value = SD_DWC_READ_REGISTER(Device, SdDwcCommand); if ((Value & SD_DWC_COMMAND_START) == 0) { Status = STATUS_SUCCESS; break; } } while (KeGetRecentTimeCounter() <= Timeout); if (!KSUCCESS(Status)) { return Status; } return Status; } VOID SdRk32Rk808InterfaceNotificationCallback ( PVOID Context, PDEVICE Device, PVOID InterfaceBuffer, ULONG InterfaceBufferSize, BOOL Arrival ) /*++ Routine Description: This routine is called to notify listeners that an RK808 interface has arrived or departed. Arguments: Context - Supplies the caller's context pointer, supplied when the caller requested interface notifications. Device - Supplies a pointer to the device exposing or deleting the interface. InterfaceBuffer - Supplies a pointer to the interface buffer of the interface. InterfaceBufferSize - Supplies the buffer size. Arrival - Supplies TRUE if a new interface is arriving, or FALSE if an interface is departing. Return Value: None. --*/ { PSD_RK32_CONTEXT SdDevice; SdDevice = Context; if (InterfaceBufferSize == sizeof(INTERFACE_RK808)) { if (Arrival != FALSE) { ASSERT(SdDevice->Rk808 == NULL); SdDevice->Rk808 = InterfaceBuffer; } else { SdDevice->Rk808 = NULL; } } return; } KSTATUS SdRk32SetRegulatorVoltage ( PSD_RK32_CONTEXT Device, BOOL LowVoltage ) /*++ Routine Description: This routine sets the regulator voltage of the SD bus pins. Arguments: Device - Supplies a pointer to the device context. LowVoltage - Supplies a boolean indicating whether to set the bus voltage to 1.8V (TRUE) or 3.3V (FALSE). Return Value: Status code. --*/ { RK808_LDO_CONFIGURATION Configuration; KSTATUS Status; ULONG Value; if ((Device->Ldo != 0) && (Device->Rk808 != NULL)) { RtlZeroMemory(&Configuration, sizeof(RK808_LDO_CONFIGURATION)); Configuration.Flags = RK808_LDO_ENABLED | RK808_LDO_OFF_IN_SLEEP; Configuration.ActiveVoltage = 3300; if (LowVoltage != FALSE) { Configuration.ActiveVoltage = 1800; } Status = Device->Rk808->SetLdo(Device->Rk808, Device->Ldo, &Configuration); if (!KSUCCESS(Status)) { return Status; } } Value = SD_DWC_READ_REGISTER(Device, SdDwcUhs); if (LowVoltage != FALSE) { Value |= SD_DWC_UHS_VOLTAGE_1V8; } else { Value = 0; } SD_DWC_WRITE_REGISTER(Device, SdDwcUhs, Value); return Status; }