Skip to content

Commit

Permalink
dwcmshc: Fix signaling voltage mismatch
Browse files Browse the repository at this point in the history
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ă <[email protected]>
  • Loading branch information
mariobalanica committed Jun 22, 2024
1 parent 52d7499 commit 14c9d75
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 17 deletions.
102 changes: 87 additions & 15 deletions drivers/sd/dwcmshc/dwcmshc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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, "()");
}

Expand Down Expand Up @@ -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
Expand All @@ -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;
}
Expand Down Expand Up @@ -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_
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -1805,6 +1840,10 @@ MshcSetSignalingVoltage(
MshcEnableGlobalInterrupt(MshcExtension, TRUE);
}

if (NT_SUCCESS(Status)) {
MshcExtension->SignalingVoltage = Voltage;
}

return Status;
}

Expand Down Expand Up @@ -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(
Expand Down
12 changes: 12 additions & 0 deletions drivers/sd/dwcmshc/dwcmshc.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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(
Expand Down
4 changes: 2 additions & 2 deletions drivers/sd/dwcmshc/rockchip_ops.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ MshcRockchipSetVoltage(
Status);
}

return STATUS_SUCCESS;
return Status;
}

NTSTATUS
Expand All @@ -150,7 +150,7 @@ MshcRockchipSetSignalingVoltage(
Status);
}

return STATUS_SUCCESS;
return Status;
}

NTSTATUS
Expand Down

0 comments on commit 14c9d75

Please sign in to comment.