diff --git a/README.md b/README.md index aeebdbd..527ffd8 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ This repository contains drivers for RK35xx-based platforms, with a focus on RK3 |USB 2.0 & 1.1|usbehci (Inbox)|🔴 Not working|Windows bugchecks if enabled. USBOHCI driver for USB 1.1 is missing in ARM64 builds.| |PCIe 3.0 & 2.1|pci (Inbox)|🔴 Not working|MSI & ITS silicon bugs, bad ACPI descriptors| |SATA|mshdc (Inbox)|🔴 Not working|If enabled, drive enumerates with no IDs and hangs.| -|eMMC||🔴 Not working|| +|eMMC|[dwcsdhc](https://github.com/worproject/Rockchip-Windows-Drivers/tree/master/drivers/sd/dwcsdhc)|🟢 Working|| |SD/SDIO||🔴 Not working|| |CPU frequency scaling||🔴 Not working|Clocks limited at values set by UEFI.| |HDMI output|MSBDD (Inbox)|🟢 Working|Single display with mode limited at 1080p 60 Hz, provided by UEFI GOP.| diff --git a/build/RockchipDrivers.sln b/build/RockchipDrivers.sln index 6573771..6577a9b 100644 --- a/build/RockchipDrivers.sln +++ b/build/RockchipDrivers.sln @@ -3,8 +3,21 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.4.33205.214 MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dwcsdhc", "..\drivers\sd\dwcsdhc\dwcsdhc.vcxproj", "{10452736-7C4F-4206-94F9-AD634213C9C4}" EndProject Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM64 = Debug|ARM64 + Release|ARM64 = Release|ARM64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {10452736-7C4F-4206-94F9-AD634213C9C4}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {10452736-7C4F-4206-94F9-AD634213C9C4}.Debug|ARM64.Build.0 = Debug|ARM64 + {10452736-7C4F-4206-94F9-AD634213C9C4}.Debug|ARM64.Deploy.0 = Debug|ARM64 + {10452736-7C4F-4206-94F9-AD634213C9C4}.Release|ARM64.ActiveCfg = Release|ARM64 + {10452736-7C4F-4206-94F9-AD634213C9C4}.Release|ARM64.Build.0 = Release|ARM64 + {10452736-7C4F-4206-94F9-AD634213C9C4}.Release|ARM64.Deploy.0 = Release|ARM64 + EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection diff --git a/drivers/sd/dwcsdhc/README.md b/drivers/sd/dwcsdhc/README.md new file mode 100644 index 0000000..19629b4 --- /dev/null +++ b/drivers/sd/dwcsdhc/README.md @@ -0,0 +1,20 @@ +# Synopsys DesignWare SD/eMMC Host Controller Driver +This is a driver for the DesignWare Core SD/eMMC Host Controller found in RK35xx SoCs. + +It is a miniport that works in conjunction with sdport.sys, which implements the actual SD/SDIO/eMMC protocols and WDM interfaces. + +## Hardware details +The controller itself is SDHCI-compliant, with some peculiarities and a few more on Rockchip's implementation: +- HS400 has a custom setting. +- the clock divider is not functional, requiring clock rate changes via CRU instead. +- the PHY Delay Lines (DLL) need to be configured depending on bus speed mode, especially for HS200/HS400(ES). + +## Supported features +- ADMA2 transfers +- up to HS SDR52 speed mode (52 MB/s) + +## To-do +* HS400 Enhanced Strobe (the other modes are not planned) + +## Credits +The driver is based on Microsoft's [sdhc sample](https://github.com/microsoft/Windows-driver-samples/tree/main/sd/miniport/sdhc) with a few fixes from [bcm2836sdhc](https://github.com/raspberrypi/windows-drivers/tree/master/drivers/sd/bcm2836/bcm2836sdhc) in regard to request completion and interrupts handling. diff --git a/drivers/sd/dwcsdhc/dwcsdhc.cpp b/drivers/sd/dwcsdhc/dwcsdhc.cpp new file mode 100644 index 0000000..f6b497a --- /dev/null +++ b/drivers/sd/dwcsdhc/dwcsdhc.cpp @@ -0,0 +1,3607 @@ +/*++ + +Copyright (c) 2002-2014 Microsoft Corporation +Copyright (c) 2014 Greg Garbern (greggar) +Copyright (c) 2023 Mario Bălănică + +Module Name: + + dwcsdhc.c + +Abstract: + + This file contains the code that interfaces with standard host controller + implementations. + +Environment: + + Kernel mode only. + +--*/ + +#include "precomp.h" +#pragma hdrstop +#include + +#include "dwcsdhc.h" + +static PDRIVER_OBJECT s_pDriverObject; + +INIT_SEGMENT_BEGIN; //====================================================== + +//----------------------------------------------------------------------------- +// SlotExtension routines. +//----------------------------------------------------------------------------- + +NTSTATUS +DriverEntry( + _In_ PVOID DriverObject, + _In_ PVOID RegistryPath + ) + +/*++ + +Routine Description: + + This routine is the entry point for the standard sdhsot miniport driver. + +Arguments: + + DriverObject - DriverObject for the standard host controller. + + RegistryPath - Registry path for this standard host controller. + +Return Value: + + NTSTATUS + +--*/ + +{ + s_pDriverObject = (PDRIVER_OBJECT)DriverObject; + + ExInitializeDriverRuntime(0); + + SDPORT_INITIALIZATION_DATA InitializationData; + + RtlZeroMemory(&InitializationData, sizeof(InitializationData)); + InitializationData.StructureSize = sizeof(InitializationData); + + // + // Initialize the entry points/callbacks for the miniport interface. + // + + InitializationData.GetSlotCount = SdhcGetSlotCount; + InitializationData.GetSlotCapabilities = SdhcGetSlotCapabilities; + InitializationData.Initialize = SdhcSlotInitialize; + InitializationData.IssueBusOperation = SdhcSlotIssueBusOperation; + InitializationData.GetCardDetectState = SdhcSlotGetCardDetectState; + InitializationData.GetWriteProtectState = SdhcSlotGetWriteProtectState; + InitializationData.Interrupt = SdhcSlotInterrupt; + InitializationData.IssueRequest = SdhcSlotIssueRequest; + InitializationData.GetResponse = SdhcSlotGetResponse; + InitializationData.ToggleEvents = SdhcSlotToggleEvents; + InitializationData.ClearEvents = SdhcSlotClearEvents; + InitializationData.RequestDpc = SdhcRequestDpc; + InitializationData.SaveContext = SdhcSaveContext; + InitializationData.RestoreContext = SdhcRestoreContext; + InitializationData.PowerControlCallback = SdhcPoFxPowerControlCallback; + InitializationData.Cleanup = SdhcCleanup; + + // + // Provide the number of slots and their size. + // + + InitializationData.PrivateExtensionSize = sizeof(SDHC_EXTENSION); + InitializationData.CrashdumpSupported = TRUE; + + // + // Hook up the IRP dispatch routines. + // + + return SdPortInitialize(DriverObject, RegistryPath, &InitializationData); +} + +INIT_SEGMENT_END; //====================================================== +NONPAGED_SEGMENT_BEGIN; //====================================================== + +NTSTATUS +SdhcGetSlotCount( + _In_ PSD_MINIPORT Miniport, + _Out_ PUCHAR SlotCount + ) + +/*++ + +Routine Description: + + Return the number of slots present on this controller. + +Arguments: + + Argument - Functional device object for this controller. + +Return value: + + STATUS_UNSUCCESSFUL - PCI config space was unable to be queried. + + STATUS_INVALID_PARAMETER - Invalid underlying bus type for the miniport. + + STATUS_SUCCESS - SlotCount returned properly. + +--*/ + +{ + + SDPORT_BUS_TYPE BusType; + UCHAR Data; + NTSTATUS Status; + + *SlotCount = 0; + BusType = Miniport->ConfigurationInfo.BusType; + switch (BusType) { + case SdBusTypeAcpi: + + // + // We don't currently have a mechanism to query the slot count for ACPI + // enumerated host controllers. Default to one slot. + // + + *SlotCount = 1; + Status = STATUS_SUCCESS; + break; + + case SdBusTypePci: + Data = 0; + Status = SdPortGetPciConfigSpace(Miniport, + SDHC_PCICFG_SLOT_INFORMATION, + &Data, + sizeof(Data)); + + if (!NT_SUCCESS(Status)) { + return Status; + } + + *SlotCount = (Data >> 4) + 1; + if ((*SlotCount) > 6) { + + NT_ASSERT((*SlotCount) <= 6); + + Status = STATUS_UNSUCCESSFUL; + } + + SdhcInitializePciConfigSpace(Miniport); + Status = STATUS_SUCCESS; + break; + + default: + + NT_ASSERT((BusType == SdBusTypeAcpi) || (BusType == SdBusTypePci)); + + *SlotCount = 1; + Status = STATUS_INVALID_PARAMETER; + break; + } + + return Status; +} + +VOID +SdhcGetSlotCapabilities( + _In_ PVOID PrivateExtension, + _Out_ PSDPORT_CAPABILITIES Capabilities + ) + +/*++ + +Routine Description: + + Override for miniport to provide host register mapping information if the + memory range provideed by the underlying bus isn't sufficient. + +Arguments: + + SlotExtension - SlotExtension interface between sdhost and this miniport. + + Capabilities - Miniport provided capabilities. + +Return value: + + STATUS_SUCCESS - Capabilities returned successfully. + +--*/ + +{ + + PSDHC_EXTENSION SdhcExtension; + + SdhcExtension = (PSDHC_EXTENSION) PrivateExtension; + RtlCopyMemory(Capabilities, + &SdhcExtension->Capabilities, + sizeof(SdhcExtension->Capabilities)); +} + +BOOLEAN +FindPhysicalBaseInCmResList( + _In_ PHYSICAL_ADDRESS PhysicalBase, + _In_ PCM_RESOURCE_LIST ResListTranslated + ) +{ + PCM_FULL_RESOURCE_DESCRIPTOR FullResDescriptor; + FullResDescriptor = ResListTranslated->List; + + for (ULONG FullResIndex = 0; FullResIndex < ResListTranslated->Count; FullResIndex++) { + + for (ULONG PartialResIndex = 0; + PartialResIndex < FullResDescriptor->PartialResourceList.Count; + PartialResIndex++) { + + PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialResDescriptor; + PartialResDescriptor = + FullResDescriptor->PartialResourceList.PartialDescriptors + PartialResIndex; + + switch (PartialResDescriptor->Type) + { + case CmResourceTypeMemory: + if (PartialResDescriptor->u.Memory.Length == SDHC_EXPECTED_ACPI_LENGTH + && PartialResDescriptor->u.Memory.Start.QuadPart == PhysicalBase.QuadPart) { + return TRUE; + } + break; + } + } + + FullResDescriptor = (PCM_FULL_RESOURCE_DESCRIPTOR)(FullResDescriptor->PartialResourceList.PartialDescriptors + + FullResDescriptor->PartialResourceList.Count); + } + + return FALSE; +} + +NTSTATUS +FindMiniportPdoByPhysicalBase( + _In_ PHYSICAL_ADDRESS PhysicalBase, + _Out_ PDEVICE_OBJECT* FoundPdo + ) +{ + NTSTATUS Status = STATUS_SUCCESS; + PDEVICE_OBJECT Fdo = NULL; + PDEVICE_OBJECT Pdo = NULL; + ULONG ResultLength = 0; + BOOLEAN FoundAddress = FALSE; + + *FoundPdo = NULL; + + for (Fdo = s_pDriverObject->DeviceObject; Fdo != NULL; Fdo = Fdo->NextDevice) { + Pdo = Fdo->DeviceObjectExtension->AttachedTo; + + Status = IoGetDeviceProperty(Pdo, DevicePropertyBootConfigurationTranslated, + 0, NULL, &ResultLength); + + if (Status != STATUS_BUFFER_TOO_SMALL) { + return Status; + } + + PCM_RESOURCE_LIST ResListTranslated; + ResListTranslated = (PCM_RESOURCE_LIST) ExAllocatePoolZero(NonPagedPool, + ResultLength, SDHC_ALLOC_TAG); + + if (ResListTranslated == NULL) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + Status = IoGetDeviceProperty(Pdo, DevicePropertyBootConfigurationTranslated, + ResultLength, ResListTranslated, &ResultLength); + + if (NT_SUCCESS(Status)) { + FoundAddress = FindPhysicalBaseInCmResList(PhysicalBase, ResListTranslated); + } + + ExFreePoolWithTag(ResListTranslated, SDHC_ALLOC_TAG); + + if (FoundAddress) { + break; + } + } + + if (FoundAddress) { + *FoundPdo = Pdo; + } else { + Status = STATUS_DEVICE_CONFIGURATION_ERROR; + } + + return Status; +} + +NTSTATUS +SdhcSlotInitialize( + _In_ PVOID PrivateExtension, + _In_ PHYSICAL_ADDRESS PhysicalBase, + _In_ PVOID VirtualBase, + _In_ ULONG Length, + _In_ BOOLEAN CrashdumpMode + ) + +/*++ + +Routine Description: + + Initialize the miniport for standard host controllers. + +Arguments: + + SlotExtension - SlotExtension interface between sdhost and this miniport. + + PhysicalBase - Physical base address of the host controller register space. + + VirtualBase - Mapped register space. + + Length - Length of the host register set in bytes. + + CrashdumpMode - whether this miniport is being used for crashdump. + +Return value: + + NTSTATUS + +--*/ + +{ + + PSDPORT_CAPABILITIES Capabilities; + SDHC_CAPABILITIES_REGISTER CapabilitiesReg; + SDHC_CAPABILITIES2_REGISTER Capabilities2Reg; + ULONG CurrentLimits; + ULONG CurrentLimitMax; + ULONG CurrentLimitMask; + ULONG CurrentLimitShift; + PSDHC_EXTENSION SdhcExtension; + USHORT SpecVersion; + NTSTATUS Status; + + SdhcExtension = (PSDHC_EXTENSION) PrivateExtension; + + // + // Find and save a pointer to this miniport's PDO in the device extension, + // we need it to issue ACPI driver calls. + // + + Status = FindMiniportPdoByPhysicalBase(PhysicalBase, &SdhcExtension->PdoPtr); + if (!NT_SUCCESS(Status)) { + return Status; + } + + // + // Initialize the SDHC_EXTENSION register space. + // + + SdhcExtension->PhysicalBaseAddress = PhysicalBase; + SdhcExtension->BaseAddress = VirtualBase; + SdhcExtension->BaseAddressSpaceSize = Length; + SdhcExtension->BaseAddressDebug = + (PSD_HOST_CONTROLLER_REGISTERS) VirtualBase; + + // + // Check whether the driver is in crashdump mode. + // + + SdhcExtension->CrashdumpMode = CrashdumpMode; + + // + // Initialize host capabilities. + // + + Capabilities = (PSDPORT_CAPABILITIES) &SdhcExtension->Capabilities; + CapabilitiesReg.AsUlong = + SdhcReadRegisterUlong(SdhcExtension, SDHC_CAPABILITIES); + + Capabilities2Reg.AsUlong = + SdhcReadRegisterUlong(SdhcExtension, SDHC_CAPABILITIES2); + + SpecVersion = SdhcReadRegisterUshort(SdhcExtension, SDHC_VERSION); + Capabilities->SpecVersion = SpecVersion & 0xFF; + Capabilities->MaximumOutstandingRequests = SDHC_MAX_OUTSTANDING_REQUESTS; + Capabilities->MaximumBlockSize = + (USHORT)(512 << CapabilitiesReg.MaxBlockLength); + + Capabilities->MaximumBlockCount = 0xFFFF; + Capabilities->BaseClockFrequencyKhz = + CapabilitiesReg.BaseClockFrequency * 1000; + + Capabilities->DmaDescriptorSize = + sizeof(SDHC_ADMA2_DESCRIPTOR_TABLE_ENTRY) + + (CapabilitiesReg.SystemBus64Support ? + sizeof(ULONGLONG) : sizeof(ULONG)); + + Capabilities->AlignmentRequirement = + (CapabilitiesReg.SystemBus64Support ? + sizeof(ULONGLONG) : sizeof(ULONG)) - 1; + + // + // For small transfers (SDIO) we want to use PIO for performance, + // for both reads and writes <= 64 bytes. + // + + Capabilities->PioTransferMaxThreshold = 64; + Capabilities->Flags.UsePioForRead = TRUE; + Capabilities->Flags.UsePioForWrite = TRUE; + + if (CapabilitiesReg.Adma2Support) { + Capabilities->Supported.ScatterGatherDma = 1; + } + + if (CapabilitiesReg.SystemBus64Support) { + Capabilities->Supported.Address64Bit = 1; + } + + if (CapabilitiesReg.Support8BitBus) { + Capabilities->Supported.BusWidth8Bit = 1; + } + + if (CapabilitiesReg.HighSpeedSupport) { + Capabilities->Supported.HighSpeed = 1; + } + + if (Capabilities2Reg.SDR50Support) { + Capabilities->Supported.SDR50 = 1; + Capabilities->Supported.SignalingVoltage18V = 1; + } + + if (Capabilities2Reg.DDR50Support) { + Capabilities->Supported.DDR50 = 1; + Capabilities->Supported.SignalingVoltage18V = 1; + } + + if (Capabilities2Reg.SDR104Support) { + Capabilities->Supported.SDR104 = 1; + Capabilities->Supported.SignalingVoltage18V = 1; + } + + Capabilities->Supported.HS200 = 0; + Capabilities->Supported.HS400 = 0; + + if (Capabilities2Reg.DriverTypeA) { + Capabilities->Supported.DriverTypeA = 1; + } + + if (Capabilities2Reg.DriverTypeC) { + Capabilities->Supported.DriverTypeC = 1; + } + + if (Capabilities2Reg.DriverTypeD) { + Capabilities->Supported.DriverTypeD = 1; + } + + Capabilities->Supported.DriverTypeB = 1; + + if (Capabilities2Reg.UseTuningForSDR50) { + Capabilities->Supported.TuningForSDR50 = 1; + } + + if (Capabilities2Reg.RetuningTimerCount != 0) { + Capabilities->TuningTimerCountInSeconds = + (1 << (Capabilities2Reg.RetuningTimerCount - 1)); + } + + if (Capabilities2Reg.RetuningModes == 0) { + Capabilities->Supported.SoftwareTuning = 1; + } + + Capabilities->Supported.AutoCmd12 = 1; + if (SpecVersion >= SDHC_SPEC_VERSION_3) { + Capabilities->Supported.AutoCmd23 = 1; + } + + if (CapabilitiesReg.Voltage18) { + Capabilities->Supported.Voltage18V; + } + + if (CapabilitiesReg.Voltage30) { + Capabilities->Supported.Voltage30V; + } + + if (CapabilitiesReg.Voltage33) { + Capabilities->Supported.Voltage33V; + } + + // + // Find the current limits suppported by the controller. + // + + CurrentLimitMask = 0; + CurrentLimitShift = 0; + if (Capabilities->Supported.Voltage33V) { + CurrentLimitMask = 0xFF; + CurrentLimitShift = 0; + + } else if (Capabilities->Supported.Voltage30V) { + CurrentLimitMask = 0xFF00; + CurrentLimitShift = 8; + + } else if (Capabilities->Supported.Voltage18V) { + CurrentLimitMask = 0xFF0000; + CurrentLimitShift = 16; + } + + CurrentLimits = SdhcReadRegisterUlong(SdhcExtension, SDHC_MAXIMUM_CURRENT); + CurrentLimitMax = + ((CurrentLimits & CurrentLimitMask) >> CurrentLimitShift) * 4; + + if (CurrentLimitMax >= 800) { + Capabilities->Supported.Limit800mA; + } + + if (CurrentLimitMax >= 600) { + Capabilities->Supported.Limit600mA; + } + + if (CurrentLimitMax >= 400) { + Capabilities->Supported.Limit400mA; + } + + if (CurrentLimitMax >= 200) { + Capabilities->Supported.Limit200mA; + } + + // + // The single active request. + // + + SdhcExtension->OutstandingRequest = NULL; + + return STATUS_SUCCESS; +} + +NTSTATUS +SdhcSlotIssueBusOperation( + _In_ PVOID PrivateExtension, + _In_ PSDPORT_BUS_OPERATION BusOperation + ) + +/*++ + +Routine Description: + + Issue host bus operation specified by BusOperation. + +Arguments: + + SlotExtension - SlotExtension interface between sdhost and this miniport. + + BusOperation - Bus operation to perform. + +Return value: + + NTSTATUS - Return value of the bus operation performed. + +--*/ + +{ + + PSDHC_EXTENSION SdhcExtension; + NTSTATUS Status; + + Status = STATUS_INVALID_PARAMETER; + SdhcExtension = (PSDHC_EXTENSION) PrivateExtension; + switch (BusOperation->Type) { + case SdResetHost: + Status = SdhcResetHost(SdhcExtension, + BusOperation->Parameters.ResetType); + + break; + + case SdSetClock: + Status = SdhcSetClock(SdhcExtension, + BusOperation->Parameters.FrequencyKhz); + + break; + + case SdSetVoltage: + Status = SdhcSetVoltage(SdhcExtension, + BusOperation->Parameters.Voltage); + + break; + + case SdSetBusWidth: + Status = SdhcSetBusWidth(SdhcExtension, + BusOperation->Parameters.BusWidth); + + break; + + case SdSetBusSpeed: + Status = SdhcSetSpeed(SdhcExtension, BusOperation->Parameters.BusSpeed); + break; + + case SdSetSignalingVoltage: + Status = + SdhcSetSignaling( + SdhcExtension, + (BOOLEAN)(BusOperation->Parameters.SignalingVoltage == + SdSignalingVoltage18)); + + break; + + case SdSetDriveStrength: + //Status = SdhcSetDriveStrength(SdhcExtension, + // BusOperation->Parameters.DriveStrength); + break; + + case SdSetDriverType: + //Status = SdhcSetDriverType(SdhcExtension, + // BusOperation->Parameters.DriverType); + + break; + + case SdSetPresetValue: + Status = SdhcSetPresetValue( + SdhcExtension, + BusOperation->Parameters.PresetValueEnabled); + + break; + + case SdSetBlockGapInterrupt: + Status = SdhcEnableBlockGapInterrupt( + SdhcExtension, + BusOperation->Parameters.BlockGapIntEnabled); + + break; + + case SdExecuteTuning: + Status = SdhcExecuteTuning(SdhcExtension); + break; + + default: + Status = STATUS_INVALID_PARAMETER; + break; + } + + return Status; +} + +BOOLEAN +SdhcSlotGetCardDetectState( + _In_ PVOID PrivateExtension + ) + +/*++ + +Routine Description: + + Determine whether a card is inserted in the slot. + +Arguments: + + SlotExtension - SlotExtension interface between sdhost and this miniport. + +Return value: + + TRUE - Card is inserted. + + FALSE - Slot is empty. + +--*/ + +{ + + PSDHC_EXTENSION SdhcExtension; + + SdhcExtension = (PSDHC_EXTENSION) PrivateExtension; + return SdhcIsCardInserted(SdhcExtension); +} + +BOOLEAN +SdhcSlotGetWriteProtectState( + _In_ PVOID PrivateExtension + ) + +/*++ + +Routine Description: + + Determine whether the slot write protection is engaged. + +Arguments: + + SlotExtension - SlotExtension interface between sdhost and this miniport. + +Return value: + + TRUE - Slot is write protected. + + FALSE - Slot is writable. + +--*/ + +{ + + PSDHC_EXTENSION SdhcExtension; + + SdhcExtension = (PSDHC_EXTENSION) PrivateExtension; + return SdhcIsWriteProtected(SdhcExtension); +} + +BOOLEAN +SdhcSlotInterrupt( + _In_ PVOID PrivateExtension, + _Out_ PULONG Events, + _Out_ PULONG Errors, + _Out_ PBOOLEAN CardChange, + _Out_ PBOOLEAN SdioInterrupt, + _Out_ PBOOLEAN Tuning + ) + +/*++ + +Routine Description: + + Level triggered DIRQL interrupt handler (ISR) for this controller. + +Arguments: + + SlotExtension - SlotExtension interface between sdhost and this miniport. + +Return value: + + Whether the interrupt is handled. + +--*/ + +{ + + PSDHC_EXTENSION SdhcExtension; + + SdhcExtension = (PSDHC_EXTENSION) PrivateExtension; + *Events = (ULONG) SdhcGetInterruptStatus(SdhcExtension); + + *Errors = 0; + *CardChange = FALSE; + *SdioInterrupt = FALSE; + *Tuning = FALSE; + + // + // If there aren't any events to handle, then we don't need to + // process anything. + // + + if (*Events == 0) { + return FALSE; + } + + if (*Events & SDHC_IS_ERROR_INTERRUPT) { + *Errors = (ULONG) SdhcGetErrorStatus(SdhcExtension); + } + + // + // If a card has changed, notify the port driver. + // + + if (*Events & SDHC_IS_CARD_DETECT) { + *CardChange = TRUE; + } + + // + // If we have an external SDIO interrupt, notify the port driver. + // + + if (*Events & SDHC_IS_CARD_INTERRUPT) { + *SdioInterrupt = TRUE; + } + + // + // If there's a tuning request, notify the port driver. + // + + if (*Events & SDHC_IS_TUNING_INTERRUPT) { + *Tuning = TRUE; + } + + // + // Acknowledge/clear interrupt status. Request completions will occur in + // the port driver's slot completion DPC. We need to make the members of + // SDPORT_REQUEST that only the port driver uses opaque to the miniport. + // See how Storport does this (if it does). + // + + SdhcAcknowledgeInterrupts(SdhcExtension, (USHORT) *Events); + *Events &= ~(SDHC_IS_CARD_DETECT | + SDHC_IS_CARD_INTERRUPT | + SDHC_IS_TUNING_INTERRUPT); + + return (BOOLEAN)((*Events != 0) || (*CardChange) || (*SdioInterrupt) || (*Tuning)); +} + +NTSTATUS +SdhcSlotIssueRequest( + _In_ PVOID PrivateExtension, + _In_ PSDPORT_REQUEST Request + ) + +/*++ + +Routine Description: + + Issue hardware request specified by Request. + +Arguments: + + PrivateExtension - This driver's device extension (SdhcExtension). + + Request - Request operation to perform. + +Return value: + + NTSTATUS - Return value of the request performed. + +--*/ + +{ + PSDHC_EXTENSION SdhcExtension; + NTSTATUS Status; + + SdhcExtension = (PSDHC_EXTENSION) PrivateExtension; + + // + // Mark the current request as being outstanding. + // + + if (InterlockedExchangePointer( + (void *volatile *) & SdhcExtension->OutstandingRequest, Request) != NULL) { + // Previous request in progress. + } + + // + // Dispatch the request based off of the request type. + // + + switch (Request->Type) { + case SdRequestTypeCommandNoTransfer: + case SdRequestTypeCommandWithTransfer: + Status = SdhcSendCommand(SdhcExtension, Request); + break; + + case SdRequestTypeStartTransfer: + Status = SdhcStartTransfer(SdhcExtension, Request); + break; + + default: + Status = STATUS_NOT_SUPPORTED; + break; + } + + return Status; +} + +VOID +SdhcSlotGetResponse( + _In_ PVOID PrivateExtension, + _In_ PSDPORT_COMMAND Command, + _Out_ PVOID ResponseBuffer + ) + +/*++ + +Routine Description: + + Return the response data for a given command back to the port driver. + +Arguments: + + PrivateExtension - This driver's device extension (SdhcExtension). + + Command - Command for which we're getting the response. + + ResponseBuffer - Response data for the given command. + +Return value: + + None. + +--*/ + +{ + + PSDHC_EXTENSION SdhcExtension; + NTSTATUS Status; + + SdhcExtension = (PSDHC_EXTENSION) PrivateExtension; + Status = SdhcGetResponse(SdhcExtension, Command, ResponseBuffer); + NT_ASSERT(NT_SUCCESS(Status)); +} + +VOID +SdhcRequestDpc( + _In_ PVOID PrivateExtension, + _Inout_ PSDPORT_REQUEST Request, + _In_ ULONG Events, + _In_ ULONG Errors + ) + +/*++ + +Routine Description: + + DPC for interrupts associated with the given request. + +Arguments: + + PrivateExtension - This driver's device extension (SdhcExtension). + + Request - Request operation to perform. + +Return value: + + NTSTATUS - Return value of the request performed. + +--*/ + +{ + PSDHC_EXTENSION SdhcExtension; + NTSTATUS Status; + + SdhcExtension = (PSDHC_EXTENSION)PrivateExtension; + + // + // Save current events, since we may not be waiting for them + // at this stage, but we may be on the next phase of the command + // processing. + // For instance with short data read requests, the transfer is probably + // completed by the time the command is done. + // If that case if we wait for that events after it has already arrived, + // we will fail with timeout. + // + + InterlockedOr((PLONG)&SdhcExtension->CurrentEvents, Events); + + // + // Check for out of sequence call? + // SDPORT does not maintain a request state, so we may get a request that + // has not been issued yet! + // + + if (InterlockedExchangePointer( + (void *volatile *) & SdhcExtension->OutstandingRequest, + SdhcExtension->OutstandingRequest) == NULL) { + return; + } + + // + // Clear the request's required events if they have completed. + // + + Request->RequiredEvents &= ~Events; + + // + // If there are errors, we need to fail whatever outstanding request + // was on the bus. Otherwise, the request succeeded. + // + + if (Errors) { + Request->RequiredEvents = 0; + InterlockedAnd((PLONG)&SdhcExtension->CurrentEvents, 0); + Status = SdhcConvertErrorToStatus((USHORT) Errors); + SdhcCompleteRequest(SdhcExtension, Request, Status); + + } else if (Request->RequiredEvents == 0) { + if (Request->Status != STATUS_MORE_PROCESSING_REQUIRED) { + Request->Status = STATUS_SUCCESS; + } + + SdhcCompleteRequest(SdhcExtension, Request, Request->Status); + } +} + +VOID +SdhcSlotToggleEvents( + _In_ PVOID PrivateExtension, + _In_ ULONG EventMask, + _In_ BOOLEAN Enable + ) + +/*++ + +Routine Description: + + Enable or disable the given event mask. + +Arguments: + + SlotExtension - SlotExtension interface between sdhost and this miniport. + + Events - The event mask to toggle. + + Enable - TRUE to enable, FALSE to disable. + +Return value: + + None. + +--*/ + +{ + + USHORT InterruptMask; + PSDHC_EXTENSION SdhcExtension; + + InterruptMask = SdhcConvertEventsToHwMask(EventMask); + SdhcExtension = (PSDHC_EXTENSION) PrivateExtension; + if (Enable) { + SdhcEnableInterrupt(SdhcExtension, + InterruptMask); + + } else { + SdhcDisableInterrupt(SdhcExtension, + InterruptMask); + } +} + +VOID +SdhcSlotClearEvents( + _In_ PVOID PrivateExtension, + _In_ ULONG EventMask + ) + +{ + + USHORT Interrupts; + PSDHC_EXTENSION SdhcExtension; + + SdhcExtension = (PSDHC_EXTENSION) PrivateExtension; + Interrupts = SdhcConvertEventsToHwMask(EventMask); + SdhcAcknowledgeInterrupts(SdhcExtension, Interrupts); +} + +VOID +SdhcSaveContext( + _In_ PVOID PrivateExtension + ) + +/*++ + +Routine Description: + + Save slot register context. + +Arguments: + + SlotExtension - SlotExtension interface between sdhost and this miniport. + +Return value: + + None. + +--*/ + +{ + UNREFERENCED_PARAMETER(PrivateExtension); +} + +VOID +SdhcRestoreContext( + _In_ PVOID PrivateExtension + ) + +/*++ + +Routine Description: + + Restore slot register context from a previously saved context. + +Arguments: + + SlotExtension - SlotExtension interface between sdhost and this miniport. + +Return value: + + None. + +--*/ + +{ + UNREFERENCED_PARAMETER(PrivateExtension); +} + +NTSTATUS +SdhcPoFxPowerControlCallback( + _In_ PSD_MINIPORT Miniport, + _In_ LPCGUID PowerControlCode, + _In_reads_bytes_opt_(InputBufferSize) PVOID InputBuffer, + _In_ SIZE_T InputBufferSize, + _Out_writes_bytes_opt_(OutputBufferSize) PVOID OutputBuffer, + _In_ SIZE_T OutputBufferSize, + _Out_opt_ PSIZE_T BytesReturned + ) + +/*++ + +Routine Description: + + Handle any PoFxPowerControl callbacks. + +Arguments: + + Miniport - Miniport interface for the controller. + + PowerControlCode - GUID defining a platform-specific PoFxPowerControl + method. + + InputBuffer - Buffer containing any input arguments. + + InputBufferSize - Size of InputBuffer in bytes. + + OutputBuffer - Buffer containing any output results. + + OutputBufferSize - Size of OutputBuffer in bytes. + + BytesReturned - Number of bytes returned. + +Return value: + + NTSTATUS + +--*/ + +{ + + UNREFERENCED_PARAMETER(Miniport); + UNREFERENCED_PARAMETER(PowerControlCode); + UNREFERENCED_PARAMETER(InputBuffer); + UNREFERENCED_PARAMETER(InputBufferSize); + UNREFERENCED_PARAMETER(OutputBuffer); + UNREFERENCED_PARAMETER(OutputBufferSize); + UNREFERENCED_PARAMETER(BytesReturned); + + return STATUS_NOT_IMPLEMENTED; +} + +VOID +SdhcCleanup( + _In_ PSD_MINIPORT Miniport + ) + +/*++ + +Routine Description: + + Cleanup any memory allocations done during the lifetime of the driver. + +Arguments: + + Miniport - Miniport interface for the controller. + +Return value: + + NTSTATUS + +--*/ + +{ + UNREFERENCED_PARAMETER(Miniport); + + s_pDriverObject = NULL; +} + +//----------------------------------------------------------------------------- +// Host routine implementations. +//----------------------------------------------------------------------------- + +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +SdhcResetHost( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ SDPORT_RESET_TYPE ResetType + ) + +/*++ + +Routine Description: + + Execute a soft reset to the socket specified. + +Arguments: + + SdhcExtension - Host controller specific driver context. + + ResetType - Either full, CMD, or DAT reset. + +Return value: + + STATUS_SUCCESS - Reset succeeded. + + STATUS_INVALID_PARAMETER - Invalid reset type chosen. + +--*/ + +{ + + UCHAR HostControl; + UCHAR Reset; + UCHAR Retries; + UCHAR Mask; + + switch (ResetType) { + case SdResetTypeAll: + Mask = SDHC_RESET_ALL; + break; + + case SdResetTypeCmd: + Mask = SDHC_RESET_CMD; + break; + + case SdResetTypeDat: + Mask = SDHC_RESET_DAT; + break; + + default: + return STATUS_INVALID_PARAMETER; + } + + if (InterlockedExchangePointer( + (void *volatile *) & SdhcExtension->OutstandingRequest, NULL) != NULL) { + // Aborted previous request. + } + + // + // Reset the host controller. + // + + Retries = 100; + SdhcWriteRegisterUchar(SdhcExtension, SDHC_RESET, Mask); + do { + Retries -= 1; + if (Retries == 0) { + return STATUS_IO_TIMEOUT; + } + + Reset = SdhcReadRegisterUchar(SdhcExtension, SDHC_RESET); + + if ((Reset & Mask) != 0) { + SdPortWait(1000); + } + + } while ((Reset & Mask) != 0); + + // + // Set the max HW timeout for bus operations. + // + + SdhcWriteRegisterUchar(SdhcExtension, + SDHC_TIMEOUT_CONTROL, + SDHC_TC_MAX_DATA_TIMEOUT); + + // + // Clear detection interrupt after reset, we will pick up the + // status from the present state register + // + + SdhcWriteRegisterUshort(SdhcExtension, + SDHC_INTERRUPT_STATUS, + 0xFFFF); + + // + // Initialize DMA if the controller supports it. + // + + HostControl = SdhcReadRegisterUchar(SdhcExtension, SDHC_HOST_CONTROL); + HostControl &= ~SDHC_HC_DMA_SELECT_MASK; + if (SdhcExtension->Capabilities.Supported.ScatterGatherDma) { + if (SdhcExtension->Capabilities.Supported.Address64Bit) { + HostControl |= SDHC_HC_DMA_SELECT_ADMA64; + + } else { + HostControl |= SDHC_HC_DMA_SELECT_ADMA32; + } + } + + SdhcWriteRegisterUchar(SdhcExtension, SDHC_HOST_CONTROL, HostControl); + return STATUS_SUCCESS; +} + +NTSTATUS +DwcSdhcRkConfigurePhy( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ ULONG Frequency + ) +{ + if (Frequency < 100000000) { + SdhcWriteRegisterUlong(SdhcExtension, DWCMSHC_EMMC_DLL_CTRL, 0); + SdhcWriteRegisterUlong(SdhcExtension, DWCMSHC_EMMC_DLL_RXCLK, 0); + SdhcWriteRegisterUlong(SdhcExtension, DWCMSHC_EMMC_DLL_TXCLK, 0); + SdhcWriteRegisterUlong(SdhcExtension, DWCMSHC_EMMC_DLL_STRBIN, + DWCMSHC_EMMC_DLL_STRBIN_DLYENA | DWCMSHC_EMMC_DLL_STRBIN_DELAY_NUM_SEL + | DWCMSHC_EMMC_DLL_STRBIN_DELAY_NUM_DEFAULT << DWCMSHC_EMMC_DLL_STRBIN_DELAY_NUM_SHIFT); + SdhcWriteRegisterUlong(SdhcExtension, DWCMSHC_EMMC_DLL_CMDOUT, 0); + + return STATUS_SUCCESS; + } + else { + return STATUS_NOT_SUPPORTED; + } +} + +NTSTATUS +DwcSdhcRkDsmSetCardClock( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ ULONG TargetFrequency, + _Out_ PULONG ActualFrequency + ) +{ + NTSTATUS Status = STATUS_SUCCESS; + ACPI_EVAL_OUTPUT_BUFFER UNALIGNED* ReturnBufferPtr = nullptr; + + ACPI_METHOD_ARGUMENT SetClockArg; + ACPI_METHOD_SET_ARGUMENT_INTEGER((&SetClockArg), TargetFrequency); + + Status = AcpiExecuteDsmFunction(SdhcExtension->PdoPtr, + &RKCP0D40_DSM_GUID, + RKCP0D40_DSM_FUNCTION_REVISION_SET_CARD_CLOCK, + RKCP0D40_DSM_FUNCTION_IDX_SET_CARD_CLOCK, + &SetClockArg, + sizeof(SetClockArg), + &ReturnBufferPtr); + + if (!NT_SUCCESS(Status)) { + return Status; + } + + *ActualFrequency = ReturnBufferPtr->Argument[0].Argument; + + ExFreePoolWithTag(ReturnBufferPtr, ACPI_TAG_EVAL_OUTPUT_BUFFER); + + return Status; +} + +NTSTATUS +DwcSdhcRkSetClock( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ ULONG TargetFrequencyKhz + ) +{ + NTSTATUS Status; + + ULONG ActualFrequencyHz = 0; + + Status = DwcSdhcRkDsmSetCardClock(SdhcExtension, + TargetFrequencyKhz * 1000, // Hz + &ActualFrequencyHz); + + if (!NT_SUCCESS(Status)) { + return Status; + } + + return DwcSdhcRkConfigurePhy(SdhcExtension, ActualFrequencyHz); +} + +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +SdhcSetClock( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ ULONG Frequency + ) + +/*++ + +Routine Description: + + Set the clock to a given frequency. + +Arguments: + + SdhcExtension - Host controller specific driver context. + + Frequency - The target frequency. + +Return value: + + STATUS_SUCCESS - The clock was successfuly set. + + STATUS_IO_TIMEOUT - The clock did not stabilize in time. + +--*/ + +{ + + //ULONG ActualFrequency; + USHORT ClockControl; + ULONG Delay; + USHORT Mask; + UCHAR Retries; + NTSTATUS Status; + + ClockControl = SdhcReadRegisterUshort(SdhcExtension, SDHC_CLOCK_CONTROL); + ClockControl &= ~(SDHC_CC_CLOCK_ENABLE | SDHC_CC_INTERNAL_CLOCK_ENABLE); + SdhcWriteRegisterUshort(SdhcExtension, SDHC_CLOCK_CONTROL, ClockControl); + + // + // RKCP0D40: + // The clock divider is broken on this implementation of the controller. + // + + // ClockControl = SdhcCalcClockFrequency(SdhcExtension, + // Frequency, + // &ActualFrequency); + + Status = DwcSdhcRkSetClock(SdhcExtension, Frequency); + + if (!NT_SUCCESS(Status)) { + return Status; + } + + ClockControl |= SDHC_CC_INTERNAL_CLOCK_ENABLE; + SdhcWriteRegisterUshort(SdhcExtension, SDHC_CLOCK_CONTROL, ClockControl); + + // + // Now the frequency is set, delay a few times to wait for it to become + // stable. + // + + Retries = 100; + Mask = SDHC_CC_CLOCK_STABLE; + do { + Retries -= 1; + if (Retries == 0) { + return STATUS_IO_TIMEOUT; + } + + ClockControl = + SdhcReadRegisterUshort(SdhcExtension, SDHC_CLOCK_CONTROL); + + if ((ClockControl & Mask) == 0) { + SdPortWait(1000); + } + + } while ((ClockControl & Mask) == 0); + + // + // Clock is now stable, now enable it. + // + + ClockControl |= SDHC_CC_CLOCK_ENABLE; + SdhcWriteRegisterUshort(SdhcExtension, SDHC_CLOCK_CONTROL, ClockControl); + + // + // Some hardware need more time here to stabilize, but minimize latency + // for fixed eMMC devices during runtime Dx transitions. + // + + Delay = (SdhcExtension->Removable) ? (10 * 1000) : 50; + SdPortWait(Delay); + return STATUS_SUCCESS; +} + +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +SdhcSetVoltage( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ SDPORT_BUS_VOLTAGE Voltage + ) + +/*++ + +Routine Description: + + Set the slot's voltage profile. + +Arguments: + + SdhcExtension - Host controller specific driver context. + + VoltageProfile - Indicates which power voltage to use. If 0, turn off the + power + +Return value: + + STATUS_SUCCESS - The bus voltage was successfully switched. + + STATUS_INVALID_PARAMETER - The voltage profile provided was invalid. + + STATUS_IO_TIMEOUT - The bus voltage did not stabilize in time. + +--*/ + +{ + + ULONG Delay; + UCHAR Mask; + UCHAR PowerControl; + UCHAR PowerControlCheck; + UCHAR Retries; + + // + // Wait 10ms if the slot is removable; otherwise, only wait 50us. + // + + Delay = (SdhcExtension->Removable) ? (10* 1000) : 50; + SdPortWait(Delay); + + // + // Use the highest voltage capable. + // + + switch (Voltage) { + case SdBusVoltage33: + PowerControl = SDHC_PC_3_3V; + break; + + case SdBusVoltage30: + PowerControl = SDHC_PC_3_0V; + break; + + case SdBusVoltage18: + PowerControl = SDHC_PC_1_8V; + break; + + case SdBusVoltageOff: + PowerControl = 0; + break; + + default: + + NT_ASSERTMSG("SDHC - Voltage profile invalid.", FALSE); + + return STATUS_INVALID_PARAMETER; + } + + Retries = 100; + Mask = SDHC_PC_VOLTAGE_MASK; + do { + Retries -= 1; + if (Retries == 0) { + return STATUS_IO_TIMEOUT; + } + + SdhcWriteRegisterUchar(SdhcExtension, SDHC_POWER_CONTROL, PowerControl); + PowerControlCheck = + SdhcReadRegisterUchar(SdhcExtension, SDHC_POWER_CONTROL); + + if ((PowerControlCheck & Mask) != PowerControl) { + SdPortWait(1000); + } + + } while ((PowerControlCheck & Mask) != PowerControl); + + PowerControl |= SDHC_PC_BUS_POWER; + SdhcWriteRegisterUchar(SdhcExtension, SDHC_POWER_CONTROL, PowerControl); + SdPortWait(Delay); + + Retries = 100; + Mask = SDHC_PC_VOLTAGE_MASK | SDHC_PC_BUS_POWER; + do { + Retries -= 1; + if (Retries == 0) { + return STATUS_IO_TIMEOUT; + } + + SdhcWriteRegisterUchar(SdhcExtension, SDHC_POWER_CONTROL, PowerControl); + PowerControlCheck = + SdhcReadRegisterUchar(SdhcExtension, SDHC_POWER_CONTROL); + + if ((PowerControlCheck & Mask) != PowerControl) { + SdPortWait(1000); + } + + } while ((PowerControlCheck & Mask) != PowerControl); + + return STATUS_SUCCESS; +} + +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +SdhcSetBusWidth( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ SDPORT_BUS_WIDTH Width + ) + +/*++ + +Routine Description: + + Set bus width for host controller. + +Arguments: + + SdhcExtension - Host controller specific driver context. + + Width - The data bus width of the slot. + +Return value: + + STATUS_SUCCESS - The bus width was properly changed. + +--*/ + +{ + + UCHAR HostControl; + + HostControl = SdhcReadRegisterUchar(SdhcExtension, SDHC_HOST_CONTROL); + HostControl &= ~(SDHC_HC_DATA_WIDTH_4BIT | SDHC_HC_DATA_WIDTH_8BIT); + switch (Width) { + case 1: + break; + + case 4: + HostControl |= SDHC_HC_DATA_WIDTH_4BIT; + break; + case 8: + HostControl |= SDHC_HC_DATA_WIDTH_8BIT; + break; + + default: + + NT_ASSERTMSG("SDHC - Provided bus width is invalid", FALSE); + + break; + } + + SdhcWriteRegisterUchar(SdhcExtension, SDHC_HOST_CONTROL, HostControl); + return STATUS_SUCCESS; +} + +NTSTATUS +SdhcSetSpeed( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ SDPORT_BUS_SPEED Speed + ) + +/*++ + +Routine Description: + + Based on the capabilities of card and host, turns on the maximum performing + speed mode for the host. Caller is expected to know the capabilities of the + card before setting the speed mode. + +Arguments: + + SdhcExtension - Host controller specific driver context. + + Speed - Bus speed to set. + +Return value: + + STATUS_SUCCESS - The selected speed mode was successfully set. + + STATUS_INVALID_PARAMETER - The speed mode selected is not valid. + +--*/ + +{ + + NTSTATUS Status; + USHORT UhsMode; + + switch (Speed) { + case SdBusSpeedNormal: + Status = SdhcSetHighSpeed(SdhcExtension, FALSE); + break; + + case SdBusSpeedHigh: + Status = SdhcSetHighSpeed(SdhcExtension, TRUE); + break; + + case SdBusSpeedSDR12: + case SdBusSpeedSDR25: + case SdBusSpeedSDR50: + case SdBusSpeedDDR50: + case SdBusSpeedSDR104: + case SdBusSpeedHS200: + case SdBusSpeedHS400: + UhsMode = SdhcGetHwUhsMode(Speed); + Status = SdhcSetUhsMode(SdhcExtension, UhsMode); + break; + + default: + + NT_ASSERTMSG("SDHC - Invalid speed mode selected.", FALSE); + + Status = STATUS_INVALID_PARAMETER; + break; + } + + return Status; +} + +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +SdhcSetHighSpeed( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ BOOLEAN Enable + ) + +/*++ + +Routine Description: + + Based on the capabilities of card and host, enables or disables high speed. + +Arguments: + + SdhcExtension - Host controller specific driver context. + + Enable - TRUE to enable high speed, FALSE to disable. + +Return value: + + STATUS_SUCCESS - High speed was successfully enabled. + +--*/ + +{ + + UCHAR HostControl; + + HostControl = SdhcReadRegisterUchar(SdhcExtension, SDHC_HOST_CONTROL); + HostControl &= ~SDHC_HC_ENABLE_HIGH_SPEED; + if (Enable) { + HostControl |= SDHC_HC_ENABLE_HIGH_SPEED; + } + + SdhcWriteRegisterUchar(SdhcExtension, SDHC_HOST_CONTROL, HostControl); + return STATUS_SUCCESS; +} + +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +SdhcSetUhsMode( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ USHORT Mode + ) + +/*++ + +Routine Description: + + Based on the capabilities of card and host, turns on the maximum performing + speed mode for the host. Caller is expected to know the capabilities of the + card before setting the speed mode. + +Arguments: + + SdhcExtension - Host controller specific driver context. + + Mode - UHS mode to set. + +Return value: + + STATUS_SUCCESS - Mode requested is set on the controller. + + STATUS_INVALID_PARAMETER - The mode selected is not a UHS mode. + +--*/ + +{ + + USHORT ClockControl; + USHORT HostControl2; + + HostControl2 = SdhcReadRegisterUshort(SdhcExtension, SDHC_HOST_CONTROL2); + + // + // If we're already in the requested mode, return. + // + + if ((HostControl2 & SDHC_HC2_UHS_MODES) == Mode) { + return STATUS_SUCCESS; + } + + ClockControl = SdhcReadRegisterUshort(SdhcExtension, SDHC_CLOCK_CONTROL); + ClockControl &= ~SDHC_CC_CLOCK_ENABLE; + SdhcWriteRegisterUshort(SdhcExtension, SDHC_CLOCK_CONTROL, ClockControl); + SdPortWait(10 * 1000); + + // + // Set the UHS mode. + // + + HostControl2 &= ~SDHC_HC2_UHS_MODES; + HostControl2 |= Mode; + SdhcWriteRegisterUshort(SdhcExtension, SDHC_HOST_CONTROL2, HostControl2); + ClockControl = SdhcReadRegisterUshort(SdhcExtension, SDHC_CLOCK_CONTROL); + ClockControl |= SDHC_CC_CLOCK_ENABLE; + SdhcWriteRegisterUshort(SdhcExtension, SDHC_CLOCK_CONTROL, ClockControl); + SdPortWait(10 * 1000); + return STATUS_SUCCESS; +} + +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +SdhcSetSignaling( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ BOOLEAN Enable + ) + +/*++ + +Routine Description: + + Set signaling voltage (1.8V or 3.3V). + +Arguments: + + SdhcExtension - Host controller specific driver context. + + Enable - TRUE for 1.8V signaling, FALSE for default 3.3V. + +Return value: + + STATUS_SUCCESS - Signaling voltage switch successful. + + STATUS_UNSUCCESSFUL - Signaling voltage switch unsuccessful. + +--*/ + +{ + + USHORT ClockControl; + ULONG DatLines; + USHORT HostControl2; + USHORT Mask; + + // + // Disable the bus clock. + // + + ClockControl = SdhcReadRegisterUshort(SdhcExtension, SDHC_CLOCK_CONTROL); + ClockControl &= ~SDHC_CC_CLOCK_ENABLE; + SdhcWriteRegisterUshort(SdhcExtension, SDHC_CLOCK_CONTROL, ClockControl); + SdPortWait(10000); + + // + // DAT[3:0] must be all zeroes. + // + + DatLines = 0; + DatLines = + SDHC_PS_DAT_3_0 & + SdhcReadRegisterUlong(SdhcExtension, SDHC_PRESENT_STATE); + + if (DatLines != 0) { + return STATUS_UNSUCCESSFUL; + } + + // + // Set the signaling voltage. + // + + HostControl2 = SdhcReadRegisterUshort(SdhcExtension, SDHC_HOST_CONTROL2); + Mask = SDHC_HC2_1_8V_SIGNALING; + if (Enable) { + HostControl2 |= Mask; + + } else { + HostControl2 &= ~Mask; + } + + SdhcWriteRegisterUshort(SdhcExtension, SDHC_HOST_CONTROL2, HostControl2); + SdPortWait(5000); + HostControl2 = SdhcReadRegisterUshort(SdhcExtension, SDHC_HOST_CONTROL2); + + // + // Make sure the signaling voltage latched. + // + + if (Enable) { + if ((HostControl2 & Mask) == 0) { + return STATUS_UNSUCCESSFUL; + } + + } else { + if ((HostControl2 & Mask) != 0) { + return STATUS_UNSUCCESSFUL; + } + } + + // + // Reenable the bus clock. + // + + ClockControl = SdhcReadRegisterUshort(SdhcExtension, SDHC_CLOCK_CONTROL); + ClockControl |= SDHC_CC_CLOCK_ENABLE; + SdhcWriteRegisterUshort(SdhcExtension, SDHC_CLOCK_CONTROL, ClockControl); + SdPortWait(10000); + + // + // DAT[3:0] must be all ones. + // + + DatLines = 0; + DatLines = + SDHC_PS_DAT_3_0 & + SdhcReadRegisterUlong(SdhcExtension, SDHC_PRESENT_STATE); + + if (DatLines != SDHC_PS_DAT_3_0) { + return STATUS_UNSUCCESSFUL; + } + + return STATUS_SUCCESS; +} + +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +SdhcExecuteTuning( + _In_ PSDHC_EXTENSION SdhcExtension + ) + +/*++ + +Routine Description: + + Tune the bus sampling point due to variations in voltage, + temperature, and time. + + Caller guarantees that the bus is in a UHS mode that + requires tuning. + +Arguments: + + SdhcExtension - Host controller specific driver context. + +Return value: + + STATUS_SUCCESS - Tuning successful. + +--*/ + +{ + + USHORT HostControl2; + //UCHAR Retries; + SDPORT_REQUEST TuningRequest; + + HostControl2 = SdhcReadRegisterUshort(SdhcExtension, SDHC_HOST_CONTROL2); + + NT_ASSERT((HostControl2 & SDHC_HC2_EXECUTE_TUNING) == 0); + + // + // Disable controller events + // + // Technically, all controller events should be disabled at tuning execute + // time, but some controllers do not follow this requirement. + // + + if ((HostControl2 & SDHC_HC2_EXECUTE_TUNING) == 0) { + HostControl2 |= SDHC_HC2_EXECUTE_TUNING; + SdhcWriteRegisterUshort(SdhcExtension, + SDHC_HOST_CONTROL2, + HostControl2); + } + + RtlZeroMemory(&TuningRequest, sizeof(TuningRequest)); + TuningRequest.Command.TransferType = SdTransferTypeSingleBlock; + TuningRequest.Command.TransferDirection = SdTransferDirectionRead; + TuningRequest.Command.Class = SdCommandClassStandard; + TuningRequest.Command.ResponseType = SdResponseTypeR1; + if (SdhcExtension->SpeedMode == SdhcSpeedModeSDR104) { + TuningRequest.Command.Index = 19; + TuningRequest.Command.BlockSize = 64; + + } else { + TuningRequest.Command.Index = 21; + TuningRequest.Command.BlockSize = 128; + } + + // + // TODO: Implement rest of tuning logic as described in + // SD Host Controller Specification. This is needed for + // SDR104, HS200, and HS400 modes, and sometimes for SDR50. + // + + // + // Execute the data transfer. + // + + //Status = SdhcBuildTransfer() + + return STATUS_SUCCESS; +} + +VOID +SdhcSetLed( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ BOOLEAN Enable + ) + +/*++ + +Routine Description: + + Turn the controller activity LED on/off. + +Arguments: + + SdhcExtension - Host controller specific driver context. + + Enable - Indicate whether to enable or disable the LED. + +Return value: + + None. + +--*/ + +{ + + UCHAR HostControl; + + HostControl = SdhcReadRegisterUchar(SdhcExtension, SDHC_HOST_CONTROL); + if (Enable) { + HostControl |= SDHC_HC_LED_POWER; + + } else { + HostControl &= ~SDHC_HC_LED_POWER; + } + + SdhcWriteRegisterUchar(SdhcExtension, SDHC_HOST_CONTROL, HostControl); +} + +NTSTATUS +SdhcSetPresetValue( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ BOOLEAN Enable + ) + +/*++ + +Routine Description: + + Enable or disable setting of preset values. Caller must + ensure that the controller supports preset value. + +Arguments: + + SdhcExtension - Host controller specific driver context. + + Enable - Indicate whether to enable or disable preset values. + +Return value: + + None. + +--*/ + +{ + + USHORT HostControl2; + + HostControl2 = SdhcReadRegisterUshort(SdhcExtension, SDHC_HOST_CONTROL2); + + NT_ASSERT((HostControl2 & SDHC_HC2_ENABLE_PRESET_VALUE) != 0); + + HostControl2 &= ~SDHC_HC2_ENABLE_PRESET_VALUE; + if (Enable) { + HostControl2 |= SDHC_HC2_ENABLE_PRESET_VALUE; + } + + SdhcWriteRegisterUshort(SdhcExtension, SDHC_HOST_CONTROL2, HostControl2); + return STATUS_SUCCESS; +} + +NTSTATUS +SdhcEnableBlockGapInterrupt( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ BOOLEAN Enable + ) + +/*++ + +Routine Description: + + Enables block gap interrupts for SDIO cards in 4-bit mode. + Caller has responsibility to make sure this is only called for + appropriate devices. + +Arguments: + + SdhcExtension - Host controller specific driver context. + + Enable - Enable or disable block gap interrupts. + +Return value: + + None. + +--*/ + +{ + + UCHAR Control; + + Control = SdhcReadRegisterUchar(SdhcExtension, SDHC_BLOCKGAP_CONTROL); + if (Enable) { + Control |= SDHC_BGC_INTERRUPT_ENABLE; + + } else { + Control &= ~SDHC_BGC_INTERRUPT_ENABLE; + } + + SdhcWriteRegisterUchar(SdhcExtension, SDHC_BLOCKGAP_CONTROL, Control); + return STATUS_SUCCESS; +} + +VOID +SdhcSetBlockGapControl( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ BOOLEAN Continue, + _In_ BOOLEAN RequestStop + ) + +/*++ + +Routine Description: + + Enable block gap interrupt requests. + +Arguments: + + SdhcExtension - Host controller specific driver context. + + Continue - Continue after the next block gap. + + RequestStop - Request the block gap interrupt request. + +Return value: + + None. + +--*/ + +{ + + UCHAR BlockGapControl; + + BlockGapControl = SdhcReadRegisterUchar(SdhcExtension, + SDHC_BLOCKGAP_CONTROL); + + BlockGapControl &= ~SDHC_BGC_CONTINUE; + BlockGapControl &= ~SDHC_BGC_STOP_NEXT_GAP; + if (Continue) { + BlockGapControl |= SDHC_BGC_CONTINUE; + } + + if (RequestStop) { + BlockGapControl |= SDHC_BGC_STOP_NEXT_GAP; + } +} + +VOID +SdhcEnableInterrupt( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ ULONG NormalInterruptMask + ) + +/*++ + +Routine Description: + + Set the host event mask to the new value specified. + +Arguments: + + SdhcExtension - Host controller specific driver context. + + NormalInterruptMask - The new normal events to set. + +Return value: + + None. + +--*/ + +{ + + USHORT InterruptEnable; + + InterruptEnable = + SdhcReadRegisterUshort(SdhcExtension, SDHC_INTERRUPT_SIGNAL_ENABLE); + + InterruptEnable |= NormalInterruptMask; + + // + // Enable the interrupt signals from controller to OS. + // + + if (!SdhcExtension->CrashdumpMode) { + SdhcWriteRegisterUshort(SdhcExtension, + SDHC_INTERRUPT_SIGNAL_ENABLE, + InterruptEnable); + + SdhcWriteRegisterUshort(SdhcExtension, + SDHC_ERROR_SIGNAL_ENABLE, + 0xFFFF); + } + + // + // Enable the interrupt signals on the controller. + // + + SdhcWriteRegisterUshort(SdhcExtension, + SDHC_INTERRUPT_STATUS_ENABLE, + InterruptEnable); + + SdhcWriteRegisterUshort(SdhcExtension, + SDHC_ERROR_STATUS_ENABLE, + 0xFFFF); +} + +VOID +SdhcDisableInterrupt( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ ULONG NormalInterruptMask + ) + +/*++ + +Routine Description: + + Set the host event mask to the new value specified. + +Arguments: + + SdhcExtension - Host controller specific driver context. + + NormalInterruptMask - Normal Interrupts to disable. + +Return value: + + None. + +--*/ + +{ + + USHORT InterruptDisable; + + InterruptDisable = + SdhcReadRegisterUshort(SdhcExtension, SDHC_INTERRUPT_SIGNAL_ENABLE); + + InterruptDisable &= ~NormalInterruptMask; + + // + // Disable the interrupt signals on the controller. + // + + SdhcWriteRegisterUshort(SdhcExtension, + SDHC_INTERRUPT_STATUS_ENABLE, + InterruptDisable); + + SdhcWriteRegisterUshort(SdhcExtension, + SDHC_ERROR_STATUS_ENABLE, + 0); + + // + // Disable the interrupt signals from controller to OS. + // + + SdhcWriteRegisterUshort(SdhcExtension, + SDHC_INTERRUPT_SIGNAL_ENABLE, + InterruptDisable); + + SdhcWriteRegisterUshort(SdhcExtension, + SDHC_ERROR_SIGNAL_ENABLE, + 0); +} + +__forceinline +USHORT +SdhcGetInterruptStatus( + _In_ PSDHC_EXTENSION SdhcExtension + ) + +/*++ + +Routine Description: + + Get current pending events from the interrupt status. This function does + not acknowledge the interrupt. The caller should acknowledge or disable + the corresponding events. + +Arguments: + + SdhcExtension - Host controller specific driver context. + +Return value: + + The event mask. + +--*/ + +{ + + USHORT InterruptStatus; + + InterruptStatus = + SdhcReadRegisterUshort(SdhcExtension, SDHC_INTERRUPT_STATUS); + + // + // 0xFFFF means HC is no longer accessible. This interrupt does not belong + // to us. + // + + if (InterruptStatus == 0xFFFF) { + return 0; + } + + return InterruptStatus; +} + +__forceinline +USHORT +SdhcGetErrorStatus( + _In_ PSDHC_EXTENSION SdhcExtension + ) + +/*++ + +Routine Description: + + This routine returns the current error status, if any. + +Arguments: + + SdhcExtension - Host controller specific driver context. + +Return value: + + The error interrupt status. + +--*/ + +{ + + return SdhcReadRegisterUshort(SdhcExtension, SDHC_ERROR_STATUS); +} + +__forceinline +USHORT +SdhcGetAutoCmd12ErrorStatus( + _In_ PSDHC_EXTENSION SdhcExtension + ) + +/*++ + +Routine Description: + + This routine returns the current Auto CMD12 error status, if any. + +Arguments: + + SdhcExtension - Host controller specific driver context. + +Return value: + + The Auto CMD12 error status. + +--*/ + +{ + + return SdhcReadRegisterUshort(SdhcExtension, SDHC_AUTO_CMD12_ERROR_STATUS); +} + +USHORT +SdhcGetAdmaErrorStatus( + _In_ PSDHC_EXTENSION SdhcExtension + ) + +/*++ + +Routine Description: + + This routine returns the current ADMA error status, if any. + +Arguments: + + SdhcExtension - Host controller specific driver context. + +Return value: + + The ADMA error status. + +--*/ + +{ + + return SdhcReadRegisterUshort(SdhcExtension, SDHC_ADMA_ERROR_STATUS); +} + +VOID +SdhcAcknowledgeInterrupts( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ USHORT Interrupts + ) + +/*++ + +Routine Description: + + Acknowlege the interrupts specified. + +Arguments: + + SdhcExtension - Host controller specific driver context. + + Interrupts - event mask to acknowledge (single event only) + +Return value: + + None. + +--*/ + +{ + + if ((Interrupts & SDHC_IS_ERROR_INTERRUPT) != 0) { + + // + // The Auto CMD12 error interrupt status bit of some Ricoh controllers + // can't get cleared by writing to the error status register alone. + // Write all-ones and all-zeroes to the Auto CMD12 error status register + // first to work around this issue. This write should have no effect on + // other types of controllers since the register should be read-only + // according to the spec. + // + + SdhcWriteRegisterUshort(SdhcExtension, + SDHC_AUTO_CMD12_ERROR_STATUS, + 0xFFFF); + + SdhcWriteRegisterUshort(SdhcExtension, + SDHC_AUTO_CMD12_ERROR_STATUS, + 0x0); + + // + // Clear the error interrupt by writing all-ones. + // + + SdhcWriteRegisterUshort(SdhcExtension, SDHC_ERROR_STATUS, 0xFFFF); + Interrupts &= ~SDHC_IS_ERROR_INTERRUPT; + } + + // + // Clear other interrupts in the interrupt status register. + // + + SdhcWriteRegisterUshort(SdhcExtension, SDHC_INTERRUPT_STATUS, Interrupts); +} + +BOOLEAN +SdhcIsCardInserted( + _In_ PSDHC_EXTENSION SdhcExtension + ) + +/*++ + +Routine Description: + + To detect whether there is a card in the socket. + +Arguments: + + SdhcExtension - Host controller specific driver context. + +Return value: + + BOOLEAN value to indicate whether there is a card in the socket. + +--*/ + +{ + + ULONG PresentState; + + PresentState = SdhcReadRegisterUlong(SdhcExtension, SDHC_PRESENT_STATE); + return (BOOLEAN) ((PresentState & SDHC_PS_CARD_INSERTED) != 0); +} + +BOOLEAN +SdhcIsWriteProtected( + _In_ PSDHC_EXTENSION SdhcExtension + ) + +/*++ + +Routine Description: + + To detect whether the card is write protected. + +Arguments: + + SdhcExtension - Host controller specific driver context. + +Return value: + + BOOLEAN value to indicate whether the card is write protected. + +--*/ + +{ + UNREFERENCED_PARAMETER(SdhcExtension); + + // + // RKCP0D40: + // SDHC_PS_WRITE_PROTECT in SDHC_PRESENT_STATE seems to be always 0 (read-only). + // + return FALSE; +} + +NTSTATUS +SdhcSendCommand( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ PSDPORT_REQUEST Request + ) + +/*++ + +Routine Description: + + This routine takes the SD command package and writes it to the appropriate + registers on the host controller. It also computes the proper flag + settings. + +Arguments: + + SdhcExtension - Host controller specific driver context. + + Request - Supplies the descriptor for this SD command + +Return value: + + STATUS_SUCCESS - Command successfully sent. + + STATUS_INVALID_PARAMETER - Invalid command response type specified. + +--*/ + +{ + + PSDPORT_COMMAND Command; + USHORT CommandType; + USHORT CommandReg; + NTSTATUS Status; + USHORT TransferMode; + + // + // Initialize transfer parameters if this command is a data command. + // + + Command = &Request->Command; + if ((Command->TransferType != SdTransferTypeNone) && + (Command->TransferType != SdTransferTypeUndefined)) { + + Status = SdhcBuildTransfer(SdhcExtension, Request); + if (!NT_SUCCESS(Status)) { + return Status; + } + } + + // + // Set the response parameters based off the given response type. + // + + SdhcWriteRegisterUlong(SdhcExtension, SDHC_ARGUMENT, Command->Argument); + + CommandReg = Command->Index << 8; + switch (Command->ResponseType) { + case SdResponseTypeNone: + break; + + case SdResponseTypeR1: + case SdResponseTypeR5: + case SdResponseTypeR6: + CommandReg |= SDHC_CMD_RESPONSE_48BIT_NOBUSY | + SDHC_CMD_CRC_CHECK_ENABLE | + SDHC_CMD_INDEX_CHECK_ENABLE; + break; + + case SdResponseTypeR1B: + case SdResponseTypeR5B: + CommandReg |= SDHC_CMD_RESPONSE_48BIT_WBUSY | + SDHC_CMD_CRC_CHECK_ENABLE | + SDHC_CMD_INDEX_CHECK_ENABLE; + break; + + case SdResponseTypeR2: + CommandReg |= SDHC_CMD_RESPONSE_136BIT | + SDHC_CMD_CRC_CHECK_ENABLE; + break; + + case SdResponseTypeR3: + case SdResponseTypeR4: + CommandReg |= SDHC_CMD_RESPONSE_48BIT_NOBUSY; + break; + + default: + + NT_ASSERTMSG("SDHC - Invalid response type", FALSE); + + return STATUS_INVALID_PARAMETER; + } + + if (Request->Command.TransferType != SdTransferTypeNone) { + TransferMode = 0; + CommandReg |= SDHC_CMD_DATA_PRESENT; + + } else { + TransferMode = + SdhcReadRegisterUshort(SdhcExtension, SDHC_TRANSFER_MODE); + + TransferMode &= ~SDHC_TM_DMA_ENABLE; + TransferMode &= ~SDHC_TM_AUTO_CMD12_ENABLE; + TransferMode &= ~SDHC_TM_AUTO_CMD23_ENABLE; + SdhcWriteRegisterUshort(SdhcExtension, + SDHC_TRANSFER_MODE, + TransferMode); + } + + switch (Command->Type) { + case SdCommandTypeSuspend: + CommandType = SDHC_CMD_TYPE_SUSPEND; + break; + + case SdCommandTypeResume: + CommandType = SDHC_CMD_TYPE_RESUME; + break; + + case SdCommandTypeAbort: + CommandType = SDHC_CMD_TYPE_ABORT; + break; + + default: + CommandType = 0; + break; + } + + // + // Set the bitmask for the required events that will fire after + // writing to the command register. Depending on the response + // type or whether the command involves data transfer, we will need + // to wait on a number of different events. + // + + InterlockedAnd((PLONG)&SdhcExtension->CurrentEvents, 0); + Request->RequiredEvents = SDHC_IS_CMD_COMPLETE; + if ((Command->ResponseType == SdResponseTypeR1B) || + (Command->ResponseType == SdResponseTypeR5B)) { + + Request->RequiredEvents |= SDHC_IS_TRANSFER_COMPLETE; + } + + if (Request->Command.TransferMethod == SdTransferMethodSgDma) { + Request->RequiredEvents |= SDHC_IS_TRANSFER_COMPLETE; + + } else if (Request->Command.TransferMethod == SdTransferMethodPio) { + if (Request->Command.TransferDirection == SdTransferDirectionRead) { + Request->RequiredEvents |= SDHC_IS_BUFFER_READ_READY; + + } else { + Request->RequiredEvents |= SDHC_IS_BUFFER_WRITE_READY; + } + } + + // + // Issue the actual command. + // + + CommandReg |= CommandType; + SdhcWriteRegisterUshort(SdhcExtension, SDHC_COMMAND, CommandReg); + + // + // We must wait until the request completes. + // + + return STATUS_PENDING; +} + +NTSTATUS +SdhcGetResponse( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ PSDPORT_COMMAND Command, + _Out_ PVOID ResponseBuffer + ) + +/*++ + +Routine Description: + + This routine reads the response of the SD card which is sent over + the command line, and stores it in the specified buffer + +Arguments: + + SdhcExtension - Host controller specific driver context. + + Request - Supplies the descriptor for this SD command. + +Return value: + + STATUS_SUCCESS - Response successfully returned. + + STATUS_INVALID_PARAMETER - Invalid response length. + +--*/ + +{ + + UCHAR Index; + PUCHAR Response; + UCHAR ResponseLength; + + *(PUCHAR)(ResponseBuffer) = 0; + + ResponseLength = SdhcGetResponseLength(Command); + if (ResponseLength > 16) { + return STATUS_INVALID_PARAMETER; + } + + Response = (PUCHAR) ResponseBuffer; + for (Index = 0; Index < ResponseLength; Index += 1) { + +#pragma prefast(suppress: 22103, "Response length is guaranteed to be <= ResponseLength.") + + Response[Index] = + SdhcReadRegisterUchar(SdhcExtension, SDHC_RESPONSE + Index); + } + + return STATUS_SUCCESS; +} + +NTSTATUS +SdhcSetTransferMode( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ PSDPORT_REQUEST Request + ) + +/*++ + +Routine Description: + + This routine sets up the host for a data transfer. + +Arguments: + + SdhcExtension - Host controller specific driver context. + + Command - Supplies the descriptor for this SD command. + +Return value: + + STATUS_SUCCESS - Transfer mode successfully set. + + STATUS_INVALID_PARAMETER - Invalid command transfer parameters. + +--*/ + +{ + + USHORT BlockCount; + USHORT BlockSize; + USHORT TransferMode; + + NT_ASSERT(Request->Command.TransferMethod != SdTransferMethodUndefined); + + BlockCount = Request->Command.BlockCount; + if (Request->Command.BlockSize > 2048) { + + NT_ASSERTMSG("SDHC - Invalid block size for command", FALSE); + + return STATUS_INVALID_PARAMETER; + } + + if ((Request->Command.TransferDirection != SdTransferDirectionRead) && + (Request->Command.TransferDirection != SdTransferDirectionWrite)) { + + return STATUS_INVALID_PARAMETER; + } + + TransferMode = 0; + TransferMode &= ~(SDHC_TM_AUTO_CMD12_ENABLE | + SDHC_TM_AUTO_CMD23_ENABLE | + SDHC_TM_DMA_ENABLE | + SDHC_TM_BLKCNT_ENABLE | + SDHC_TM_MULTIBLOCK); + + // if (Command->TransferType = TransferTypeMultiBlockNoStop) { + + //} + + if (BlockCount > 1) { + TransferMode |= SDHC_TM_MULTIBLOCK; + TransferMode |= SDHC_TM_BLKCNT_ENABLE; + TransferMode |= SDHC_TM_AUTO_CMD12_ENABLE; + } + + if (Request->Command.TransferMethod == SdTransferMethodSgDma) { + TransferMode |= SDHC_TM_DMA_ENABLE; + + } else { + + NT_ASSERT(Request->Command.TransferMethod == SdTransferMethodPio); + + } + + BlockSize = Request->Command.BlockSize; + TransferMode &= ~SDHC_TM_TRANSFER_READ; + if (Request->Command.TransferDirection == SdTransferDirectionRead) { + TransferMode |= SDHC_TM_TRANSFER_READ; + } + + SdhcWriteRegisterUlong(SdhcExtension, SDHC_SYSADDR, BlockCount); + SdhcWriteRegisterUshort(SdhcExtension, SDHC_BLOCK_SIZE, BlockSize); + SdhcWriteRegisterUshort(SdhcExtension, SDHC_BLOCK_COUNT, BlockCount); + SdhcWriteRegisterUshort(SdhcExtension, SDHC_TRANSFER_MODE, TransferMode); + return STATUS_SUCCESS; +} + +VOID +SdhcReadDataPort( + _In_ PSDHC_EXTENSION SdhcExtension, + _Out_writes_all_(Length) PUCHAR Buffer, + _In_ SIZE_T Length + ) + +/*++ + +Routine Description: + + The data port must be accessed maintaining DWORD alignment. So for example: + + IN DWORD 130 + IN DWORD 130 + + is the same as + + IN USHORT 130 + IN USHORT 132 + IN UCHAR 130 + IN UCHAR 131 + IN UCHAR 132 + IN UCHAR 133 + +Arguments: + + SdhcExtension - Host controller specific driver context. + + Buffer - Data buffer to read. + + Length - Length of the data buffer in bytes. + +Return value: + + None. + +--*/ + +{ + + PUCHAR Port; + + Port = (PUCHAR) SdhcExtension->BaseAddress + SDHC_DATA_PORT; + while (Length >= sizeof(ULONG)) { + SdhcReadRegisterBufferUlong(SdhcExtension, + SDHC_DATA_PORT, + (PULONG) Buffer, + 1); + + Buffer += sizeof(ULONG); + Length -= sizeof(ULONG); + } + + if (Length >= sizeof(USHORT)) { + SdhcReadRegisterBufferUshort(SdhcExtension, + SDHC_DATA_PORT, + (PUSHORT) Buffer, + 1); + + Buffer += sizeof(USHORT); + Length -= sizeof(USHORT); + } + + if (Length >= sizeof(UCHAR)) { + SdhcReadRegisterBufferUchar(SdhcExtension, + SDHC_DATA_PORT + sizeof(USHORT), + Buffer, + 1); + } +} + +VOID +SdhcWriteDataPort( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_reads_(Length) PUCHAR Buffer, + _In_ ULONG Length + ) + +/*++ + +Routine Description: + + The data port must be accessed maintaining DWORD alignment. So for example: + + IN DWORD 130 + IN DWORD 130 + + is the same as + + IN USHORT 130 + IN USHORT 132 + IN UCHAR 130 + IN UCHAR 131 + IN UCHAR 132 + IN UCHAR 133 + +Arguments: + + SdhcExtension - Host controller specific driver context. + + Buffer - Data buffer to write. + + Length - Length of the data buffer in bytes. + +Return value: + + None. + +--*/ + +{ + + while (Length >= sizeof(ULONG)) { + SdhcWriteRegisterBufferUlong(SdhcExtension, + SDHC_DATA_PORT, + (PULONG) Buffer, + 1); + + Buffer += sizeof(ULONG); + Length -= sizeof(ULONG); + } + + if (Length >= sizeof(USHORT)) { + SdhcWriteRegisterBufferUshort(SdhcExtension, + SDHC_DATA_PORT, + (PUSHORT) Buffer, + 1); + + Buffer += sizeof(USHORT); + Length -= sizeof(USHORT); + } + + if (Length >= sizeof(UCHAR)) { + SdhcWriteRegisterBufferUchar(SdhcExtension, + SDHC_DATA_PORT + sizeof(USHORT), + Buffer, + 1); + } + + NT_ASSERT((SdhcReadRegisterUshort(SdhcExtension, SDHC_ERROR_STATUS) & + SDHC_ES_BAD_DATA_SPACE_ACCESS) == 0); +} + +NTSTATUS +SdhcBuildTransfer( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ PSDPORT_REQUEST Request + ) + +/*++ + +Routine Description: + + Prepare the transfer request. + +Arguments: + + SdhcExtension - Host controller specific driver context. + + Command - The command for which we're building the transfer request. + +Return value: + + NTSTATUS + +--*/ + +{ + + NTSTATUS Status; + + NT_ASSERT(Request->Command.TransferType != SdTransferTypeNone); + + NT_ASSERT(Request->Command.TransferMethod != SdTransferMethodUndefined); + + switch (Request->Command.TransferMethod) { + case SdTransferMethodPio: + Status = SdhcBuildPioTransfer(SdhcExtension, Request); + break; + + case SdTransferMethodSgDma: + Status = SdhcBuildAdmaTransfer(SdhcExtension, Request); + break; + + default: + Status = STATUS_NOT_SUPPORTED; + break; + } + + return Status; +} + +NTSTATUS +SdhcStartTransfer( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ PSDPORT_REQUEST Request + ) + +/*++ + +Routine Description: + + Execute the transfer request. + +Arguments: + + SdhcExtension - Host controller specific driver context. + + Request - The command for which we're building the transfer request. + +Return value: + + NTSTATUS + +--*/ + +{ + + NTSTATUS Status; + + NT_ASSERT(Request->Command.TransferType != SdTransferTypeNone); + + switch (Request->Command.TransferMethod) { + case SdTransferMethodPio: + Status = SdhcStartPioTransfer(SdhcExtension, Request); + break; + + case SdTransferMethodSgDma: + Status = SdhcStartAdmaTransfer(SdhcExtension, Request); + break; + + default: + Status = STATUS_NOT_SUPPORTED; + break; + } + + return Status; +} + +NTSTATUS +SdhcBuildPioTransfer( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ PSDPORT_REQUEST Request + ) + +/*++ + +Routine Description: + + Prepare the PIO transfer request. + +Arguments: + + SdhcExtension - Host controller specific driver context. + + Request - The command for which we're building the transfer. + +Return value: + + NTSTATUS + +--*/ + +{ + + return SdhcSetTransferMode(SdhcExtension, Request); +} + + +NTSTATUS +SdhcBuildAdmaTransfer( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ PSDPORT_REQUEST Request + ) + +/*++ + +Routine Description: + + Prepare the ADMA2 transfer request. + +Arguments: + + SdhcExtension - Host controller specific driver context. + + Request - The command for which we're building the transfer. + +Return value: + + NTSTATUS + +--*/ + +{ + + NTSTATUS Status; + ULONG TransferLength; + + Status = SdhcSetTransferMode(SdhcExtension, Request); + if (!NT_SUCCESS(Status)) { + return Status; + } + + NT_ASSERT(Request->Command.ScatterGatherList != NULL); + + // + // Create the ADMA2 descriptor table in the host's DMA buffer. + // + + Status = + SdhcCreateAdmaDescriptorTable( + Request, + (BOOLEAN) SdhcExtension->Capabilities.Supported.Address64Bit, + &TransferLength); + + if (!NT_SUCCESS(Status)) { + return Status; + } + + SdhcWriteRegisterUlong(SdhcExtension, + SDHC_ADMA_SYSADDR_LOW, + Request->Command.DmaPhysicalAddress.LowPart); + + if (SdhcExtension->Capabilities.Supported.Address64Bit) { + SdhcWriteRegisterUlong(SdhcExtension, + SDHC_ADMA_SYSADDR_HIGH, + Request->Command.DmaPhysicalAddress.HighPart); + } else { + + NT_ASSERT(Request->Command.DmaPhysicalAddress.HighPart == 0); + + } + + return STATUS_SUCCESS; +} + +NTSTATUS +SdhcStartPioTransfer( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ PSDPORT_REQUEST Request + ) + +/*++ + +Routine Description: + + Execute the PIO transfer request. + +Arguments: + + SdhcExtension - Host controller specific driver context. + + Request - The command for which we're building the transfer. + +Return value: + + NTSTATUS + +--*/ + +{ + ULONG CurrentEvents; + NTSTATUS Status = STATUS_PENDING; + + NT_ASSERT((Request->Command.TransferDirection == SdTransferDirectionRead) || + (Request->Command.TransferDirection == SdTransferDirectionWrite)); + + CurrentEvents = InterlockedExchange((PLONG)&SdhcExtension->CurrentEvents, 0); + + if (Request->Command.TransferDirection == SdTransferDirectionRead) { + SdhcReadDataPort(SdhcExtension, + Request->Command.DataBuffer, + Request->Command.BlockSize); + + } else { + SdhcWriteDataPort(SdhcExtension, + Request->Command.DataBuffer, + Request->Command.BlockSize); + } + + Request->Command.BlockCount -= 1; + if (Request->Command.BlockCount >= 1) { + Request->Command.DataBuffer += Request->Command.BlockSize; + if (Request->Command.TransferDirection == SdTransferDirectionRead) { + Request->RequiredEvents |= SDHC_IS_BUFFER_READ_READY; + + } else { + Request->RequiredEvents |= SDHC_IS_BUFFER_WRITE_READY; + } + + Request->Status = STATUS_MORE_PROCESSING_REQUIRED; + + } else { + + NT_ASSERT(Request->Command.BlockCount == 0); + + Request->Status = STATUS_SUCCESS; + if ((CurrentEvents & SDHC_IS_TRANSFER_COMPLETE) != 0) { + SdhcCompleteRequest(SdhcExtension, Request, STATUS_SUCCESS); + Status = STATUS_SUCCESS; + } + else { + Request->RequiredEvents |= SDHC_IS_TRANSFER_COMPLETE; + } + } + + return STATUS_PENDING; +} + +NTSTATUS +SdhcStartAdmaTransfer( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ PSDPORT_REQUEST Request + ) + +/*++ + +Routine Description: + + Execute the ADMA2 transfer request. + +Arguments: + + SdhcExtension - Host controller specific driver context. + + Request - The command for which we're building the transfer. + +Return value: + + NTSTATUS + +--*/ + +{ + + UNREFERENCED_PARAMETER(SdhcExtension); + + Request->Status = STATUS_SUCCESS; + SdhcCompleteRequest(SdhcExtension, Request, Request->Status); + return STATUS_SUCCESS; +} + +USHORT +SdhcCalcClockFrequency( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ ULONG TargetFrequency, + _Out_ PULONG ActualFrequency + ) + +/*++ + +Routine Description: + + Execute the ADMA2 transfer request. + +Arguments: + + SdhcExtension - Host controller specific driver context. + + TargetFrequency - The frequency in kHz to which we want to set the bus. + + ActualFrequency - The actual frequency to which the bus is set. + +Return value: + + ClockControl - The value of the Clock Control register to be written. + +--*/ + +{ + + ULONG BaseFrequency; + USHORT ClockControl; + ULONG Divisor; + USHORT SpecVersion; + + *ActualFrequency = 0; + BaseFrequency = SdhcExtension->Capabilities.BaseClockFrequencyKhz; + Divisor = MAX(BaseFrequency / TargetFrequency, 1); + + NT_ASSERT(Divisor > 0); + + SpecVersion = SdhcReadRegisterUshort(SdhcExtension, SDHC_VERSION) & 0xFF; + if (SpecVersion > SDHC_SPEC_VERSION_3) { + + // + // Calculate the fastest available clock frequency which is <= + // tthe requested frequency. + // + + Divisor = 1; + while (((BaseFrequency / Divisor) > TargetFrequency) && + (Divisor < SDHC_MAX_CLOCK_DIVISOR)) { + + Divisor <<= 1; + } + + *ActualFrequency = BaseFrequency / Divisor; + Divisor >>= 1; + ClockControl = ((USHORT) Divisor << 8); + + } else { + + // + // Host controller version 3.0 supports the 10-bit divided clock mode. + // + + Divisor = BaseFrequency / TargetFrequency; + Divisor >>= 1; + if ((TargetFrequency < BaseFrequency) && + (TargetFrequency * 2 * Divisor != BaseFrequency)) { + + Divisor += 1; + } + + if (Divisor > SDHC_MAX_CLOCK_DIVISOR_SPEC_3 / 2) { + Divisor = SDHC_MAX_CLOCK_DIVISOR_SPEC_3 / 2; + } + + if (Divisor == 0) { + *ActualFrequency = BaseFrequency; + + } else { + *ActualFrequency = BaseFrequency / Divisor; + *ActualFrequency >>= 1; + } + + ClockControl = ((USHORT) Divisor & 0xFF) << 8; + Divisor >>= 8; + ClockControl |= ((USHORT) Divisor & 0x03) << 6; + } + + NT_ASSERT((BaseFrequency <= TargetFrequency) ? (Divisor == 0) : TRUE); + + return ClockControl; +} + +NTSTATUS +SdhcCreateAdmaDescriptorTable( + _In_ PSDPORT_REQUEST Request, + _In_ BOOLEAN Use64BitDescriptor, + _Out_ PULONG TotalTransferLength + ) + +/*++ + +Routine Description: + + This routine creates a ADMA descriptor table based on scattor gatther list + given by Sglist. + +Arguments: + + Socket - Supplies the pointer to the socket + + Request - Data transfer request for which to build the descriptor table. + + TotalTransferLength - Supplies the pointer to return the total transfer + length of the descriptor table + +Return value: + + Whether the table was successfully created. + +--*/ + +{ + + PUCHAR Buffer; + PSDHC_ADMA2_DESCRIPTOR_TABLE_ENTRY Descriptor; + ULONG NumberOfElements; + PHYSICAL_ADDRESS NextAddress; + ULONG NextLength; + ULONG RemainingLength; + PSCATTER_GATHER_ELEMENT SglistElement; + + Buffer = (PUCHAR) Request->Command.DmaVirtualAddress; + Descriptor = NULL; + NumberOfElements = Request->Command.ScatterGatherList->NumberOfElements; + SglistElement = &Request->Command.ScatterGatherList->Elements[0]; + *TotalTransferLength = 0; + + NT_ASSERT(NumberOfElements > 0); + + // + // Iterate through each element in the SG list and convert it into the + // descriptor table required by the controller. + // + + while (NumberOfElements > 0) { + RemainingLength = SglistElement->Length; + NextAddress.QuadPart = SglistElement->Address.QuadPart; + + NT_ASSERT(RemainingLength > 0); + + while (RemainingLength > 0) { + Descriptor = (PSDHC_ADMA2_DESCRIPTOR_TABLE_ENTRY) Buffer; + Buffer += sizeof(SDHC_ADMA2_DESCRIPTOR_TABLE_ENTRY); + + NT_ASSERT((ULONG_PTR) Buffer < + (ULONG_PTR) Request->Command.DmaVirtualAddress + + Request->Command.Length); + + NextLength = MIN(SDHC_ADMA2_MAX_LENGTH_PER_ENTRY, RemainingLength); + RemainingLength -= NextLength; + + // + // Set the entry attributes and length. + // + + Descriptor->AsUlong = 0; + Descriptor->Action = SDHC_ADMA2_ACTION_TRAN; + Descriptor->Attribute = SDHC_ADMA2_ATTRIBUTE_VALID; + Descriptor->Length = NextLength; + *TotalTransferLength += NextLength; + + // + // Set the address field. + // + + if (Use64BitDescriptor) { + *((PULONGLONG) Buffer) = NextAddress.QuadPart; + Buffer += sizeof(LONGLONG); + + } else { + + // + // The HighPart should not be a non-zero value, since in the + // DMA adapter object, we declared that this device only + // supports 32-bit addressing. + // + + NT_ASSERT(NextAddress.HighPart == 0); + + *((PULONG) Buffer) = NextAddress.LowPart; + Buffer += sizeof(ULONG); + } + + NextAddress.QuadPart += NextLength; + } + + SglistElement += 1; + NumberOfElements -= 1; + + // + // Set the END bit if we're at the last element. + // + + if (NumberOfElements == 0) { + Descriptor->Attribute |= SDHC_ADMA2_ATTRIBUTE_END; + } + } + + return STATUS_SUCCESS; +} + +VOID +SdhcInitializePciConfigSpace( + _In_ PSD_MINIPORT Miniport + ) + +/*++ + +Routine Description: + + This routine updates the PCI configuration space. + +Arguments: + + Argument - Pointer to the functional device object extension for the SD host controller + +Return value: + + None. + +--*/ + +{ + + + + + + + + + if (Miniport->ConfigurationInfo.BusType != SdBusTypePci) { + return; + } + + // + // Some PCI based controllers require access to PCI configuration + // space to enable certain features. In order to do this, sdport + // provides two APIs, SdPortGetPciConfigSpace and + // SdPortSetPciConfigSpace to access various PCI configuration + // registers. For example, to access offset 0xA0 and unset bit + // 31 (hypothetically to turn on or off a feature): + // + // ULONG PciConfig; + // + // PciConfig = 0; + // SdPortGetPciConfigSpace(Miniport, + // 0xA0, + // (PUCHAR)&PciConfig, + // sizeof(PciConfig)); + // + // PciConfig &= ~(1 << 31); + // SdPortSetPciConfigSpace(Miniport, + // 0xA0, + // (PUCHAR)&PciConfig, + // sizeof(PciConfig)); + // + +} + +_Use_decl_annotations_ +VOID +SdhcCompleteRequest( + PSDHC_EXTENSION SdhcExtension, + PSDPORT_REQUEST Request, + NTSTATUS Status + ) + +/*++ + +Routine Description: + + SdhcCompleteRequest clears the miniport run times variables associated + with the request processing, and completes the request with SDPORT. + +Arguments: + + SdhcExtension - The miniport extension. + + Request - The request to completed. + + Status - The completion status. + +Return value: + + None. + +--*/ + +{ + const SDPORT_REQUEST* CurRequest; + const SDPORT_COMMAND* Command = &Request->Command; + BOOLEAN IsCommandCompleted = TRUE; + + // + // Data commands are done after all data has been + // transfered. + // + + if ((Command->TransferType != SdTransferTypeNone) && + (Command->TransferType != SdTransferTypeUndefined)) { + IsCommandCompleted = Command->BlockCount == 0; + } // if + + if (IsCommandCompleted) { + CurRequest = (const SDPORT_REQUEST*) + InterlockedExchangePointer( + (void *volatile *) & SdhcExtension->OutstandingRequest, NULL); + if (CurRequest != Request) { + NT_ASSERT(FALSE); + } // if + } // if + + SdPortCompleteRequest(Request, Status); +} // SdhcCompleteRequest (...) + +NONPAGED_SEGMENT_END; //====================================================== diff --git a/drivers/sd/dwcsdhc/dwcsdhc.h b/drivers/sd/dwcsdhc/dwcsdhc.h new file mode 100644 index 0000000..0662684 --- /dev/null +++ b/drivers/sd/dwcsdhc/dwcsdhc.h @@ -0,0 +1,2202 @@ +/*++ + +Copyright (c) 2014 Microsoft Corporation +Copyright (c) 2014 Greg Garbern (greggar) +Copyright (c) 2023 Mario Bălănică + +Module Name: + + dwcsdhc.h + +Abstract: + + Header for default standard host controller implementation. + +Environment: + + Kernel mode only. + +--*/ + +#if defined (_MSC_VER) && (_MSC_VER > 1000) +#pragma once +#endif + +#pragma warning(disable:4214) // bit field types other than int +#pragma warning(disable:4201) // nameless struct/union +#pragma warning(disable:4115) // named type definition in parentheses + +// +// RKCP0D40 Device Specific Method UUID +// +// {434addb0-8ff3-49d5-a724-95844b79ad1f} +// +DEFINE_GUID( + RKCP0D40_DSM_GUID, + 0x434addb0, 0x8ff3, 0x49d5, 0xa7, 0x24, 0x95, 0x84, 0x4b, 0x79, 0xad, 0x1f); + +// +// ACPI _DSM function to set card clock. +// +#define RKCP0D40_DSM_FUNCTION_IDX_SET_CARD_CLOCK 1 +#define RKCP0D40_DSM_FUNCTION_REVISION_SET_CARD_CLOCK 0 + +// +// Allocation tag +// +#define SDHC_ALLOC_TAG ULONG('ScwD') + +// +// MMIO length +// +#define SDHC_EXPECTED_ACPI_LENGTH 0x10000 + +// +// Memory registers +// + +#define SDHC_SYSADDR 0x00 +#define SDHC_BLOCK_SIZE 0x04 +#define SDHC_BLOCK_COUNT 0x06 +#define SDHC_ARGUMENT 0x08 +#define SDHC_TRANSFER_MODE 0x0c +#define SDHC_COMMAND 0x0e +#define SDHC_RESPONSE 0x10 +#define SDHC_DATA_PORT 0x20 +#define SDHC_PRESENT_STATE 0x24 +#define SDHC_HOST_CONTROL 0x28 +#define SDHC_POWER_CONTROL 0x29 +#define SDHC_BLOCKGAP_CONTROL 0x2a +#define SDHC_WAKEUP_CONTROL 0x2b +#define SDHC_CLOCK_CONTROL 0x2c +#define SDHC_TIMEOUT_CONTROL 0x2e +#define SDHC_RESET 0x2f +#define SDHC_INTERRUPT_STATUS 0x30 +#define SDHC_ERROR_STATUS 0x32 +#define SDHC_INTERRUPT_STATUS_ENABLE 0x34 +#define SDHC_ERROR_STATUS_ENABLE 0x36 +#define SDHC_INTERRUPT_SIGNAL_ENABLE 0x38 +#define SDHC_ERROR_SIGNAL_ENABLE 0x3a +#define SDHC_AUTO_CMD12_ERROR_STATUS 0x3c +#define SDHC_HOST_CONTROL2 0x3e +#define SDHC_CAPABILITIES 0x40 +#define SDHC_CAPABILITIES2 0x44 +#define SDHC_MAXIMUM_CURRENT 0x48 +#define SDHC_ADMA_ERROR_STATUS 0x54 +#define SDHC_ADMA_SYSADDR_LOW 0x58 +#define SDHC_ADMA_SYSADDR_HIGH 0x5c + + +#define SDHC_SLOT_INFORMATION 0xfc +#define SDHC_VERSION 0xfe + +// +// DWC specific registers +// + +#define DWCMSHC_HOST_CTRL3 0x508 +#define DWCMSHC_EMMC_CONTROL 0x52c + + +// +// Rockchip specific registers +// + +#define DWCMSHC_EMMC_DLL_CTRL 0x800 +#define DWCMSHC_EMMC_DLL_RXCLK 0x804 +#define DWCMSHC_EMMC_DLL_TXCLK 0x808 +#define DWCMSHC_EMMC_DLL_STRBIN 0x80c +#define DWCMSHC_EMMC_DLL_CMDOUT 0x810 +#define DWCMSHC_EMMC_DLL_STATUS0 0x840 +#define DWCMSHC_EMMC_DLL_STATUS1 0x844 + +// +// Bits defined in DWCMSHC_EMMC_CONTROL +// + +#define DWCMSHC_CARD_IS_EMMC (1 << 0) + +// +// Bits defined in DWCMSHC_EMMC_DLL_STRBIN +// + +#define DWCMSHC_EMMC_DLL_STRBIN_DLYENA (1 << 27) +#define DWCMSHC_EMMC_DLL_STRBIN_DELAY_NUM_SEL (1 << 26) +#define DWCMSHC_EMMC_DLL_STRBIN_DELAY_NUM_SHIFT 16 +#define DWCMSHC_EMMC_DLL_STRBIN_DELAY_NUM_DEFAULT 0x10 + +// +// Bits defined in SDHC_TRANSFER_MODE +// + +#define SDHC_TM_DMA_ENABLE 0x0001 +#define SDHC_TM_BLKCNT_ENABLE 0x0002 +#define SDHC_TM_AUTO_CMD12_ENABLE 0x0004 +#define SDHC_TM_AUTO_CMD23_ENABLE 0x0008 +#define SDHC_TM_TRANSFER_READ 0x0010 +#define SDHC_TM_MULTIBLOCK 0x0020 + +// +// Bits defined in SDHC_COMMAND +// + +#define SDHC_CMD_RESPONSE_136BIT 0x0001 +#define SDHC_CMD_RESPONSE_48BIT_NOBUSY 0x0002 +#define SDHC_CMD_RESPONSE_48BIT_WBUSY 0x0003 + +#define SDHC_CMD_CRC_CHECK_ENABLE 0x0008 +#define SDHC_CMD_INDEX_CHECK_ENABLE 0x0010 +#define SDHC_CMD_DATA_PRESENT 0x0020 + +#define SDHC_CMD_TYPE_SUSPEND 0x0040 +#define SDHC_CMD_TYPE_RESUME 0x0080 +#define SDHC_CMD_TYPE_ABORT 0x0060 + +// +// Bits defined in SDHC_PRESENT_STATE +// + +#define SDHC_PS_CMD_INHIBIT 0x00000001 +#define SDHC_PS_DAT_INHIBIT 0x00000002 +#define SDHC_PS_DAT_ACTIVE 0x00000004 +#define SDHC_PS_RETUNING_REQUEST 0x00000008 +#define SDHC_PS_WRITE_TRANSFER_ACTIVE 0x00000100 +#define SDHC_PS_READ_TRANSFER_ACTIVE 0x00000200 +#define SDHC_PS_BUFFER_WRITE_ENABLE 0x00000400 +#define SDHC_PS_BUFFER_READ_ENABLE 0x00000800 + +#define SDHC_PS_CARD_INSERTED 0x00010000 +#define SDHC_PS_CARD_STATE_STABLE 0x00020000 +#define SDHC_PS_CARD_DETECT 0x00040000 +#define SDHC_PS_WRITE_PROTECT 0x00080000 + +#define SDHC_PS_DAT0_SIGNAL 0x00100000 +#define SDHC_PS_DAT1_SIGNAL 0x00200000 +#define SDHC_PS_DAT2_SIGNAL 0x00400000 +#define SDHC_PS_DAT3_SIGNAL 0x00800000 + +#define SDHC_PS_DAT_3_0 (SDHC_PS_DAT0_SIGNAL | \ + SDHC_PS_DAT1_SIGNAL | \ + SDHC_PS_DAT2_SIGNAL | \ + SDHC_PS_DAT3_SIGNAL) + +#define SDHC_PS_CMD_SIGNAL 0x01000000 + +// +// Bits defined in SDHC_HOST_CONTROL +// + +#define SDHC_HC_LED_POWER 0x01 +#define SDHC_HC_DATA_WIDTH_4BIT 0x02 +#define SDHC_HC_ENABLE_HIGH_SPEED 0x04 +#define SDHC_HC_DATA_WIDTH_8BIT 0x20 + +#define SDHC_HC_DMA_SELECT_MASK 0x18 +#define SDHC_HC_DMA_SELECT_SDMA 0x00 +#define SDHC_HC_DMA_SELECT_ADMA32 0x10 +#define SDHC_HC_DMA_SELECT_ADMA64 0x18 + +// +// Bits defined in SDHC_HOST_CONTROL2 +// + +#define SDHC_HC2_SDR12 0x0000 +#define SDHC_HC2_SDR25 0x0001 +#define SDHC_HC2_SDR50 0x0002 +#define SDHC_HC2_SDR104 0x0003 +#define SDHC_HC2_DDR50 0x0004 +#define SDHC_HC2_HS200 0x0005 + +#define SDHC_HC2_UHS_MODES (SDHC_HC2_SDR12 | \ + SDHC_HC2_SDR25 | \ + SDHC_HC2_SDR50 | \ + SDHC_HC2_SDR104| \ + SDHC_HC2_DDR50 | \ + SDHC_HC2_HS200) + +#define SDHC_HC2_1_8V_SIGNALING 0x0008 + +#define SDHC_HC2_SELECT_TYPE_B 0x0000 +#define SDHC_HC2_SELECT_TYPE_A 0x0010 +#define SDHC_HC2_SELECT_TYPE_C 0x0020 +#define SDHC_HC2_SELECT_TYPE_D 0x0030 + +#define SDHC_HC2_EXECUTE_TUNING 0x0040 +#define SDHC_HC2_SELECT_SAMPLING_CLOCK 0x0080 +#define SDHC_HC2_ENABLE_ASYNC_INTERRUPT 0x4000 +#define SDHC_HC2_ENABLE_PRESET_VALUE 0x8000 + +// +// Bits defined in SDHC_POWER_CONTROL +// + +#define SDHC_PC_BUS_POWER 0x01 +#define SDHC_PC_1_8V 0x0a +#define SDHC_PC_3_0V 0x0c +#define SDHC_PC_3_3V 0x0e + +#define SDHC_PC_VOLTAGE_MASK 0x0e + +// +// Bits defined in SDHC_BLOCKGAP_CONTROL +// + +#define SDHC_BGC_STOP_NEXT_GAP 0x01 +#define SDHC_BGC_CONTINUE 0x02 +#define SDHC_BGC_READ_WAIT_ENABLE 0x04 +#define SDHC_BGC_INTERRUPT_ENABLE 0x08 + +// +// Bits defined in SDHC_WAKEUP_CONTROL +// + +#define SDHC_WC_CARD_INT_ENABLE 0x01 +#define SDHC_WC_CARD_INSERTION_ENABLE 0x02 +#define SDHC_WC_CARD_REMOVAL_ENABLE 0x04 +#define SDHC_WC_CARD_SUPPORTS_WAKEUP 0x80 + + +// +// Bits defined in SDHC_CLOCK_CONTROL +// + +#define SDHC_CC_INTERNAL_CLOCK_ENABLE 0x0001 +#define SDHC_CC_CLOCK_STABLE 0x0002 +#define SDHC_CC_CLOCK_ENABLE 0x0004 +#define SDHC_CC_CLOCK_DIVISOR_2 0x0100 +#define SDHC_CC_CLOCK_DIVISOR_4 0x0200 +#define SDHC_CC_CLOCK_DIVISOR_8 0x0400 +#define SDHC_CC_CLOCK_DIVISOR_16 0x0800 +#define SDHC_CC_CLOCK_DIVISOR_32 0x1000 +#define SDHC_CC_CLOCK_DIVISOR_64 0x2000 +#define SDHC_CC_CLOCK_DIVISOR_128 0x4000 +#define SDHC_CC_CLOCK_DIVISOR_256 0x8000 + + +// +// Bits defined in SDHC_TIMEOUT_CONTROL +// + +#define SDHC_TC_COUNTER_MASK 0x0f +#define SDHC_TC_MAX_DATA_TIMEOUT 0x0e + +// +// Bits defined in SDHC_RESET +// + +#define SDHC_RESET_ALL 0x01 +#define SDHC_RESET_CMD 0x02 +#define SDHC_RESET_DAT 0x04 + +// +// Bits defined in SDHC_INTERRUPT_STATUS +// SDHC_INTERRUPT_STATUS_ENABLE +// SDHC_INTERRUPT_SIGNAL_ENABLE +// + +#define SDHC_IS_CMD_COMPLETE 0x0001 +#define SDHC_IS_TRANSFER_COMPLETE 0x0002 +#define SDHC_IS_BLOCKGAP_EVENT 0x0004 +#define SDHC_IS_DMA_EVENT 0x0008 +#define SDHC_IS_BUFFER_WRITE_READY 0x0010 +#define SDHC_IS_BUFFER_READ_READY 0x0020 +#define SDHC_IS_CARD_INSERTION 0x0040 +#define SDHC_IS_CARD_REMOVAL 0x0080 +#define SDHC_IS_CARD_INTERRUPT 0x0100 +#define SDHC_IS_TUNING_INTERRUPT 0x1000 + +#define SDHC_IS_ERROR_INTERRUPT 0x8000 + +#define SDHC_IS_CARD_DETECT (SDHC_IS_CARD_INSERTION | SDHC_IS_CARD_REMOVAL) + +// +// Bits defined in SDHC_ERROR_STATUS +// Bits defined in SDHC_ERROR_STATUS_ENABLE +// Bits defined in SDHC_ERROR_SIGNAL_ENABLE +// +// PLEASE NOTE: these values need to match exactly the +// "generic" values in sdbus.h +// + +#define SDHC_ES_CMD_TIMEOUT 0x0001 +#define SDHC_ES_CMD_CRC_ERROR 0x0002 +#define SDHC_ES_CMD_END_BIT_ERROR 0x0004 +#define SDHC_ES_CMD_INDEX_ERROR 0x0008 +#define SDHC_ES_DATA_TIMEOUT 0x0010 +#define SDHC_ES_DATA_CRC_ERROR 0x0020 +#define SDHC_ES_DATA_END_BIT_ERROR 0x0040 +#define SDHC_ES_BUS_POWER_ERROR 0x0080 +#define SDHC_ES_AUTO_CMD12_ERROR 0x0100 +#define SDHC_ES_ADMA_ERROR 0x0200 +#define SDHC_ES_BAD_DATA_SPACE_ACCESS 0x2000 + +// +// Bits defined in SDHC_AUTO_CMD12_ERROR_STATUS +// + +#define SDHC_ACMD12_NOT_EXECUTED 0x0001 +#define SDHC_ACMD12_RESPONSE_TIMEOUT 0x0002 +#define SDHC_ACMD12_RESPONSE_CRC_ERROR 0x0004 +#define SDHC_ACMD12_END_BIT_ERROR 0x0008 +#define SDHC_ACMD12_INDEX_ERROR 0x0010 +#define SDHC_ACMD12_CWODAT_NOT_EXECUTED 0x0080 + + +#define SDHC_MAX_CLOCK_DIVISOR 256 +#define SDHC_MAX_CLOCK_DIVISOR_SPEC_3 2046 + + +// +// determines the poll interval for host reset and clock setting (10ms) +// + +#define SDHC_DEFAULT_POLL_INTERVAL 10000 + +// +// determines the poll count (10 ms * 100 tries = 1 second) +// + +#define SDHC_DEFAULT_POLL_COUNT 100 + +#define SDHC_MAX_SET_POWER_RETRIES 5 + +#define SDHC_ALIGNMENT_SDMA 1 +#define SDHC_ALIGNMENT_ADMA2 3 +#define SDHC_ALIGNMENT_ADMA2_64 7 + +#define SDHC_SPEC_VERSION_1 0 +#define SDHC_SPEC_VERSION_2 1 +#define SDHC_SPEC_VERSION_3 2 + +//---------------------------------------------------------------------------- +// Host register layout +//---------------------------------------------------------------------------- + +// --------------------------------------------------------------------------- +// +// SD card information +// +// --------------------------------------------------------------------------- + +#pragma pack(push) +#pragma pack(1) + +// +// The entire layout of an SD host controller in memory for debug purposes. +// + +typedef struct _SD_HOST_CONTROLLER_REGISTERS { + + union { + struct { + USHORT Low; + USHORT High; + } Argument2; + struct { + USHORT Low; + USHORT High; + } SDMAAddress; + } Argument2; + + USHORT BlockSize; // 04h + + USHORT BlockCount; // 06h + + union { + struct { + USHORT Low; // 08h + USHORT High; // 0Ah + } Argument1; + ULONG Ulong; + } Argument1; + + union { + struct { + USHORT DMAEnable : 1; + USHORT BlockCountEnable : 1; + USHORT AutoCommandEnable : 2; + USHORT DataTransferDirectionSelect : 1; + USHORT MultiBlockSelect : 1; + USHORT Reserved0 : 10; + } Flags; + USHORT Value; // 0Ch + } TransferMode; + + union { + struct { + USHORT ResponseTypeSelect : 2; + USHORT Reserved0 : 1; + USHORT CommandCRCCheckEnable : 1; + USHORT CommandIndexCheckEnable : 1; + USHORT DataPresentSelect : 1; + USHORT CommandType : 2; + USHORT CommandIndex : 6; + USHORT Reserved1 : 2; + } Flags; + USHORT Value; // 0Eh + } Command; + + union { + + struct { + union { + struct { + ULONG ReservedManufacturerTest0 : 1; + ULONG ReservedManufacturerTest1 : 1; + ULONG ReservedAppspecific : 1; + ULONG AKE_SEQ_ERROR : 1; + ULONG ReservedSDIO : 1; + ULONG APP_CMD : 1; + ULONG Reserved0 : 2; + ULONG READY_FOR_DATA : 1; + ULONG CURRENT_STATE : 4; + ULONG ERASE_RESSET : 1; + ULONG CARD_ECC_DISABLE : 1; + ULONG WP_ERASE_SKIP : 1; + ULONG CSD_OVERWRITE : 1; + ULONG RESERVED_DEFERRED_RESPONSE : 1; + ULONG RESERVED : 1; + ULONG ERROR : 1; + ULONG CC_ERROR : 1; + ULONG CARD_ECC_FAILED : 1; + ULONG ILLEGAL_COMMAND : 1; + ULONG COM_CRC_ERROR : 1; + ULONG LOCK_UNLOCK_FAILED : 1; + ULONG CARD_IS_LOCKED : 1; + ULONG WP_VIOLATION : 1; + ULONG ERASE_PARAM : 1; + ULONG ERASE_SEQ_ERROR : 1; + ULONG BLOCK_LEN_ERROR : 1; + ULONG ADDRESS_ERROR : 1; + ULONG OUT_OF_RANGE : 1; + } Flags; + ULONG Ulong; + } CardStatus; + } R1; + + struct { + ULONG Reserved0[3]; + ULONG CardStatus; + } R1b; + + struct { + union { + + struct { + USHORT ManufacturingData : 12; + USHORT Reserved0 : 4; + ULONG SerialNumber; + UCHAR Revision; + UCHAR ProductName[5]; + USHORT ApplicationID; + UCHAR ManufactureID; + } CID; + + struct { + UCHAR Reserved0 : 2; + UCHAR FILE_FORMAT : 2; + UCHAR TMP_WRITE_PROTECT : 1; + UCHAR PERM_WRITE_PROTECT : 1; + UCHAR COPY : 1; + UCHAR FILE_FORMAT_GRP : 1; + + USHORT Reserved1 : 5; + USHORT WRITE_BL_PARTIAL : 1; + USHORT WRITE_BL_LEN : 4; + USHORT R2W_FACTOR : 3; + USHORT Reserved2 : 2; + USHORT WP_GRP_ENABLE : 1; + + ULONG WP_GRP_SIZE : 7; + ULONG SECTOR_SIZE : 7; + ULONG ERASE_BLK_EN : 1; + ULONG C_SIZE_MULT : 3; + ULONG VDD_W_CURR_MAX : 3; + ULONG VDD_W_CURR_MIN : 3; + ULONG VDD_R_CURR_MAX : 3; + ULONG VDD_R_CURR_MIN : 3; + ULONG C_SIZE_LOW : 2; + + ULONG C_SIZE_HIGH : 10; + ULONG Reserved3 : 2; + ULONG DSR_IMP : 1; + ULONG READ_BLK_MISALIGN : 1; + ULONG WRITE_BLK_MISALIGN : 1; + ULONG READ_BL_PARTIAL : 1; + ULONG READ_BL_LEN : 4; + ULONG CCC : 12; + + UCHAR TRAN_SPEED; + UCHAR NSAC; + UCHAR TAAC; + + UCHAR Reserved4 : 6; + UCHAR CSD_STRUCTURE : 2; + } CSDv1; + + struct { + UCHAR Reserved0 : 2; + UCHAR FILE_FORMAT : 2; + UCHAR TMP_WRITE_PROTECT : 1; + UCHAR PERM_WRITE_PROTECT : 1; + UCHAR COPY : 1; + UCHAR FILE_FORMAT_GRP : 1; + + USHORT Reserved1 : 5; + USHORT WRITE_BL_PARTIAL : 1; + USHORT WRITE_BL_LEN : 4; + USHORT R2W_FACTOR : 3; + USHORT Reserved2 : 2; + USHORT WP_GRP_ENABLE : 1; + + ULONG WP_GRP_SIZE : 7; + ULONG SECTOR_SIZE : 7; + ULONG ERASE_BLK_EN : 1; + ULONG Reserved3 : 1; + ULONG DeviceSizeLow : 16; + + ULONG DeviceSizeHigh : 6; + ULONG Reserved4 : 6; + ULONG DSR_IMP : 1; + ULONG READ_BLK_MISALIGN : 1; + ULONG WRITE_BLK_MISALIGN : 1; + ULONG READ_BL_PARTIAL : 1; + ULONG READ_BL_LEN : 4; + ULONG CCC : 12; + + UCHAR TRAN_SPEED : 8; + UCHAR NSAC : 8; + UCHAR TAAC : 8; + UCHAR Reserved5 : 6; + UCHAR CSD_STRUCTURE : 2; + } CSDv2; + } Register; + } R2; + + struct { + union { + struct { + ULONG Reserved0 : 7; + ULONG ReservedLowVoltageRange : 1; + ULONG Reserved1 : 7; + ULONG Voltage27V_28V : 1; + ULONG Voltage28V_29V : 1; + ULONG Voltage29V_30V : 1; + ULONG Voltage30V_31V : 1; + ULONG Voltage31V_32V : 1; + ULONG Voltage32V_33V : 1; + ULONG Voltage33V_34V : 1; + ULONG Voltage34V_35V : 1; + ULONG Voltage35V_36V : 1; + ULONG SwitchingTo18VAccepted : 1; + ULONG Reserved2 : 5; + ULONG CardCapacityStatus : 1; + ULONG CardPowerUpStatus : 1; + } Flags; + ULONG ULong; + } Ocr; + } R3; + + struct { + UCHAR Buffer[16]; + } R4; + + struct { + UCHAR Buffer[16]; + } R5; + + struct { + union { + struct { + USHORT ReservedManufacturerTest0 : 1; + USHORT ReservedManufacturerTest1 : 1; + USHORT ReservedAppspecific : 1; + USHORT AKE_SEQ_ERROR : 1; + USHORT ReservedSDIO : 1; + USHORT APP_CMDEnable : 1; + USHORT Reserved0 : 2; + USHORT READY_FOR_DATA : 1; + USHORT CurrentState : 4; + + USHORT ERROR : 1; + USHORT ILLEGAL_COMMAND : 1; + USHORT COM_CRC_ERROR : 1; + } Flags; + USHORT Register; + } CardStatusBits; + + USHORT NewRCA : 16; + } R6; + + struct { + ULONG CheckPattern : 8; + ULONG VoltageAccepted : 4; + ULONG Reserved : 20; + } R7; + + struct { + USHORT Response0; // 10h + USHORT Response1; // 12h + USHORT Response2; // 14h + USHORT Response3; // 16h + USHORT Response4; // 18h + USHORT Response5; // 1Ah + USHORT Response6; // 1Ch + USHORT Response7; // 1Eh + } Words; + UCHAR Buffer[16]; + } Response; + + union { + struct { + USHORT BufferDataPort0; // 20h + USHORT BufferDataPort1; // 22h + } Words; + ULONG Port; + } BufferDataPort; + + union { + struct { + USHORT PresentState0; // 24h + USHORT PresentState1; // 26h + } Words; + struct { + ULONG CommandInhibitCMD : 1; + ULONG CommandInhibitDAT : 1; + ULONG DATLineActive : 1; + ULONG ReTuningRequest : 1; + ULONG Reserved0 : 4; + ULONG WriteTransferActive : 1; + ULONG ReadTransferActive : 1; + ULONG BufferWriteEnable : 1; + ULONG BufferReadEnable : 1; + ULONG Reserved1 : 4; + ULONG CardInserted : 1; + ULONG CardStateStable : 1; + ULONG CardDetectPinLevel : 1; + ULONG WriteProtectSwitchPinLevel : 1; + ULONG DATLineSignalLevel : 4; + ULONG CMDLineSignalLevel : 1; + ULONG Reserved2 : 7; + } Flags; + } PresentState; + + union { + struct { + UCHAR LEDControlOn : 1; + UCHAR DataTransferWidth4Bit : 1; + UCHAR HighSpeedEnable : 1; + UCHAR DMASelect : 2; + UCHAR ExtendedDataTransferWidth8Bit : 1; + UCHAR CardDetectTestLevelCardInserted : 1; + UCHAR CardDetectSignalSelectionEnable : 1; + } Flags; + UCHAR Byte; // 28h + } HostControl1; + + union { + struct { + UCHAR SDBusPowerOn : 1; + UCHAR SDBUSVoltageSelect : 3; + UCHAR Reserved0 : 4; + } Flags; + UCHAR Byte; // 29h + } PowerControl; + + union { + struct { + UCHAR StopAtBlockGapRequest : 1; + UCHAR ContinueRequest : 1; + UCHAR ReadWaitControlEnable : 1; + UCHAR InterruptAtBlockGapEnable : 1; + UCHAR Reserved0 : 4; + } Flags; + UCHAR Byte; // 2Ah + } BlockGapControl; + + union { + struct { + UCHAR WakeupEventEnableOnSDCardInterrupt : 1; + UCHAR WakeupEventEnableOnSDCardInsertion : 1; + UCHAR WakeupEventEnableOnSDCardRemoval : 1; + UCHAR Reserved0 : 5; + } Flags; + UCHAR Byte; // 2Bh + } WakeupControl; + + union { + struct { + USHORT InternalClockEnable : 1; + USHORT InternalClockStable : 1; + USHORT SDClockEnable : 1; + USHORT Reserved0 : 2; + USHORT ClockGeneratorSelectProgrammable : 1; + USHORT FrequencySelectUpperBits : 2; + USHORT FrequencySelectLowerBits : 8; + } Flags; + USHORT Word; // 2Ch + } ClockControl; + + union { + struct { + UCHAR DataTimeoutCounterValue : 4; + UCHAR Reserved0 : 4; + } Flags; + UCHAR Byte; // 2Eh + } TimeoutControl; + + union { + struct { + UCHAR SoftwareResetForAll : 1; + UCHAR SoftwareResetForCMDLine : 1; + UCHAR SoftwareResetForDATLine : 1; + UCHAR Reserved0 : 5; + } Flags; + UCHAR Byte; // 2Fh + } SoftwareReset; + + union { + struct { + USHORT CommandComplete : 1; + USHORT TransferComplete : 1; + USHORT BlockGapEvent : 1; + USHORT DMAInterrupt : 1; + USHORT BufferWriteReady : 1; + USHORT BufferReadReady : 1; + USHORT CardInsertion : 1; + USHORT CardRemoval : 1; + USHORT CardInterrupt : 1; + USHORT INT_A : 1; + USHORT INT_B : 1; + USHORT INT_C : 1; + USHORT RetuningEvent : 1; + USHORT Reserved0 : 2; + USHORT ErrorInterrupt : 1; + } Flags; + USHORT Word; + } NormalInterruptStatus; + + union { + struct { + USHORT CommandTimeoutError : 1; + USHORT CommandCRCError : 1; + USHORT CommandEndBitError : 1; + USHORT CommandIndexError : 1; + USHORT DataTimeoutError : 1; + USHORT DataCRCError : 1; + USHORT DataEndBitError : 1; + USHORT CurrentLimitError : 1; + USHORT AutoCMD12Error : 1; + USHORT ADMAError : 1; + USHORT TuningError : 1; + USHORT Reserved0 : 1; + USHORT VendorSpecific : 4; + } Flags; + USHORT Word; + } ErrorInterruptStatus; + + union { + struct { + USHORT CommandCompleteStatusEnable : 1; + USHORT TransferCompleteStatusEnable : 1; + USHORT BlockGapEventStatusEnable : 1; + USHORT DMAInterruptStatusEnable : 1; + USHORT BufferWriteReadyStatusEnable : 1; + USHORT BufferReadReadyStatusEnable : 1; + USHORT CardInsertionStatusEnable : 1; + USHORT CardRemovalStatusEnable : 1; + USHORT CardInterruptStatusEnable : 1; + USHORT INT_AStatusEnable : 1; + USHORT INT_BStatusEnable : 1; + USHORT INT_CStatusEnable : 1; + USHORT RetuningEventStatusEnable : 1; + USHORT Reserved0 : 2; + USHORT FixedToZero : 1; + } Flags; + USHORT Word; // 34h + } NormalInterruptStatusEnable; + + union { + struct { + USHORT CommandTimeoutErrorStatusEnable : 1; + USHORT CommandCRCErrorStatusEnable : 1; + USHORT CommandEndBitErrorStatusEnable : 1; + USHORT CommandIndexErrorStatusEnable : 1; + USHORT DataTimeoutErrorStatusEnable : 1; + USHORT DataCRCErrorStatusEnable : 1; + USHORT DataEndBitErrorStatusEnable : 1; + USHORT CurrentLimitErrorStatusEnable : 1; + USHORT AutoCMD12ErrorStatusEnable : 1; + USHORT ADMAErrorStatusEnable : 1; + USHORT TuningErrorStatusEnable : 1; + USHORT Reserved0 : 1; + USHORT VendorSpecificStatusEnable : 4; + } Flags; + USHORT Word; + } ErrorInterruptStatusEnable; // 36h + + union { + struct { + USHORT CommandCompleteSignalEnable : 1; + USHORT TransferCompleteSignalEnable : 1; + USHORT BlockGapEventSignalEnable : 1; + USHORT DMAInterruptSignalEnable : 1; + USHORT BufferWriteReadySignalEnable : 1; + USHORT BufferReadReadySignalEnable : 1; + USHORT CardInsertionSignalEnable : 1; + USHORT CardRemovalSignalEnable : 1; + USHORT CardInterruptSignalEnable : 1; + USHORT INT_ASignalEnable : 1; + USHORT INT_BSignalEnable : 1; + USHORT INT_CSignalEnable : 1; + USHORT RetuningEventSignalEnable : 1; + USHORT Reserved0 : 2; + USHORT FixedToZero : 1; + } Flags; + USHORT Word; + } NormalInterruptSignalEnable; // 38h + + union { + struct { + USHORT CommandTimeoutErrorSignalEnable : 1; + USHORT CommandCRCErrorSignalEnable : 1; + USHORT CommandEndBitErrorSignalEnable : 1; + USHORT CommandIndexErrorSignalEnable : 1; + USHORT DataTimeoutErrorSignalEnable : 1; + USHORT DataCRCErrorSignalEnable : 1; + USHORT DataEndBitErrorSignalEnable : 1; + USHORT CurrentLimitErrorSignalEnable : 1; + USHORT AutoCMD12ErrorSignalEnable : 1; + USHORT ADMAErrorSignalEnable : 1; + USHORT TuningErrorSignalEnable : 1; + USHORT Reserved0 : 1; + USHORT VendorSpecificSignalEnable : 4; + } Flags; + USHORT Word; + } ErrorInterruptSignalEnable; // 3Ah + + union { + struct { + USHORT AutoCMD12NotExecuted : 1; + USHORT AutoCMD12TimeoutError : 1; + USHORT AutoCMD12CRCError : 1; + USHORT AutoCMD12EndBitError : 1; + USHORT AutoCMDIndexError : 1; + USHORT Reserved0 : 2; + USHORT CommandNotIssuedByAutoCMD12Error : 1; + USHORT Reserved1 : 8; + } Flags; + USHORT Word; + } AutoCMDErrorStatus; // 3Ch + + union { + struct { + USHORT UHSModeSelect : 3; + USHORT Signalling18V : 1; + USHORT DriverStrengthSelect : 2; + USHORT ExecuteTuning : 1; + USHORT SamplingClockSelect : 1; + USHORT Reserved : 6; + USHORT AsynchronousInterruptEnable : 1; + USHORT PresetValueEnable : 1; + } Flags; + USHORT Word; + } HostControl2; // 3Eh + + union { + struct { + ULONGLONG TimeoutClockFrequency : 6; + ULONGLONG Reserved0 : 1; + ULONGLONG TimeoutClockUnit : 1; + ULONGLONG BaseClockFrequencyForSDClock : 8; + ULONGLONG MaxBlockLength : 2; + ULONGLONG EmbeddedDrive8Bit : 1; + ULONGLONG ADMA2SUpport : 1; + ULONGLONG Reserved1 : 1; + ULONGLONG HighSpeedSupport : 1; + ULONGLONG SDMASupport : 1; + ULONGLONG SuspendResumeSupport : 1; + ULONGLONG VoltageSupport33 : 1; + ULONGLONG VoltageSupport30 : 1; + ULONGLONG VoltageSupport18 : 1; + ULONGLONG Reserved2 : 1; + ULONGLONG SystemBus64bitSupport : 1; + ULONGLONG AsynchronousInterruptSupport : 1; + ULONGLONG SlotType : 2; + ULONGLONG SDR50Support : 1; + ULONGLONG SDR104Support : 1; + ULONGLONG DDR50Support : 1; + ULONGLONG Reserved3 : 1; + ULONGLONG DriverTypeASupport : 1; + ULONGLONG DriverTypeCSupport : 1; + ULONGLONG DriverTypeDSupport : 1; + ULONGLONG Reserved4 : 1; + ULONGLONG TimerCountForRetuning : 4; + ULONGLONG Reserved5 : 1; + ULONGLONG UseTuningForSDR50 : 1; + ULONGLONG RetuningModes : 2; + ULONGLONG ClockMultiplier : 8; + ULONGLONG Reserved6 : 8; + } Flags; + struct { + USHORT Capabilities0; // 40h + USHORT Capabilities1; // 42h + USHORT Capabilities2; // 44h + USHORT Capabilities3; // 46h + } Words; + ULONGLONG ULongLong; + } Capabilities; + + union { + struct { + ULONGLONG MaxCurrent33V : 8; + ULONGLONG MaxCurrent30V : 8; + ULONGLONG MaxCurrent18V : 8; + ULONGLONG Reserved0 : 40; + } Flags; + struct { + USHORT MaxCurrentCaps0; // 48h + USHORT MaxCurrentCaps1; // 4Ah + USHORT MaxCurrentCaps2; // 4Ch + USHORT MaxCurrentCaps3; // 4Eh + } Words; + ULONGLONG ULongLong; + } MaxCurrentCaps; + + union { + struct { + USHORT ForceEventForAutoCMD12NotExecuted : 1; + USHORT ForceEventForAutoCMD12TimeoutError : 1; + USHORT ForceEventForAutoCMD12CRCError : 1; + USHORT ForceEventForAutoCMD12EndBitError : 1; + USHORT ForceEventForAutoCMD12IndexError : 1; + USHORT Reserved0 : 2; + USHORT ForceEventForCommandNotIssuedByAutoCMD12Error : 1; + USHORT Reserved1 : 8; + } Flags; + USHORT Word; // 50h + } ForceEventAutoCMDError; + + union { + struct { + USHORT ForceCommandTimeoutError : 1; + USHORT ForceCommandCRCError : 1; + USHORT ForceCommandEndBitError : 1; + USHORT ForceCommandIndexError : 1; + USHORT ForceDataTimeoutError : 1; + USHORT ForceDataCRCError : 1; + USHORT ForceDataEndBitError : 1; + USHORT ForceCurrentLimitError : 1; + USHORT ForceAutoCMD12Error : 1; + USHORT ForceADMAError : 1; + USHORT Reserved0 : 2; + USHORT VendorSpecific : 4; + } Flags; + USHORT Word; // 52h + } ForceEventInterruptError; + + union { + struct { + UCHAR ADMAErrorStates : 2; + UCHAR ADMALengthMismatchError : 1; + UCHAR Reserved0 : 5; + } Flags; + UCHAR Byte; + } ADMAErrorStatus; + + UCHAR Reserved0; // 55h + USHORT Reserved1; // 56h + + union { + struct { + USHORT ADMASystemAddress0; // 58h + USHORT ADMASystemAddress1; // 5Ah + USHORT ADMASystemAddress2; // 5Ch + USHORT ADMASystemAddress3; // 5Eh + } Words; + ULONGLONG ULongLong; + } ADMASystemAddress; + + union { + struct { + USHORT SDCLKFrequencySelectValue : 10; + USHORT ClockGeneratorSelectValue : 1; + USHORT Reserved0 : 3; + USHORT DriverStrengthSelectValue : 2; + } Flags; + USHORT Word; + } PresetValueInit; // 60h + + union { + struct { + USHORT SDCLKFrequencySelectValue : 10; + USHORT ClockGeneratorSelectValue : 1; + USHORT Reserved0 : 3; + USHORT DriverStrengthSelectValue : 2; + } Flags; + USHORT Word; + } PresetValueDefaultSpeed; // 62h + + union { + struct { + USHORT SDCLKFrequencySelectValue : 10; + USHORT ClockGeneratorSelectValue : 1; + USHORT Reserved0 : 3; + USHORT DriverStrengthSelectValue : 2; + } Flags; + USHORT Word; + } PresetValueHighSpeed; // 64h + + union { + struct { + USHORT SDCLKFrequencySelectValue : 10; + USHORT ClockGeneratorSelectValue : 1; + USHORT Reserved0 : 3; + USHORT DriverStrengthSelectValue : 2; + } Flags; + USHORT Word; + } PresetValueSDR12; // 66h + + union { + struct { + USHORT SDCLKFrequencySelectValue : 10; + USHORT ClockGeneratorSelectValue : 1; + USHORT Reserved0 : 3; + USHORT DriverStrengthSelectValue : 2; + } Flags; + USHORT Word; + } PresetValueSDR25; // 68h + + union { + struct { + USHORT SDCLKFrequencySelectValue : 10; + USHORT ClockGeneratorSelectValue : 1; + USHORT Reserved0 : 3; + USHORT DriverStrengthSelectValue : 2; + } Flags; + USHORT Word; + } PresetValueSDR50; // 6Ah + + union { + struct { + USHORT SDCLKFrequencySelectValue : 10; + USHORT ClockGeneratorSelectValue : 1; + USHORT Reserved0 : 3; + USHORT DriverStrengthSelectValue : 2; + } Flags; + USHORT Word; + } PresetValueSDR104; // 6Ch + + union { + struct { + USHORT SDCLKFrequencySelectValue : 10; + USHORT ClockGeneratorSelectValue : 1; + USHORT Reserved0 : 3; + USHORT DriverStrengthSelectValue : 2; + } Flags; + USHORT Word; + } PresetValueDDR50; // 6Eh + + UCHAR Reserved2[112]; // 70h - DFh + + union { + struct { + ULONG NumberOfClockPins : 3; + ULONG Reserved0 : 1; + ULONG NumberOfInterruptInputPins : 2; + ULONG Reserved1 : 2; + ULONG BusWidthPreset : 7; + ULONG Reserved2 : 1; + ULONG ClockPinSelect : 3; + ULONG Reserved3 : 1; + ULONG InterruptPinSelect : 3; + ULONG Reserved4 : 1; + ULONG BackEndPowerControl : 7; + ULONG Reserved5 : 1; + } Flags; + struct { + USHORT Low; // E0h + USHORT High; // E2h + } Words; + ULONG Ulong; + } SharedBusControl; + + UCHAR Reserved3[12]; // E4h - EFh + UCHAR Reserved4[12]; // F0h - FBh + + union { + struct { + USHORT Slot1 : 1; + USHORT Slot2 : 1; + USHORT Slot3 : 1; + USHORT Slot4 : 1; + USHORT Slot5 : 1; + USHORT Slot6 : 1; + USHORT Slot7 : 1; + USHORT Slot8 : 1; + USHORT Reserved0 : 8; + } Flags; + USHORT Word; // FCh + } SlotInterruptStatus; + + union { + struct { + USHORT SpecificationVersionNumber : 8; + USHORT VendorVersionNumber : 8; + } Flags; + USHORT Word; // FEh + } HostControllerVersion; + +} SD_HOST_CONTROLLER_REGISTERS, *PSD_HOST_CONTROLLER_REGISTERS; + +// the size of the SD_CONTROLLER_REGISTER structure must always be 256 bytes +C_ASSERT(sizeof(SD_HOST_CONTROLLER_REGISTERS) == 256); + +#pragma pack(pop) + +// --------------------------------------------------------------------------- +// +// Register layout definitions +// +// --------------------------------------------------------------------------- + +#pragma pack(1) + +// +// Host control register +// + +typedef union _SDHC_HOST_CONTROL_REGISTER { + struct { + UCHAR LedControl:1; + UCHAR DataTransferWidth:1; + UCHAR HgihSpeedEnable:1; + UCHAR DmaSelect:2; + UCHAR Reserved1:1; + UCHAR CardDetectTestLevel:1; + UCHAR CardDetectSignalSlection:1; + }; + UCHAR AsUchar; +} SDHC_HOST_CONTROL_REGISTER, *PSDHC_HOST_CONTROL_REGISTER; + +typedef union _SDHC_HOST_CONTROL2_REGISTER { + struct { + USHORT UhsModeSelect:3; + USHORT SignalingEnable1_8V:1; + USHORT DriverStrengthSelect:2; + USHORT ExecuteTuning:1; + USHORT SamplingClockSelect:1; + USHORT Reserved:6; + USHORT AsyncInterruptEnable:1; + USHORT PresetValueEnable:1; + }; + USHORT AsUshort; +} SDHC_HOST_CONTROL2_REGISTER, *PSDHC_HOST_CONTROL2_REGISTER; + + +// +// Power control register +// + +typedef union _SDHC_POWER_CONTROL_REGISTER { + struct { + UCHAR SdBusPower:1; + UCHAR SdBusPowerSelect:3; + UCHAR Reserved1:4; + }; + UCHAR AsUchar; +} SDHC_POWER_CONTROL_REGISTER, *PSDHC_POWER_CONTROL_REGISTER; + + +// +// Clock control register +// + +typedef union _SDHC_CLOCK_CONTROL_REGISTER { + struct { + USHORT InternalClockEnable:1; + USHORT InternalClockStable:1; + USHORT SdClockEnable:1; + USHORT Reserved1:4; + USHORT SdclkFrequencySelect:8; + }; + UCHAR AsUshort; +} SDHC_CLOCK_CONTROL_REGISTER, *PSDHC_CLOCK_CONTROL_REGISTER; + + +// +// Transfer mode register +// + +typedef union _SDHC_TRANSFER_MODE_REGISTER { + struct { + USHORT DmaEnable:1; + USHORT BlockCountEnable:1; + USHORT AutoCmd12Enable:1; + USHORT AutoCmd23Enable:1; + USHORT DataTranferDirectionSelect:1; + USHORT BlockModeSelect:1; + USHORT Reserved2:10; + }; + USHORT AsUshort; +} SDHC_TRANSFER_MODE_REGISTER, *PSDHC_TRANSFER_MODE_REGISTER; + + +// +// Capabilities register +// + +typedef union _SDHC_CAPABILITIES_REGISTER { + struct { + ULONG TimeoutClockFrequency:6; + ULONG Reserved1:1; + ULONG TimeoutClockUnit:1; + ULONG BaseClockFrequency:8; + ULONG MaxBlockLength:2; + ULONG Support8BitBus:1; + ULONG Adma2Support:1; + ULONG Reserved2:1; + ULONG HighSpeedSupport:1; + ULONG DmaSupport:1; + ULONG SuspendResumeSupport:1; + ULONG Voltage33:1; + ULONG Voltage30:1; + ULONG Voltage18:1; + ULONG Reserved3:1; + ULONG SystemBus64Support:1; + ULONG AsynchronousInterruptSupport:1; + ULONG SlotType:2; + }; + ULONG AsUlong; +} SDHC_CAPABILITIES_REGISTER, *PSDHC_CAPABILITIES_REGISTER; + +typedef union _SDHC_CAPABILITIES2_REGISTER { + struct { + ULONG SDR50Support:1; + ULONG SDR104Support:1; + ULONG DDR50Support:1; + ULONG Reserved1:1; + ULONG DriverTypeA:1; + ULONG DriverTypeC:1; + ULONG DriverTypeD:1; + ULONG Reserved2:1; + ULONG RetuningTimerCount:4; + ULONG Reserved3:1; + ULONG UseTuningForSDR50:1; + ULONG RetuningModes:2; + ULONG ClockMultiplier:8; + ULONG Reserved4:8; + }; + ULONG AsUlong; +} SDHC_CAPABILITIES2_REGISTER, *PSDHC_CAPABILITIES2_REGISTER; + +// --------------------------------------------------------------------------- +// +// ADMA2 descriptor table definitions +// +// --------------------------------------------------------------------------- + +// +// Bits defined in descriptor->attribute +// + +#define SDHC_ADMA2_ATTRIBUTE_VALID 0x00000001 +#define SDHC_ADMA2_ATTRIBUTE_END 0x00000002 +#define SDHC_ADMA2_ATTRIBUTE_INT 0x00000004 + +// +// Bits defined in descriptor->Action +// + +#define SDHC_ADMA2_ACTION_NOP 0x00000000 +#define SDHC_ADMA2_ACTION_TRAN 0x00000002 +#define SDHC_ADMA2_ACTION_LINK 0x00000003 + +// +// Max transfer length per descriptor entry +// +// SD Host Controller Spec 1.13.4 +// Length field is 16 bit field so we can go up to 64K (0xFFFF). +// However, we need to limit max length to 0xF000 because using 0xFFFF +// can cause un-aligned, In addition, some HC require the length to be +// multiple of 0x1000 (except 1st and last descriptor) +// +#define SDHC_ADMA2_MAX_LENGTH_PER_ENTRY 0x0000F000 + +// +// Layout of a descriptor (excluding the Address field) +// + +typedef union _SDHC_ADMA2_DESCRIPTOR_TABLE_ENTRY { + struct { + ULONG Attribute:3; + ULONG Reserved1:1; + ULONG Action:2; + ULONG Reserved2:10; + ULONG Length:16; + }; + ULONG AsUlong; +} SDHC_ADMA2_DESCRIPTOR_TABLE_ENTRY, *PSDHC_ADMA2_DESCRIPTOR_TABLE_ENTRY; + +#pragma pack() + +// +// Voltage profile +// + +typedef union _SDHC_VOLTAGE_PROFILE { + struct { + ULONG Reserved1 : 7; + ULONG VoltageLow : 1; + ULONG Voltage20 : 1; + ULONG Voltage21 : 1; + ULONG Voltage22 : 1; + ULONG Voltage23 : 1; + ULONG Voltage24 : 1; + ULONG Voltage25 : 1; + ULONG Voltage26 : 1; + ULONG Voltage27 : 1; + ULONG Voltage28 : 1; + ULONG Voltage29 : 1; + ULONG Voltage30 : 1; + ULONG Voltage31 : 1; + ULONG Voltage32 : 1; + ULONG Voltage33 : 1; + ULONG Voltage34 : 1; + ULONG Voltage35 : 1; + }; + ULONG AsUlong; +} SDHC_VOLTAGE_PROFILE, *PSDHC_VOLTAGE_PROFILE; + +typedef enum _SDHC_SPEED_MODE { + SdhcSpeedModeNormal = 0, + SdhcSpeedModeHigh, + SdhcSpeedModeSDR50, + SdhcSpeedModeDDR50, + SdhcSpeedModeSDR104, + SdhcSpeedModeHS200, + SdhcSpeedModeHS400 +} SDHC_SPEED_MODE; + +#define SDHC_MAX_OUTSTANDING_REQUESTS 1 + +typedef struct _SDHC_EXTENSION { + + BOOLEAN Removable; + + // + // Host register space. + // + + PHYSICAL_ADDRESS PhysicalBaseAddress; + PVOID BaseAddress; + SIZE_T BaseAddressSpaceSize; + PSD_HOST_CONTROLLER_REGISTERS BaseAddressDebug; + + // + // Capabilities. + // + + SDPORT_CAPABILITIES Capabilities; + SDHC_SPEED_MODE SpeedMode; + UCHAR SpecVersion; + + // + // Requests to handle. + // + + PSDPORT_REQUEST OutstandingRequest; + ULONG CurrentEvents; + + // + // Whether this driver is used for crashdump/hibernate. + // + + BOOLEAN CrashdumpMode; + + // + // Associated PDO + // + PDEVICE_OBJECT PdoPtr; + +} SDHC_EXTENSION, *PSDHC_EXTENSION; + +// --------------------------------------------------------------------------- +// PCI Config definitions +// --------------------------------------------------------------------------- + +#define SDHC_PCICFG_SLOT_INFORMATION 0x40 + +extern "C" { +NTSTATUS +DriverEntry( + _In_ PVOID DriverObject, + _In_ PVOID RegistryPath + ); +} + +//----------------------------------------------------------------------------- +// SlotExtension callbacks. +//----------------------------------------------------------------------------- + +SDPORT_GET_SLOT_COUNT SdhcGetSlotCount; +/* +NTSTATUS +SdhcGetSlotCount( + _In_ PVOID Argument, + _In_ SDPORT_BUS_TYPE BusType + ); +*/ + +SDPORT_GET_SLOT_CAPABILITIES SdhcGetSlotCapabilities; +/* +VOID +SdhcGetSlotCapabilities( + _In_ PSDPORT_SLOT_EXTENSION SlotExtension, + _Inout_ PSDPORT_CAPABILITIES Capabilities + ); +*/ + +SDPORT_INITIALIZE SdhcSlotInitialize; +/* +NTSTATUS +SdhcSlotInitialize( + _Inout_ PSDPORT_SLOT_EXTENSION SlotExtension + ); +*/ + +SDPORT_ISSUE_BUS_OPERATION SdhcSlotIssueBusOperation; +/* +NTSTATUS +SdhcSlotIssueBusOperation( + _In_ PSDPORT_SLOT_EXTENSION SlotExtension, + _In_ PSDPORT_REQUEST Request + ); +*/ + +SDPORT_GET_CARD_DETECT_STATE SdhcSlotGetCardDetectState; +/* +BOOLEAN +SdhcSlotGetCardDetectState( + _In_ PSDPORT_SLOT_EXTENSION SlotExtension + ); +*/ + +SDPORT_GET_WRITE_PROTECT_STATE SdhcSlotGetWriteProtectState; +/* +BOOLEAN +SdhcSlotGetWriteProtectState( + _In_ PSDPORT_SLOT_EXTENSION SlotExtension + ); +*/ + +SDPORT_INTERRUPT SdhcSlotInterrupt; +/* +BOOLEAN +SdhcSlotInterrupt( + _In_ PSDPORT_SLOT_EXTENSION SlotExtension, + _Out_ PULONG Events, + _Out_ PULONG Errors, + _Out_ PBOOLEAN NotifyCardChange, + _Out_ PBOOLEAN NotifySdioInterrupt, + _Out_ PBOOLEAN NotifyTuning + ); +*/ + +SDPORT_ISSUE_REQUEST SdhcSlotIssueRequest; +/* +NTSTATUS +SdhcSlotIssueRequest( + _In_ PSDPORT_SLOT_EXTENSION SlotExtension, + _In_ PSDPORT_REQUEST Request + ); +*/ + +SDPORT_GET_RESPONSE SdhcSlotGetResponse; +/* +VOID +SdhcSlotGetResponse( + _In_ PVOID PrivateExtension, + _In_ PSDPORT_COMMAND Command, + _Out_ PVOID ResponseBuffer + ); +*/ + +SDPORT_TOGGLE_EVENTS SdhcSlotToggleEvents; +/* +VOID +SdhcSlotToggleEevnts( + _In_ PSDPORT_SLOT_EXTENSION SlotExtension, + _In_ ULONG EventMask, + _In_ BOOLEAN Enable, + _In_ BOOLEAN CrashdumpMode + ); +*/ + +SDPORT_CLEAR_EVENTS SdhcSlotClearEvents; +/* +VOID +SdhcSlotClearEvents( + _In_ PSDPORT_SLOT_EXTENSION SlotExtension, + _In_ ULONG EventMask + ); +*/ + +SDPORT_REQUEST_DPC SdhcRequestDpc; +/* +VOID +SdhcRequestDpc( + _In_ PVOID PrivateExtension, + _Inout_ PSDPORT_REQUEST Request, + _In_ ULONG Events, + _In_ ULONG Errors + ); +*/ + +SDPORT_SAVE_CONTEXT SdhcSaveContext; +/* +VOID +SdhcSaveContext( + _In_ PSDPORT_SLOT_EXTENSION SlotExtension + ); +*/ + +SDPORT_RESTORE_CONTEXT SdhcRestoreContext; +/* +VOID +SdhcRestoreContext( + _In_ PSDPORT_SLOT_EXTENSION SlotExtension + ); +*/ + +SDPORT_PO_FX_POWER_CONTROL_CALLBACK SdhcPoFxPowerControlCallback; +/* +NTSTATUS +SdhcPoFxPowerControlCallback( + _In_ PSD_MINIPORT Miniport, + _In_ LPCGUID PowerControlCode, + _In_reads_bytes_opt_(InputBufferSize) PVOID InputBuffer, + _In_ SIZE_T InputBufferSize, + _Out_writes_bytes_opt_(OutputBufferSize) PVOID OutputBuffer, + _In_ SIZE_T OutputBufferSize, + _Out_opt_ PSIZE_T BytesReturned + ) +*/ + +SDPORT_CLEANUP SdhcCleanup; +/* +VOID +SdhcCleanup( + _In_ PSD_MINIPORT Miniport + ) +*/ + +//----------------------------------------------------------------------------- +// Hardware access routines. +//----------------------------------------------------------------------------- + +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +SdhcResetHost( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ SDPORT_RESET_TYPE ResetType + ); + +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +SdhcSetVoltage( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ SDPORT_BUS_VOLTAGE VoltageProfile + ); + +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +SdhcSetClock( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ ULONG Frequency + ); + +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +SdhcSetBusWidth( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ SDPORT_BUS_WIDTH BusWidth + ); + +NTSTATUS +SdhcSetSpeed( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ SDPORT_BUS_SPEED Speed + ); + +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +SdhcSetHighSpeed( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ BOOLEAN Enable + ); + +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +SdhcSetUhsMode( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ USHORT Mode + ); + +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +SdhcSetSignaling( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ BOOLEAN Enable + ); + +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +SdhcExecuteTuning( + _In_ PSDHC_EXTENSION SdhcExtension + ); + +VOID +SdhcSetLed( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ BOOLEAN Enable + ); + +NTSTATUS +SdhcSetPresetValue( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ BOOLEAN Enable + ); + +NTSTATUS +SdhcEnableBlockGapInterrupt( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ BOOLEAN Enable + ); + +VOID +SdhcSetBlockGapControl( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ BOOLEAN Continue, + _In_ BOOLEAN RequestStop + ); + +VOID +SdhcSetBlockGapControl( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ BOOLEAN Continue, + _In_ BOOLEAN RequestStop + ); + +VOID +SdhcEnableInterrupt( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ ULONG NormalMask + ); + +VOID +SdhcDisableInterrupt( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ ULONG NormalMask + ); + +USHORT +SdhcGetInterruptStatus( + _In_ PSDHC_EXTENSION SdhcExtension + ); + +USHORT +SdhcGetErrorStatus( + _In_ PSDHC_EXTENSION SdhcExtension + ); + +USHORT +SdhcGetAutoCmd12ErrorStatus( + _In_ PSDHC_EXTENSION SdhcExtension + ); + +USHORT +SdhcGetAdmaErrorStatus( + _In_ PSDHC_EXTENSION SdhcExtension + ); + +VOID +SdhcAcknowledgeInterrupts( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ USHORT Interrupts + ); + +BOOLEAN +SdhcIsCardInserted( + _In_ PSDHC_EXTENSION SdhcExtension + ); + +BOOLEAN +SdhcIsWriteProtected( + _In_ PSDHC_EXTENSION SdhcExtension + ); + +NTSTATUS +SdhcSendCommand( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ PSDPORT_REQUEST Request + ); + +NTSTATUS +SdhcGetResponse( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ PSDPORT_COMMAND Command, + _Out_ PVOID ResponseBuffer + ); + +NTSTATUS +SdhcSetTransferMode( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ PSDPORT_REQUEST Request + ); + +VOID +SdhcReadDataPort( + _In_ PSDHC_EXTENSION SdhcExtension, + _Out_writes_all_(Length) PUCHAR Buffer, + _In_ SIZE_T Length + ); + +VOID +SdhcWriteDataPort( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_reads_(Length) PUCHAR Buffer, + _In_ ULONG Length + ); + +NTSTATUS +SdhcBuildTransfer( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ PSDPORT_REQUEST Request + ); + +NTSTATUS +SdhcStartTransfer( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ PSDPORT_REQUEST Request + ); + +NTSTATUS +SdhcBuildPioTransfer( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ PSDPORT_REQUEST Request + ); + +NTSTATUS +SdhcBuildAdmaTransfer( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ PSDPORT_REQUEST Request + ); + +NTSTATUS +SdhcStartPioTransfer( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ PSDPORT_REQUEST Request + ); + +NTSTATUS +SdhcStartAdmaTransfer( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ PSDPORT_REQUEST Request + ); + +VOID +SdhcCompleteRequest( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ PSDPORT_REQUEST Request, + _In_ NTSTATUS Status + ); + +//----------------------------------------------------------------------------- +// General utility functions. +//----------------------------------------------------------------------------- + +NTSTATUS +DwcSdhcRkConfigurePhy( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ ULONG Frequency + ); + +NTSTATUS +DwcSdhcRkDsmSetCardClock( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ ULONG TargetFrequency, + _Out_ PULONG ActualFrequency + ); + +NTSTATUS +DwcSdhcRkSetClock( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ ULONG TargetFrequencyKhz + ); + +USHORT +SdhcCalcClockFrequency( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ ULONG TargetFrequency, + _Out_ PULONG ActualFrequency + ); + +USHORT +SdhcConvertEventsToHwMask( + _In_ ULONG Events + ); + +NTSTATUS +SdhcConvertErrorToStatus( + _In_ USHORT Error + ); + +NTSTATUS +SdhcCreateAdmaDescriptorTable( + _In_ PSDPORT_REQUEST Request, + _In_ BOOLEAN Use64BitDescriptor, + _Out_ PULONG TotalTransferLength + ); + +VOID +SdhcInitializePciConfigSpace( + _In_ PSD_MINIPORT Miniport + ); + +__forceinline +USHORT +SdhcGetHwUhsMode( + _In_ SDPORT_BUS_SPEED BusSpeed + ) + +{ + + NT_ASSERT(BusSpeed != SdBusSpeedNormal); + + NT_ASSERT(BusSpeed != SdBusSpeedHigh); + + switch (BusSpeed) { + case SdBusSpeedSDR12: + return SDHC_HC2_SDR12; + + case SdBusSpeedSDR25: + return SDHC_HC2_SDR25; + + case SdBusSpeedSDR50: + return SDHC_HC2_SDR50; + + case SdBusSpeedDDR50: + return SDHC_HC2_SDR50; + + case SdBusSpeedSDR104: + return SDHC_HC2_SDR50; + + // + // PCI controllers don't support the higher speed eMMC modes. + // + + case SdBusSpeedHS200: + case SdBusSpeedHS400: + default: + break; + } + + NT_ASSERTMSG("SDHC - Invalid bus speed selected", FALSE); + + return 0; +} + +__forceinline +USHORT +SdhcConvertEventsToHwMask( + _In_ ULONG EventMask + ) + +{ + + return EventMask & 0xFFFF; +} + +__forceinline +USHORT +SdhcConvertErrorsToHwMask( + _In_ ULONG ErrorMask + ) + +{ + + return ErrorMask & 0xFFFF; +} + +__forceinline +NTSTATUS +SdhcConvertErrorToStatus( + _In_ USHORT Error + ) + +{ + + if (Error == 0) { + return STATUS_SUCCESS; + } + + if (Error & (SDHC_ES_CMD_TIMEOUT | SDHC_ES_DATA_TIMEOUT)) { + return STATUS_IO_TIMEOUT; + } + + if (Error & (SDHC_ES_CMD_CRC_ERROR | SDHC_ES_DATA_CRC_ERROR)) { + return STATUS_CRC_ERROR; + } + + if (Error & (SDHC_ES_CMD_END_BIT_ERROR | SDHC_ES_DATA_END_BIT_ERROR)) { + return STATUS_DEVICE_DATA_ERROR; + } + + if (Error & SDHC_ES_CMD_INDEX_ERROR) { + return STATUS_DEVICE_PROTOCOL_ERROR; + } + + if (Error & SDHC_ES_BUS_POWER_ERROR) { + return STATUS_DEVICE_POWER_FAILURE; + } + + return STATUS_IO_DEVICE_ERROR; +} + + +//----------------------------------------------------------------------------- +// Register access macros. +//----------------------------------------------------------------------------- + +__forceinline +VOID +SdhcWriteRegisterUlong( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ ULONG Register, + _In_ ULONG Data + ) + +{ + + SdPortWriteRegisterUlong(SdhcExtension->BaseAddress, Register, Data); + return; +} + +__forceinline +VOID +SdhcWriteRegisterUshort( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ ULONG Register, + _In_ USHORT Data + ) + +{ + + SdPortWriteRegisterUshort(SdhcExtension->BaseAddress, Register, Data); + return; +} + +__forceinline +VOID +SdhcWriteRegisterUchar( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ ULONG Register, + _In_ UCHAR Data + ) + +{ + + SdPortWriteRegisterUchar(SdhcExtension->BaseAddress, Register, Data); + return; +} + +__forceinline +ULONG +SdhcReadRegisterUlong( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ ULONG Register + ) + +{ + return SdPortReadRegisterUlong(SdhcExtension->BaseAddress, Register); +} + +__forceinline +USHORT +SdhcReadRegisterUshort( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ ULONG Register + ) + +{ + return SdPortReadRegisterUshort(SdhcExtension->BaseAddress, Register); +} + +__forceinline +UCHAR +SdhcReadRegisterUchar( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ ULONG Register + ) + +{ + return SdPortReadRegisterUchar(SdhcExtension->BaseAddress, Register); +} + +__forceinline +VOID +SdhcReadRegisterBufferUlong( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ ULONG Register, + _Out_writes_all_(Length) PULONG Buffer, + _In_ ULONG Length) + +{ + SdPortReadRegisterBufferUlong(SdhcExtension->BaseAddress, + Register, + Buffer, + Length); +} + +__forceinline +VOID +SdhcReadRegisterBufferUshort( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ ULONG Register, + _Out_writes_all_(Length) PUSHORT Buffer, + _In_ ULONG Length) + +{ + SdPortReadRegisterBufferUshort(SdhcExtension->BaseAddress, + Register, + Buffer, + Length); +} + +__forceinline +VOID +SdhcReadRegisterBufferUchar( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ ULONG Register, + _Out_writes_all_(Length) PUCHAR Buffer, + _In_ ULONG Length) + +{ + SdPortReadRegisterBufferUchar(SdhcExtension->BaseAddress, + Register, + Buffer, + Length); +} + +__forceinline +VOID +SdhcWriteRegisterBufferUlong( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ ULONG Register, + _In_reads_(Length) PULONG Buffer, + _In_ ULONG Length + ) + +{ + + SdPortWriteRegisterBufferUlong(SdhcExtension->BaseAddress, + Register, + Buffer, + Length); +} + +__forceinline +VOID +SdhcWriteRegisterBufferUshort( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ ULONG Register, + _In_reads_(Length) PUSHORT Buffer, + _In_ ULONG Length + ) + +{ + + SdPortWriteRegisterBufferUshort(SdhcExtension->BaseAddress, + Register, + Buffer, + Length); +} + +__forceinline +VOID +SdhcWriteRegisterBufferUchar( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ ULONG Register, + _In_reads_(Length) PUCHAR Buffer, + _In_ ULONG Length + ) + +{ + + SdPortWriteRegisterBufferUchar(SdhcExtension->BaseAddress, + Register, + Buffer, + Length); + +} + +__forceinline +UCHAR +SdhcGetResponseLength( + _In_ PSDPORT_COMMAND Command + ) + +/*++ + +Routine Description: + + Return the number of bytes associated with a given response type. + +Arguments: + + ResponseType + +Return value: + + Length of response. + +--*/ + +{ + + UCHAR Length; + + switch (Command->ResponseType) { + case SdResponseTypeR1: + case SdResponseTypeR3: + case SdResponseTypeR4: + case SdResponseTypeR5: + case SdResponseTypeR6: + case SdResponseTypeR1B: + case SdResponseTypeR5B: + Length = 4; + break; + + case SdResponseTypeR2: + Length = 16; + break; + + case SdResponseTypeNone: + Length = 0; + break; + + default: + + NT_ASSERTMSG("Invalid response type", FALSE); + + Length = 0; + break; + } + + return Length; +} + +#define MIN(x,y) ((x) > (y) ? (y) : (x)) // return minimum among x & y +#define MAX(x,y) ((x) > (y) ? (x) : (y)) // return maximum among x & y +#define DIV_CEIL(x, y) (((x) + (y) - 1) / (y)) diff --git a/drivers/sd/dwcsdhc/dwcsdhc.inx b/drivers/sd/dwcsdhc/dwcsdhc.inx new file mode 100644 index 0000000..10fe5f3 --- /dev/null +++ b/drivers/sd/dwcsdhc/dwcsdhc.inx @@ -0,0 +1,138 @@ +;/*++ +; +;Copyright (c) 2014 Microsoft Corporation. All rights Reserved. +;Copyright (c) 2023 Mario Bălănică +; +;Module Name: +; +; dwcsdhc.inf +; +;Abstract: +; +; INF file for installing the Synopsys DesignWare Core SD Host Controller driver. +; +;--*/ + +[Version] +Signature="$Windows NT$" +Class=SDHost +ClassGUID={a0a588a4-c46f-4b37-b7ea-c82fe89870c6} +Provider=%ProviderString% +CatalogFile=dwcsdhc.cat +PnpLockdown=1 + +[SourceDisksNames] +1 = Disk + +[SourceDisksFiles] +dwcsdhc.sys = 1 + +[Manufacturer] +%RKCP%=RKCP,NT$ARCH$ + +[ControlFlags] +BasicDriverOk=* +ExcludeFromSelect=* + +; +; Rockchip models +; + +[RKCP.NT$ARCH$] +%ACPI\RKCP0D40.DeviceDesc%=DWCSDHC, ACPI\RKCP0D40 + +; +; Service install +; + +[dwcsdhc_Service_Inst] +ServiceType = %SERVICE_KERNEL_DRIVER% +StartType = %SERVICE_DEMAND_START% +ErrorControl = %SERVICE_ERROR_NORMAL% +ServiceBinary = %13%\dwcsdhc.sys +LoadOrderGroup = System Bus Extender +AddReg = DWCSDHC_ServiceReg + +; +; Registry keys +; + +[DWCSDHC_LocationReg] +HKR,,UINumberDescFormat,,%DWCSDHC_Slot% + +[DWCSDHC_Reg] +HKR,,Driver,,"dwcsdhc.sys" + +[DWCSDHC_ServiceReg] +HKR,,BootFlags,0x00010003,0x00000008 +HKR,Parameters,SdCmdFlags,1, 05,01, 06,01, 08,11, 09,19, 0A,19, 0D,11, \ + 10,01, 11,01, 12,01, 17,01, 18,05, 19,05, \ + 1A,01, 1B,01, 1C,01, \ + 20,05, 21,05, 26,05, \ + 2A,01, \ + 34,02, 35,02, \ + 37,01, 38,01, \ + 22,01, 23,05, 24,01, 25,01 + +HKR,Parameters,SdAppCmdFlags,1, 06,01, 0D,01, 16,01, 17,01, 33,01, \ + 12,01, 19,01, 1A,01, 26,01, 2B,01, \ + 2C,01, 2D,01, 2E,01, 2F,01, 30,01, 31,01 + +; +; Copy Files +; + +[DWCSDHC_CopyFiles] +dwcsdhc.sys + +[DestinationDirs] +DWCSDHC_CopyFiles=13 + +; +; Controller Specific Sections +; + +[DWCSDHC] +CopyFiles=DWCSDHC_CopyFiles +AddReg=DWCSDHC_Reg + +[DWCSDHC.Services] +AddService = dwcsdhc, 2, dwcsdhc_Service_Inst + +[DWCSDHC.HW] +AddReg=DWCSDHC_LocationReg + +[Strings] +; +; Localizable strings below +; + +RKCP="Rockchip" + +ACPI\RKCP0D40.DeviceDesc="Synopsys DesignWare SD/eMMC Host Controller" + +; +; Slot description string +; + +DWCSDHC_Slot="SD Host Slot %1!u!" + +; +; Non-localizable strings below +; + +SERVICE_KERNEL_DRIVER = 1 +SERVICE_BOOT_START = 0 +SERVICE_SYSTEM_START = 1 +SERVICE_DEMAND_START = 3 +SERVICE_ERROR_NORMAL = 1 +SERVICE_ERROR_IGNORE = 0 +REG_EXPAND_SZ = 0x00020000 +REG_DWORD = 0x00010001 +REG_SZ = 0x00000000 + +; +; Manufacturer name strings +; + +ProviderString="Windows on R Project" diff --git a/drivers/sd/dwcsdhc/dwcsdhc.rc b/drivers/sd/dwcsdhc/dwcsdhc.rc new file mode 100644 index 0000000..43dc9e4 --- /dev/null +++ b/drivers/sd/dwcsdhc/dwcsdhc.rc @@ -0,0 +1,20 @@ +// +// Copyright (C) Microsoft. All rights reserved. +// +#include + +#include + +#undef VER_PRODUCTNAME_STR +#undef VER_COMPANYNAME_STR + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_COMPANYNAME_STR "Windows on R Project" +#define VER_FILEDESCRIPTION_STR "Synopsys DesignWare SD/eMMC Host Controller Driver" +#define VER_INTERNALNAME_STR "dwcsdhc.sys" +#define VER_LEGALCOPYRIGHT_STR "Copyright (c) 2023 Mario Bălănică. All Rights Reserved." +#define VER_ORIGINALFILENAME_STR "dwcsdhc.sys" +#define VER_PRODUCTNAME_STR "Synopsys DesignWare SD/eMMC Host Controller Driver" + +#include "common.ver" diff --git a/drivers/sd/dwcsdhc/dwcsdhc.vcxproj b/drivers/sd/dwcsdhc/dwcsdhc.vcxproj new file mode 100644 index 0000000..139c58e --- /dev/null +++ b/drivers/sd/dwcsdhc/dwcsdhc.vcxproj @@ -0,0 +1,101 @@ + + + + + Debug + ARM64 + + + Release + ARM64 + + + + {10452736-7C4F-4206-94F9-AD634213C9C4} + $(MSBuildProjectName) + Debug + Win32 + {BF01AA4F-592C-4E53-A80E-E8E61BE4F7BC} + + + + Windows10 + False + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + + + + + + + + + + dwcsdhc + + + dwcsdhc + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\sdport.lib;$(DDK_LIB_PATH)\ntoskrnl.lib + + + true + Level4 + + + ..\..\include;%(AdditionalIncludeDirectories) + + + sha256 + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\sdport.lib;$(DDK_LIB_PATH)\ntoskrnl.lib + + + true + Level4 + + + ..\..\include;%(AdditionalIncludeDirectories) + + + sha256 + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/drivers/sd/dwcsdhc/precomp.h b/drivers/sd/dwcsdhc/precomp.h new file mode 100644 index 0000000..fca8c99 --- /dev/null +++ b/drivers/sd/dwcsdhc/precomp.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +extern "C" { + #include + #include + #include +} // extern "C" + +#define NONPAGED_SEGMENT_BEGIN \ + __pragma(code_seg(push)) \ + //__pragma(code_seg(.text)) + +#define NONPAGED_SEGMENT_END \ + __pragma(code_seg(pop)) + +#define INIT_SEGMENT_BEGIN \ + __pragma(code_seg(push)) \ + __pragma(code_seg("INIT")) + +#define INIT_SEGMENT_END \ + __pragma(code_seg(pop)) + +#define POOL_ZERO_DOWN_LEVEL_SUPPORT