From 14c9d756b6b8d649b2f804b7cecde89a90fadc21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20B=C4=83l=C4=83nic=C4=83?= Date: Sat, 22 Jun 2024 15:02:09 +0300 Subject: [PATCH] dwcmshc: Fix signaling voltage mismatch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SD cards can only be switched back to 3.3V after a power cycle. The firmware does not currently implement card power control (and some boards don't even have a switch to begin with), so we need to ensure that the card is not initialized before being able to safely change signaling on the host side. This should only happen after the card is unplugged. Signed-off-by: Mario Bălănică --- drivers/sd/dwcmshc/dwcmshc.cpp | 102 ++++++++++++++++++++++++---- drivers/sd/dwcmshc/dwcmshc.h | 12 ++++ drivers/sd/dwcmshc/rockchip_ops.cpp | 4 +- 3 files changed, 101 insertions(+), 17 deletions(-) diff --git a/drivers/sd/dwcmshc/dwcmshc.cpp b/drivers/sd/dwcmshc/dwcmshc.cpp index fc395bb..feccdb7 100644 --- a/drivers/sd/dwcmshc/dwcmshc.cpp +++ b/drivers/sd/dwcmshc/dwcmshc.cpp @@ -1045,6 +1045,10 @@ MshcSlotGetResponse( NT_ASSERTMSG("Invalid response type!", FALSE); } + if (Command->Index == SDCMD_SEND_RELATIVE_ADDR) { + MshcExtension->CardRca = Response[0] >> 16; + } + MSHC_LOG_EXIT(MshcExtension->LogHandle, MshcExtension, "()"); } @@ -1403,20 +1407,6 @@ MshcResetHost( } if (ResetType == SdResetTypeAll) { - // Mask all interrupts, sdport will toggle them as needed. - MshcDisableInterrupts(MshcExtension, MSHC_INT_ALL, MSHC_IDINT_ALL); - - // Global interrupt enable - MshcWriteRegister(MshcExtension, MSHC_CTRL, MSHC_CTRL_INT_ENABLE); - - // Set the max HW timeout for bus operations. - MshcWriteRegister(MshcExtension, MSHC_TMOUT, 0xffffffff); - - // - // Bring the slot in its initial state. - // - MshcSetClock(MshcExtension, 0); - // // WORKAROUND: // In crashdump mode, sdport will switch to an UHS-I mode @@ -1425,12 +1415,29 @@ MshcResetHost( // for the best... // if (!gCrashdumpMode) { + // + // Reset voltages. + // We don't trust sdport to always do it when needed. + // MshcSetVoltage(MshcExtension, SdBusVoltageOff); MshcSetSignalingVoltage(MshcExtension, SdSignalingVoltage33); } + // Disable clock + MshcSetClock(MshcExtension, 0); + + // Reset bus width MshcSetBusWidth(MshcExtension, SdBusWidth1Bit); + // Mask all interrupts, sdport will toggle them as needed. + MshcDisableInterrupts(MshcExtension, MSHC_INT_ALL, MSHC_IDINT_ALL); + + // Global interrupt enable + MshcWriteRegister(MshcExtension, MSHC_CTRL, MSHC_CTRL_INT_ENABLE); + + // Set the max HW timeout for bus operations. + MshcWriteRegister(MshcExtension, MSHC_TMOUT, 0xffffffff); + MshcExtension->TuningPerformed = FALSE; MshcExtension->DataCrcErrorsSinceLastTuning = 0; } @@ -1501,12 +1508,14 @@ MshcSetVoltage( Status = PlatformOperations->SetVoltage(MshcExtension, Voltage); } + MshcExtension->CardPowerControlSupported = NT_SUCCESS(Status); + // // Wait 10ms for regulator to stabilize. // SdPortWait(10000); - return Status; + return STATUS_SUCCESS; } _Use_decl_annotations_ @@ -1714,11 +1723,37 @@ MshcSetSignalingVoltage( switch (Voltage) { case SdSignalingVoltage33: + // + // The card should've been power cycled for its signaling level to + // return to 3.3V. If the platform does not support power control and the + // card was already initialized at 1.8V, it's unsafe to change voltage here. + // Stay at the current level, since sdport will not switch to 1.8V again + // if the card responds with S18A = 0 to ACMD41. + // + if((MshcExtension->SignalingVoltage == SdSignalingVoltage18) && + !MshcExtension->CardPowerControlSupported) { + // + // Issue SEND_STATUS. If it fails, the card was either removed + // or isn't actually initialized yet (RCA won't match -> timeout). + // We will allow the voltage switch only in this case. + // + ULONG StatusRegister = 0; + Status = MshcSendStatusCommand(MshcExtension, &StatusRegister); + if (NT_SUCCESS(Status)) { + MSHC_LOG_WARN( + MshcExtension->LogHandle, + MshcExtension, + "Not changing signaling for current card - power control unsupported!"); + return STATUS_SUCCESS; + } + } Switch18v = FALSE; break; + case SdSignalingVoltage18: Switch18v = TRUE; break; + default: NT_ASSERTMSG("Invalid signaling voltage!", FALSE); return STATUS_INVALID_PARAMETER; @@ -1805,6 +1840,10 @@ MshcSetSignalingVoltage( MshcEnableGlobalInterrupt(MshcExtension, TRUE); } + if (NT_SUCCESS(Status)) { + MshcExtension->SignalingVoltage = Voltage; + } + return Status; } @@ -2824,6 +2863,39 @@ MshcSendStopCommand( return MshcIssueRequestSynchronously(MshcExtension, &Request, 1000000, FALSE); } +_Use_decl_annotations_ +NTSTATUS +MshcSendStatusCommand( + _In_ PMSHC_EXTENSION MshcExtension, + _Out_ PULONG StatusRegister + ) +{ + NTSTATUS Status; + SDPORT_REQUEST Request; + + RtlZeroMemory(&Request, sizeof(Request)); + + Request.Command.Index = SDCMD_SEND_STATUS; + Request.Command.Class = SdCommandClassStandard; + Request.Command.ResponseType = SdResponseTypeR1; + Request.Command.TransferType = SdTransferTypeNone; + Request.Command.Argument = MshcExtension->CardRca << 16; + + // + // Send the command. + // + Request.Type = SdRequestTypeCommandNoTransfer; + + Status = MshcIssueRequestSynchronously(MshcExtension, &Request, 50000, TRUE); + if (!NT_SUCCESS(Status)) { + return Status; + } + + MshcSlotGetResponse(MshcExtension, &Request.Command, StatusRegister); + + return Status; +} + _Use_decl_annotations_ NTSTATUS MshcSendTuningCommand( diff --git a/drivers/sd/dwcmshc/dwcmshc.h b/drivers/sd/dwcmshc/dwcmshc.h index e1cc638..d8af1be 100644 --- a/drivers/sd/dwcmshc/dwcmshc.h +++ b/drivers/sd/dwcmshc/dwcmshc.h @@ -429,8 +429,10 @@ typedef struct _MSHC_IDMAC_DESCRIPTOR { // Standard SD/MMC commands // #define SDCMD_GO_IDLE_STATE 0 +#define SDCMD_SEND_RELATIVE_ADDR 3 #define SDCMD_VOLTAGE_SWITCH 11 #define SDCMD_STOP_TRANSMISSION 12 +#define SDCMD_SEND_STATUS 13 #define SDCMD_GO_INACTIVE_STATE 15 #define SDCMD_SEND_TUNING_BLOCK 19 #define SDCMD_EMMC_SEND_TUNING_BLOCK 21 @@ -544,6 +546,9 @@ struct _MSHC_EXTENSION { // Current bus configuration // BOOLEAN CardInitialized; + BOOLEAN CardPowerControlSupported; + USHORT CardRca; + SDPORT_SIGNALING_VOLTAGE SignalingVoltage; SDPORT_BUS_SPEED BusSpeed; SDPORT_BUS_WIDTH BusWidth; ULONG BusFrequencyKhz; @@ -821,6 +826,13 @@ MshcSendStopCommand( _In_ PMSHC_EXTENSION MshcExtension ); +_IRQL_requires_max_(APC_LEVEL) +NTSTATUS +MshcSendStatusCommand( + _In_ PMSHC_EXTENSION MshcExtension, + _Out_ PULONG StatusRegister + ); + _IRQL_requires_max_(APC_LEVEL) NTSTATUS MshcSendTuningCommand( diff --git a/drivers/sd/dwcmshc/rockchip_ops.cpp b/drivers/sd/dwcmshc/rockchip_ops.cpp index 5688a05..47c0b8d 100644 --- a/drivers/sd/dwcmshc/rockchip_ops.cpp +++ b/drivers/sd/dwcmshc/rockchip_ops.cpp @@ -126,7 +126,7 @@ MshcRockchipSetVoltage( Status); } - return STATUS_SUCCESS; + return Status; } NTSTATUS @@ -150,7 +150,7 @@ MshcRockchipSetSignalingVoltage( Status); } - return STATUS_SUCCESS; + return Status; } NTSTATUS