|
@@ -0,0 +1,3067 @@
|
|
|
+MODULE Sd;
|
|
|
+(**
|
|
|
+ AUTHOR Timothée Martiel, 2015
|
|
|
+ PURPOSE SD Card Host Controller Driver
|
|
|
+*)
|
|
|
+
|
|
|
+IMPORT
|
|
|
+ SYSTEM, SdEnvironment, Log := SdEnvironment;
|
|
|
+
|
|
|
+CONST
|
|
|
+ BlockSize * = 512;
|
|
|
+ InitialClockFrequency * = 400000; (* Hz *)
|
|
|
+
|
|
|
+ (* Commands *) (*! Do not change values *)
|
|
|
+ CMD_GO_IDLE_STATE * = 0; (** CMD0 bc [31:0] stuff bits - *)
|
|
|
+ CMD_ALL_SEND_CID * = 2; (** CMD2 bcr [31:0] stuff bits R2 *)
|
|
|
+ CMD_SEND_RELATIVE_ADDR * = 3; (** CMD3 bcr [31:0] stuff bits R6 *)
|
|
|
+ CMD_SET_DSR * = 4; (** CMD4 bc [31:16] DSR [15:0] stuff bits - *)
|
|
|
+ CMD_IO_SEND_OP_COND * = 5; (** CMD5 ?? [31:25] stuff bits [24] switch 1.8V request [23:0] I/O OCR R4 *)
|
|
|
+ CMD_SWITCH_FUNC * = 6; (** CMD6 adtc [31] Mode 0:Check function 1:Switch function [30:24] reserved (All '0') [23:20] reserved for function group 6 (0h or Fh) [19:16] reserved for function group 5 (0h or Fh) [15:12] function group 4 for current limit [11:8] funciton group 3 for drive strength [7:4] function group 2 for command system [3:0] function group 1 for access mode R1 *)
|
|
|
+ CMD_SELECT_DESELECT_CARD * = 7; (** CMD7 ac [31:16] RCA [15:0] stuff bits R1b (only from the selected card) *)
|
|
|
+ CMD_SEND_IF_COND * = 8; (** CMD8 bcr [31:12] reserved bits [11:8] supply voltage(VHS) [7:0]check pattern R7 *)
|
|
|
+ CMD_SEND_CSD * = 9; (** CMD9 ac [31:16] RCA [15:0] stuff bits R2 *)
|
|
|
+ CMD_SEND_CID * = 10; (** CMD10 ac [31:16] RCA [15:0] stuff bits R2 *)
|
|
|
+ CMD_VOLTAGE_SWITCH * = 11; (** CMD11 ac [31:0] reserved bits (all 0) R1 *)
|
|
|
+ CMD_STOP_TRANSMISSION * = 12; (** CMD12 ac [31:0] stuff bits R1b *)
|
|
|
+ CMD_SEND_STATUS * = 13; (** CMD13 ac [31:16] RCA [15:0] stuff bits R1 *)
|
|
|
+ CMD_GO_INACTIVE_STATE * = 15; (** CMD15 ac [31:16] RCA [15:0] reserved bits - *)
|
|
|
+ CMD_SET_BLOCKLEN * = 16; (** CMD16 ac [31:0] block length R1 *)
|
|
|
+ CMD_READ_SINGLE_BLOCK * = 17; (** CMD17 adtc [31:0] data address2 R1 *)
|
|
|
+ CMD_READ_MULTIPLE_BLOCK * = 18; (** CMD18 adtc [31:0] data address2 R1 *)
|
|
|
+ CMD_SEND_TUNING_BLOCK * = 19; (** CMD19 adtc [31:0] reserved bits (all 0) R1 *)
|
|
|
+ CMD_SPEED_CLASS_CONTROL * = 20; (** CMD20 ac [31:28]Speed Class Control [27:0] Reserved (all-0) R1b *)
|
|
|
+ CMD_SET_BLOCK_COUNT * = 23; (** CMD23 ac [31:0] Block Count R1 *)
|
|
|
+ CMD_WRITE_BLOCK * = 24; (** CMD24 adtc [31:0] data address2 R1 *)
|
|
|
+ CMD_WRITE_MULTIPLE_BLOCK * = 25; (** CMD25 adtc [31:0] data address2 R1 *)
|
|
|
+ CMD_PROGRAM_CSD * = 27; (** CMD27 adtc [31:0] stuff bits R1 *)
|
|
|
+ CMD_SET_WRITE_PROT * = 28; (** CMD28 ac [31:0] data address2 R1b *)
|
|
|
+ CMD_CLR_WRITE_PROT * = 29; (** CMD29 ac [31:0] data address2 R1b *)
|
|
|
+ CMD_SEND_WRITE_PROT * = 30; (** CMD30 adtc [31:0] write protect data address2 R1 *)
|
|
|
+ CMD_ERASE_WR_BLK_START * = 32; (** CMD32 ac [31:0] data address1 R1 *)
|
|
|
+ CMD_ERASE_WR_BLK_END * = 33; (** CMD33 ac [31:0] data address1 R1 *)
|
|
|
+ CMD_ERASE * = 38; (** CMD38 ac [31:0] stuff bits R1b *)
|
|
|
+ CMD_LOCK_UNLOCK * = 42; (** CMD42 adtc [31:0] Reserved bits (Set all 0) R1 *)
|
|
|
+ CMD_APP_CMD * = 55; (** CMD55 ac [31:16] RCA [15:0] stuff bits R1 *)
|
|
|
+ CMD_GEN_CMD * = 56; (** CMD56 adtc [31:1] stuff bits. [0] RD/WR R1 *)
|
|
|
+
|
|
|
+ (** Application Commands *) (*! Do Not Change Values *)
|
|
|
+ ACMD_SET_BUS_WIDTH * = 6; (** ACMD6 ac [31:2] stuff bits [1:0] bus width R1 *)
|
|
|
+ ACMD_SD_STATUS * = 13; (** ACMD13 adtc [31:0] stuff bits R1 *)
|
|
|
+ ACMD_SEND_NUM_WR_BLOCKS * = 22; (** ACMD22 adtc [31:0] stuff bits R1 *)
|
|
|
+ ACMD_SET_WR_BLK_ERASE_COUNT * = 23; (** ACMD23 ac [31:23] stuff bits [22:0] Number of blocks R1 *)
|
|
|
+ ACMD_SD_SEND_OP_COND * = 41; (** ACMD41 bcr [31]reserved bit [30] HCS(OCR[30]) [29] reserved for eSD [28] XPC [27:25] reserved bits [24] S18R [23:0] VDD Voltage Window(OCR[23:0]) R3 *)
|
|
|
+ ACMD_SET_CLR_CARD_DETECT * = 42; (** ACMD42 ac [31:1] stuff bits [0] set_cd R1 *)
|
|
|
+ ACMD_SEND_SCR * = 51; (** ACMD51 adtc [31:0] stuff bits R1 *)
|
|
|
+
|
|
|
+ (** Errors *)
|
|
|
+ ErrorNone * = 0; (** No error *)
|
|
|
+ ErrorCmdTimeout * = 1; (** Timeout on command line *)
|
|
|
+ ErrorCmdCrc * = 2; (** CRC error on command line *)
|
|
|
+ ErrorDatTimeout * = 3; (** Timeout on data line *)
|
|
|
+ ErrorDatCrc * = 4; (** CRC error on data line *)
|
|
|
+ ErrorNoCard * = 5; (** No card present *)
|
|
|
+ ErrorCard * = 6; (** Card failed to perform operation *)
|
|
|
+ ErrorUnrecoverable * = 7; (** Host controller in an unrecoverable state *)
|
|
|
+ ErrorInvalidParameters * = 8; (** Invalid parameters *)
|
|
|
+
|
|
|
+ (** Card Versions: maximal SD physical layer specifications version supported by the card *)
|
|
|
+ Version1 * = 0; (** v1.00 or v1.01 *)
|
|
|
+ Version1p1 * = 1; (** v1.10 *)
|
|
|
+ Version2 * = 2; (** v2.00 *)
|
|
|
+ Version3 * = 3; (** v3.01 *)
|
|
|
+ Version4 * = 4; (** v4.10 *)
|
|
|
+ Version5 * = 5; (** v5.10 *)
|
|
|
+ Version6 * =6; (** v6.0 *)
|
|
|
+
|
|
|
+ (** Card Type *)
|
|
|
+ TypeNone * = 0; (** Unknow *)
|
|
|
+ TypeSDSC * = 1; (** SD standard capacity - physical specs v1.0 or v1.1, limited to 2 GB *)
|
|
|
+ TypeSDHC * = 2; (** SD High Capacity - 2 GB to 32 GB *)
|
|
|
+ TypeSDXC * = 3; (** SD Extended Capacity - 32 GB to 2 TB *)
|
|
|
+
|
|
|
+ (** Card Events *)
|
|
|
+ OnInitialization * = 0;
|
|
|
+ OnRemoval * = 1;
|
|
|
+ OnReadComplete * = 2;
|
|
|
+ OnWriteComplete * = 3;
|
|
|
+
|
|
|
+ (** Command Record Flags *)
|
|
|
+ FlagData * = 0;
|
|
|
+ FlagRead * = 1;
|
|
|
+ FlagAutoCmd12 * = 2;
|
|
|
+ FlagAutoCmd23 * = 3;
|
|
|
+ FlagMultipleBlockTx * = 4;
|
|
|
+ FlagCountBlocks * = 5;
|
|
|
+ FlagAbort * = 7;
|
|
|
+ FlagApplicationCmd * = 8;
|
|
|
+ FlagIgnoreIllegalCmd * = 9;
|
|
|
+
|
|
|
+ (** Response Types *) (*! Do not change values *)
|
|
|
+ ResponseNone * = -1;
|
|
|
+ ResponseR1 * = 0;
|
|
|
+ ResponseR1b * = 1;
|
|
|
+ ResponseR2 * = 2;
|
|
|
+ ResponseR3 * = 3;
|
|
|
+ ResponseR4 * = 4;
|
|
|
+ ResponseR5 * = 5;
|
|
|
+ ResponseR5b * = 6;
|
|
|
+ ResponseR6 * = 7;
|
|
|
+ ResponseR7 * = 8;
|
|
|
+
|
|
|
+ (** Host Controller States *)
|
|
|
+ HcOperational * = 0; (** Host controller is operational *)
|
|
|
+ HcConfiguring * = 1; (** Host controller is waiting for configuration input *)
|
|
|
+ HcError * = 2; (** Error occurred *)
|
|
|
+
|
|
|
+ (** Card States *)
|
|
|
+ CardIdle = 0;
|
|
|
+ CardReady = 1;
|
|
|
+ CardIdentification = 2;
|
|
|
+ CardStandby = 3;
|
|
|
+ CardTransfer = 4;
|
|
|
+ CardData = 5;
|
|
|
+ CardReceive = 6;
|
|
|
+ CardProgram = 7;
|
|
|
+ CardDisabled = 8;
|
|
|
+
|
|
|
+ (** Operation modes *)
|
|
|
+ OpCpu = 0;
|
|
|
+ OpSdma = 1;
|
|
|
+ OpAdma = 2;
|
|
|
+
|
|
|
+ (* Present State bits *)
|
|
|
+ PresentState_CommandInhibitCmd = 0;
|
|
|
+ PresentState_CommandInhibitDat = 1;
|
|
|
+ PresentState_DatLineActive = 2;
|
|
|
+ PresentState_RetuningRequest = 3;
|
|
|
+ PresentState_WriteTransferActive = 8;
|
|
|
+ PresentState_ReadTransferActive = 9;
|
|
|
+ PresentState_BufferWriteEnable = 10;
|
|
|
+ PresentState_BufferReadEnable = 11;
|
|
|
+ PresentState_CardInserted = 16;
|
|
|
+ PresentState_CardStateStable = 17;
|
|
|
+ PresentState_CardDetectPinLevel = 18;
|
|
|
+ PresentState_WriteProtectSwitchPinLevel = 19;
|
|
|
+ PresentState_CmdLineSignalLevel = 24;
|
|
|
+ PresentState_DatLineSignalLevelOfs = 20;
|
|
|
+ PresentState_DatLineSignalLevelMask = {20 .. 23};
|
|
|
+
|
|
|
+ (* Interrupt Status, Status Enable, Signal Enable bits *)
|
|
|
+ Interrupt_Normal_CommandComplete = 0;
|
|
|
+ Interrupt_Normal_TransferComplete = 1;
|
|
|
+ Interrupt_Normal_BlockGapEvent = 2;
|
|
|
+ Interrupt_Normal_DmaInterrupt = 3;
|
|
|
+ Interrupt_Normal_BufferWriteReady = 4;
|
|
|
+ Interrupt_Normal_BufferReadReady = 5;
|
|
|
+ Interrupt_Normal_CardInsertion = 6;
|
|
|
+ Interrupt_Normal_CardRemoval = 7;
|
|
|
+ Interrupt_Normal_CardInterrupt = 8;
|
|
|
+ Interrupt_Normal_IntA = 9;
|
|
|
+ Interrupt_Normal_IntB = 10;
|
|
|
+ Interrupt_Normal_IntC = 11;
|
|
|
+ Interrupt_Normal_RetuningEvent = 12;
|
|
|
+ Interrupt_Normal_ErrorInterrupt = 15;
|
|
|
+ Interrupt_Error_CommandTimeout = 16;
|
|
|
+ Interrupt_Error_CommandCrc = 17;
|
|
|
+ Interrupt_Error_CommandEndBit = 18;
|
|
|
+ Interrupt_Error_CommandIndex = 19;
|
|
|
+ Interrupt_Error_DataTimeout = 20;
|
|
|
+ Interrupt_Error_DataCrc = 21;
|
|
|
+ Interrupt_Error_DataEndBit = 22;
|
|
|
+ Interrupt_Error_CurrentLimit = 23;
|
|
|
+ Interrupt_Error_AutoCmd12 = 24;
|
|
|
+ Interrupt_Error_Adma = 25;
|
|
|
+ Interrupt_Error_Tuning = 26;
|
|
|
+ Interrupt_Normal_All = {Interrupt_Normal_CommandComplete, Interrupt_Normal_TransferComplete, Interrupt_Normal_BlockGapEvent, Interrupt_Normal_DmaInterrupt,
|
|
|
+ Interrupt_Normal_BufferWriteReady, Interrupt_Normal_BufferReadReady, Interrupt_Normal_CardInsertion, Interrupt_Normal_CardRemoval,
|
|
|
+ Interrupt_Normal_CardInterrupt, Interrupt_Normal_IntA, Interrupt_Normal_IntB, Interrupt_Normal_IntC, Interrupt_Normal_RetuningEvent,
|
|
|
+ Interrupt_Normal_ErrorInterrupt};
|
|
|
+ Interrupt_Error_All = {Interrupt_Error_CommandTimeout, Interrupt_Error_CommandCrc, Interrupt_Error_CommandEndBit,
|
|
|
+ Interrupt_Error_CommandIndex, Interrupt_Error_DataTimeout, Interrupt_Error_DataCrc, Interrupt_Error_DataEndBit, Interrupt_Error_CurrentLimit,
|
|
|
+ Interrupt_Error_AutoCmd12, Interrupt_Error_Adma, Interrupt_Error_Tuning};
|
|
|
+ Interrupt_All = Interrupt_Normal_All + Interrupt_Error_All;
|
|
|
+
|
|
|
+ (* Transfer Mode Register bits *)
|
|
|
+ TransferMode_DmaEnable = 0;
|
|
|
+ TransferMode_BlockCountEnable = 1;
|
|
|
+ TransferMode_AutoCmdOfs = 2;
|
|
|
+ TransferMode_AutoCmdMask = {2 .. 3};
|
|
|
+ TransferMode_DataTxDirection = 4;
|
|
|
+ TransferMode_MultipleBlocks = 5;
|
|
|
+ TransferMode_AutoCmd_None = {};
|
|
|
+ TransferMode_AutoCmd_Cmd12 = {2};
|
|
|
+ TransferMode_AutoCmd_Cmd23 = {3};
|
|
|
+
|
|
|
+ (* Command Register bits *)
|
|
|
+ Command_ResponseTypeOffset = 0;
|
|
|
+ Command_CrcCheckEnable = 3;
|
|
|
+ Command_IndexCheckEnable = 4;
|
|
|
+ Command_DataPresent = 5;
|
|
|
+ Command_CommandTypeOffset = 6;
|
|
|
+ Command_CommandTypeMask = {6 .. 7};
|
|
|
+ Command_CommandIndexOffset = 8;
|
|
|
+ Command_CommandIndexMask = {8 .. 13};
|
|
|
+
|
|
|
+ Command_ResponseType_None = 0;
|
|
|
+ Command_ResponseType_136b = 1;
|
|
|
+ Command_ResponseType_48b = 2;
|
|
|
+ Command_ResponseType_48bBusy = 3;
|
|
|
+
|
|
|
+ (* Capabilities Register *)
|
|
|
+ (* Low Word *)
|
|
|
+ Capabilities_TimeoutClockFrequencyOfs = 0;
|
|
|
+ Capabilities_TimeoutClockFrequencyMask = {0 .. 5};
|
|
|
+ Capabilities_TimeoutClockUnit = 7;
|
|
|
+ Capabilities_BaseClockFreqSdOfs = 8;
|
|
|
+ Capabilities_BaseClockFreqSdMask = {8 .. 15};
|
|
|
+ Capabilities_MaxBlockLenOfs = 16;
|
|
|
+ Capabilities_MaxBlockLenMask = {16 .. 17};
|
|
|
+ Capabilities_8BitEmbedded = 18;
|
|
|
+ Capabilities_ADMA2 = 19;
|
|
|
+ Capabilities_HighSpeed = 21;
|
|
|
+ Capabilities_SDMA = 22;
|
|
|
+ Capabilities_SuspendResume = 23;
|
|
|
+ Capabilities_Voltage33 = 24;
|
|
|
+ Capabilities_Voltage30 = 25;
|
|
|
+ Capabilities_Voltage18 = 26;
|
|
|
+ Capabilities_64BitBus = 28;
|
|
|
+ Capabilities_AsyncInterrupt = 29;
|
|
|
+ Capabilities_SlotTypeOfs = 30;
|
|
|
+ Capabilities_SlotTypeMask = {30 .. 31};
|
|
|
+ (* High Word *)
|
|
|
+ Capabilities_SDR50 = 0;
|
|
|
+ Capabilities_SDR104 = 1;
|
|
|
+ Capabilities_DDR50 = 2;
|
|
|
+ Capabilities_DriverTypeA = 4;
|
|
|
+ Capabilities_DriverTypeC = 5;
|
|
|
+ Capabilities_DriverTypeD = 6;
|
|
|
+ Capabilities_TimerCountRetuningOfs = 8;
|
|
|
+ Capabilities_TimerCountRetuningMask = {8 .. 11};
|
|
|
+ Capabilities_TuningSDR50 = 13;
|
|
|
+ Capabilities_RetuningModesOfs = 14;
|
|
|
+ Capabilities_RetuningModesMask = {14 .. 15};
|
|
|
+ Capabilities_ClockMultiplierOfs = 16;
|
|
|
+ Capabilities_ClockMultiplierMask = {16 .. 23};
|
|
|
+ (* Patterns *)
|
|
|
+ Capabilities_SlotType_Removable = {};
|
|
|
+ Capabilities_SlotType_Embedded = {30};
|
|
|
+ Capabilities_SlotType_SharedBus = {31};
|
|
|
+
|
|
|
+ (* Host Control 1 register values *)
|
|
|
+ HostControl1_LedControl = 0;
|
|
|
+ HostControl1_DataTransferWidth = 1;
|
|
|
+ HostControl1_HighSpeedEnable = 2;
|
|
|
+ HostControl1_DmaSelectOfs = 3;
|
|
|
+ HostControl1_DmaSelectMask = {3 .. 4};
|
|
|
+ HostControl1_ExtendedDataTxWidth = 5;
|
|
|
+ HostControl1_CardDetectTestLevel = 6;
|
|
|
+ HostControl1_CardDetectSignalSelection = 7;
|
|
|
+ HostControl1_DmaSelect_Sdma = {};
|
|
|
+ HostControl1_DmaSelect_32Adma = {4};
|
|
|
+
|
|
|
+ (* SoftwareReset register values *)
|
|
|
+ SoftwareResetAll = 1;
|
|
|
+ SoftwareResetCmd = 2;
|
|
|
+ SoftwareResetDat = 4;
|
|
|
+
|
|
|
+ (* Clock Control register values *)
|
|
|
+ ClockControl_InternalClockEnable = 0;
|
|
|
+ ClockControl_InternalClockState = 1;
|
|
|
+ ClockControl_SdClockEnable = 2;
|
|
|
+ ClockControl_ClockGeneratorSelect = 5;
|
|
|
+ ClockControl_SdClockFreqUpperOfs = 6;
|
|
|
+ ClockControl_SdClockFreqUpperMask = {6, 7};
|
|
|
+ ClockControl_SdClockFreqOfs = 8;
|
|
|
+ ClockControl_SdClockFreqMask = {8 .. 15};
|
|
|
+
|
|
|
+ (* Power Control register values *)
|
|
|
+ PowerControl_SDBusPower* = 0;
|
|
|
+ PowerControl_SDBusVoltageOfs = 1;
|
|
|
+ PowerControl_SDBusVoltageMask = {1 .. 3};
|
|
|
+ PowerControl_SDBusVoltage_18 = {1, 3};
|
|
|
+ PowerControl_SDBusVoltage_30 = {2, 3};
|
|
|
+ PowerControl_SDBusVoltage_33 = {1, 2, 3};
|
|
|
+
|
|
|
+ (* Host Controller Version *)
|
|
|
+ HostControllerVersion_SpecificationMask = {0 .. 7};
|
|
|
+ HostControllerVersion_VendorOfs = 8;
|
|
|
+ HostControllerVersion_VendorMask = {8 .. 15};
|
|
|
+
|
|
|
+
|
|
|
+ (* SD Status fields *)
|
|
|
+ SdStatus_FuleSupport = 312;
|
|
|
+ SdStatus_DiscardSupport = 313;
|
|
|
+ SdStatus_PerformanceEnhanceOfs = 335;
|
|
|
+ SdStatus_PerformanceEnhanceWidth = 8;
|
|
|
+ SdStatus_AppPerfClassOfs = 336;
|
|
|
+ SdStatus_AppPerfClassWidth = 4;
|
|
|
+ SdStatus_SusAddrOfs = 346;
|
|
|
+ SdStatus_SusAddrWidth = 22;
|
|
|
+ SdStatus_VscAuSizeOfs = 368;
|
|
|
+ SdStatus_VscAuSizeWidth = 10;
|
|
|
+ SdStatus_VideoSpeedClassOfs = 384;
|
|
|
+ SdStatus_VideoSpeedClassWidth = 8;
|
|
|
+ SdStatus_UhsAuSizeOfs = 392;
|
|
|
+ SdStatus_UhsAuSizeWidth = 4;
|
|
|
+ SdStatus_UhsSpeedGradeOfs = 396;
|
|
|
+ SdStatus_UhsSpeedGradeWidth = 4;
|
|
|
+ SdStatus_EraseOffsetOfs = 400;
|
|
|
+ SdStatus_EraseOffsetWidth = 2;
|
|
|
+ SdStatus_EraseTimeoutOfs = 402;
|
|
|
+ SdStatus_EraseTimeoutWidth = 6;
|
|
|
+ SdStatus_EraseSizeOfs = 408;
|
|
|
+ SdStatus_EraseSizeWidth = 16;
|
|
|
+ SdStatus_AuSizeOfs = 428;
|
|
|
+ SdStatus_AuSizeWidth = 4;
|
|
|
+ SdStatus_PerformanceMoveOfs = 432;
|
|
|
+ SdStatus_PerformanceMoveWidth = 8;
|
|
|
+ SdStatus_SpeedClassOfs = 440;
|
|
|
+ SdStatus_SpeedClassWidth = 8;
|
|
|
+ SdStatus_SizeOfProtectedAreaOfs = 448;
|
|
|
+ SdStatus_SizeOfProtectedAreaWidth = 32;
|
|
|
+ SdStatus_SdCardTypeOfs = 480;
|
|
|
+ SdStatus_SdCardTypeWidth = 16;
|
|
|
+ SdStatus_SecuredMode = 509;
|
|
|
+ SdStatus_DatBusWidthOfs = 510;
|
|
|
+ SdStatus_DatBusWidthWidth = 2;
|
|
|
+
|
|
|
+ (* Card Status register -- R1 *)
|
|
|
+ CardStatus_AkeSpecError = 3;
|
|
|
+ CardStatus_AppCmd = 5;
|
|
|
+ CardStatus_ReadyForData = 8;
|
|
|
+ CardStatus_CurrentStateOffset = 9;
|
|
|
+ CardStatus_CurrentStateMask = {9 .. 12};
|
|
|
+ CardStatus_EraseReset = 13;
|
|
|
+ CardStatus_CardEccDisable = 14;
|
|
|
+ CardStatus_WpEraseSkip = 15;
|
|
|
+ CardStatus_CsdOverwrite = 16;
|
|
|
+ CardStatus_Error = 19;
|
|
|
+ CardStatus_CcError = 20;
|
|
|
+ CardStatus_CardEccFailed = 21;
|
|
|
+ CardStatus_IllegalCommand = 22;
|
|
|
+ CardStatus_ComCrcError = 23;
|
|
|
+ CardStatus_LockUnlockFailed = 24;
|
|
|
+ CardStatus_CardIsLocked = 25;
|
|
|
+ CardStatus_WpViolation = 26;
|
|
|
+ CardStatus_EraseParam = 27;
|
|
|
+ CardStatus_EraseSeqError = 28;
|
|
|
+ CardStatus_BlockLenError = 29;
|
|
|
+ CardStatus_AddressError = 30;
|
|
|
+ CardStatus_OutOfRange = 31;
|
|
|
+
|
|
|
+ (* OCR Registers *)
|
|
|
+ CardOcr_Vdd27_28 = 15;
|
|
|
+ CardOcr_Vdd28_29 = 16;
|
|
|
+ CardOcr_Vdd29_30 = 17;
|
|
|
+ CardOcr_Vdd30_31 = 18;
|
|
|
+ CardOcr_Vdd31_32 = 19;
|
|
|
+ CardOcr_Vdd32_33 = 20;
|
|
|
+ CardOcr_Vdd33_34 = 21;
|
|
|
+ CardOcr_Vdd34_35 = 22;
|
|
|
+ CardOcr_Vdd35_36 = 23;
|
|
|
+ CardOcr_S18A = 24;
|
|
|
+ CardOcr_UHS2CardStatus = 29;
|
|
|
+ CardOcr_CardCapacityStatus = 30;
|
|
|
+ CardOcr_PowerUpStatus = 31;
|
|
|
+
|
|
|
+ (* CID Register *)
|
|
|
+ CardCid_ManufacturerIdOfs = 112;
|
|
|
+ CardCid_ManufacturerIdWidth = 8;
|
|
|
+ CardCid_OEM_ApplicationIdOfs = 96;
|
|
|
+ CardCid_OEM_ApplicationIdWidth = 16;
|
|
|
+ CardCid_ProductNameOfs = 56;
|
|
|
+ CardCid_ProductNameWidth = 40;
|
|
|
+ CardCid_ProductRevisionOfs = 48;
|
|
|
+ CardCid_ProductRevisionWidth = 8;
|
|
|
+ CardCid_ProductSerialNbOfs = 16;
|
|
|
+ CardCid_ProductSerialNbWidth = 32;
|
|
|
+ CardCid_ProductManufacturingDateOfs = 0;
|
|
|
+ CardCid_ProductManufacturingDateWidth = 12;
|
|
|
+
|
|
|
+ (* CSD Register. This excludes the CRC7 of the specifications. *)
|
|
|
+ CardCsd_FileFormatOfs = 2;
|
|
|
+ CardCsd_FileFormatWidth = 2;
|
|
|
+ CardCsd_TmpWriteProtect = 4;
|
|
|
+ CardCsd_PermWriteProtect = 5;
|
|
|
+ CardCsd_Copy = 6;
|
|
|
+ CardCsd_FileFormatGrp = 7;
|
|
|
+ CardCsd_WriteBlPartial = 13;
|
|
|
+ CardCsd_WriteBlLenOfs = 14;
|
|
|
+ CardCsd_WriteBlLenWidth = 4;
|
|
|
+ CardCsd_R2wFactorOfs = 18;
|
|
|
+ CardCsd_R2wFactorWidth = 3;
|
|
|
+ CardCsd_WpGrpEnable = 23;
|
|
|
+ CardCsd_WpGrpSizeOfs = 24;
|
|
|
+ CardCsd_WpGrpSizeWidth = 7;
|
|
|
+ CardCsd_SectorSizeOfs = 31;
|
|
|
+ CardCsd_SectorSizeWidth = 7;
|
|
|
+ CardCsd_EraseBlkEn = 38;
|
|
|
+ CardCsd_CSizeMultOfs1 = 39; (** V1 *)
|
|
|
+ CardCsd_CSizeMultWidth1 = 3; (** V1 *)
|
|
|
+ CardCsd_VddWCurrMaxOfs1 = 42; (** V1 *)
|
|
|
+ CardCsd_VddWCurrMaxWidth1 = 3; (** V1 *)
|
|
|
+ CardCsd_VddWCurrMinOfs1 = 45; (** V1 *)
|
|
|
+ CardCsd_VddWCurrMinWidth1 = 3; (** V1 *)
|
|
|
+ CardCsd_VddRCurrMaxOfs1 = 48; (** V1 *)
|
|
|
+ CardCsd_VddRCurrMaxWidth1 = 3; (** V1 *)
|
|
|
+ CardCsd_VddRCurrMinOfs1 = 51; (** V1 *)
|
|
|
+ CardCsd_VdddRCurrMaxWidth1 = 3; (** V1 *)
|
|
|
+ CardCsd_CSizeOfs1 = 54; (** V1 *)
|
|
|
+ CardCsd_CSizeWidth1 = 12; (** V1 *)
|
|
|
+ CardCsd_CSizeOfs2 = 40; (** V2 *)
|
|
|
+ CardCsd_CSizeWidth2 = 22; (** V2 *)
|
|
|
+ CardCsd_DsrImp = 68;
|
|
|
+ CardCsd_ReadBlkMisalign = 69;
|
|
|
+ CardCsd_WriteBlkMisalign = 70;
|
|
|
+ CardCsd_ReadBlPartial = 71;
|
|
|
+ CardCsd_ReadBlLenOfs = 72;
|
|
|
+ CardCsd_ReadBlLenWidth = 4;
|
|
|
+ CardCsd_CccOfs = 76;
|
|
|
+ CardCsd_CccWidth = 12;
|
|
|
+ CardCsd_TranSpeedOfs = 88;
|
|
|
+ CardCsd_TranSpeedWidth = 8;
|
|
|
+ CardCsd_NsacOfs = 96;
|
|
|
+ CardCsd_NsacWidth = 8;
|
|
|
+ CardCsd_TaacOfs = 104;
|
|
|
+ CardCsd_TaacWidth = 8;
|
|
|
+ CardCsd_CsdStructureOfs = 118;
|
|
|
+ CardCsd_CsdStructureWidth = 2;
|
|
|
+
|
|
|
+ (* SCR Register *)
|
|
|
+ CardScr_CommandSupportOfs = 32;
|
|
|
+ CardScr_CommandSupportWidth = 4;
|
|
|
+ CardScr_SpecVXOfs = 38;
|
|
|
+ CardScr_SpecVXWidth = 4;
|
|
|
+ CardScr_SpecV4 = 42;
|
|
|
+ CardScr_ExtendedSecurityOfs = 43;
|
|
|
+ CardScr_ExtendedSecurityWidth = 4;
|
|
|
+ CardScr_SpecV3 = 47;
|
|
|
+ CardScr_BusWidthsOfs = 48;
|
|
|
+ CardScr_BusWidthsWidth = 4;
|
|
|
+ CardScr_SecurityOfs = 52;
|
|
|
+ CardScr_SecurityWidth = 3;
|
|
|
+ CardScr_DataStateAfterErase = 55;
|
|
|
+ CardScr_SpecVersionOfs = 56;
|
|
|
+ CardScr_SpecVersionWidth = 4;
|
|
|
+ CardScr_StructureOfs = 60;
|
|
|
+ CardScr_StructureWidth = 4;
|
|
|
+
|
|
|
+ (* SCR register fields values *)
|
|
|
+ CardScr_SpecVX_v5 = 1;
|
|
|
+ CardScr_SpecVX_v6 = 2;
|
|
|
+
|
|
|
+ (* Card categories, used in SD status card type *)
|
|
|
+ CategoryRW * = 0;
|
|
|
+ CategoryRO * = 1;
|
|
|
+ CategoryOTP * = 2;
|
|
|
+
|
|
|
+ (* Performance enhancing features, used in perfEnhance in SD status *)
|
|
|
+ PerformanceCardMaintenance * = 0; (** Card supports card-initiated maintenance *)
|
|
|
+ PerformanceHostMaintenance * = 1; (** Card supports host-initiated maintenance *)
|
|
|
+ PerformanceCache * = 2; (** Card supports internal caching *)
|
|
|
+ PerformanceQueue * =3; (** Card supports command queue *)
|
|
|
+
|
|
|
+ (* Transfer options *)
|
|
|
+ TxDma = TRUE; (** Use DMA for transfers on all hosts that support it *)
|
|
|
+ TxBufferSize = 4096; (** Buffer size used for DMA transfers *)
|
|
|
+ TxBufferAlign = 32; (** Alignment requirement on DMA buffer: here cache line size of ARM *)
|
|
|
+
|
|
|
+ (* ADMA2 flags *) (*! Do not change values *)
|
|
|
+ Adma2Valid = 0; (** Valid entry *)
|
|
|
+ Adma2End = 1; (** Last entry *)
|
|
|
+ Adma2Int = 2; (** Entry generates interrupt on completion *)
|
|
|
+ Adma2Nop = {}; (** No-operation, just continue to next *)
|
|
|
+ Adma2Trans = {5}; (** Transfer descriptor *)
|
|
|
+ Adma2Link = {4, 5}; (** Link descriptor *)
|
|
|
+ Adma2ActMask = {4, 5}; (** Mask for Nop, Trans and Link *)
|
|
|
+
|
|
|
+ (* Timeout values *)
|
|
|
+ TimeoutCardInit = 100; (** Timeout used in card initialization, ms *)
|
|
|
+ TimeoutReadFactor = 100; (** Timeout factor on typical read time (according to CSD) *)
|
|
|
+ TimeoutReadFix = 100; (** Maximal read timeout in ms. To be used unconditionally with SDHC *)
|
|
|
+ TimeoutWriteFactor = 100; (** Timeout factor on typical write time (according to CSD) *)
|
|
|
+ TimeoutWriteFix = 250; (** Maximal write timeout in ms. To be used unconditionally with SDHC *)
|
|
|
+ TimeoutErase = 250; (** Typical timeout per erased block, ms *)
|
|
|
+
|
|
|
+ DefaultTimeout = 1000; (* Default timeout for blocking, in ms *)
|
|
|
+
|
|
|
+ (* Multi-threading *)
|
|
|
+ Synchronize * = FALSE (*TRUE*); (** Do we need to take care of concurrency? *)
|
|
|
+
|
|
|
+ (* Tracing options for debugging *)
|
|
|
+ EnableTraceCmd* = FALSE;
|
|
|
+ EnableTrace* = FALSE;
|
|
|
+
|
|
|
+TYPE
|
|
|
+ (** Command execution procedure *)
|
|
|
+ CommandProcedure * = PROCEDURE (VAR command: Command; VAR result: LONGINT): BOOLEAN;
|
|
|
+ (** Data command execution procedure *)
|
|
|
+ TransferProcedure * = PROCEDURE (VAR command: Command; VAR data: ARRAY OF SYSTEM.BYTE; ofs, len: LONGINT; VAR result: LONGINT): BOOLEAN;
|
|
|
+ (** Procedure called to wait for interrupt. mask specifies which interrupts are expected, timeout is in ms. Returns FALSE if timeout occurred *)
|
|
|
+ Blocker * = PROCEDURE {DELEGATE} (hc: HostController; mask: SET; timeout: LONGINT): BOOLEAN;
|
|
|
+
|
|
|
+ (**
|
|
|
+ SD Host controller descriptor.
|
|
|
+ *)
|
|
|
+ HostController * = POINTER TO HostControllerDesc;
|
|
|
+ HostControllerDesc * = RECORD
|
|
|
+ state -, (** HC state *)
|
|
|
+ version -: LONGINT; (** Specifications version *)
|
|
|
+
|
|
|
+ execute -: CommandProcedure; (** Method to execute commands *)
|
|
|
+ transfer -: TransferProcedure; (** Method to execute data commands *)
|
|
|
+ acquire *, release *: PROCEDURE {DELEGATE}; (** Procedures used for locking *)
|
|
|
+
|
|
|
+ baseFrequency, (** Base hc clock frequency *)
|
|
|
+ frequency, (** Bus frequency *)
|
|
|
+ timeoutFrequency: HUGEINT; (** Timeout clock frequency *)
|
|
|
+ lastRca: LONGINT; (** Last RCA selected by the controller. *)
|
|
|
+
|
|
|
+ handle: EventHandler; (** Card event handler *)
|
|
|
+ handlerParam: ANY; (** Parameter of the eventHandler *)
|
|
|
+ block: Blocker; (** Procedure called to wait for interrupts *)
|
|
|
+
|
|
|
+ regs-: HcRegisters; (** Memory-mapped I/O registers *)
|
|
|
+ cards: Card; (** List of cards on this HC *)
|
|
|
+ next: HostController; (** Linked list of controllers for interrupt handling *)
|
|
|
+ desc{ALIGNED(32)}: ARRAY 32 OF HUGEINT; (** DMA descriptor *)
|
|
|
+ END;
|
|
|
+
|
|
|
+ (**
|
|
|
+ Command record.
|
|
|
+ This record type is used to describe a command and its result.
|
|
|
+ To execute a command, fill in the fields 'hc', 'command', 'argument', 'responseType'.
|
|
|
+ If the command uses the DAT line or is an application command, setup then necessary flags.
|
|
|
+ If the command is an application command, you also need to specify the RCA for CMD55.
|
|
|
+ After command execution, you can read the command response in the 'response' field. Use the
|
|
|
+ 'GetR*' procedures to extract all information from this field in a convenient way.
|
|
|
+ *)
|
|
|
+ Command * = RECORD
|
|
|
+ hc *: HostController; (** Host controller on which the command is executed *)
|
|
|
+ rca *, (** Optional RCA parameter. Required only for ACMDs *)
|
|
|
+ command *, (** Command number *)
|
|
|
+ argument *, (** Command argument *)
|
|
|
+ responseType *, (** Response type *)
|
|
|
+ blockSize *, (** Block size for read, write or erase *)
|
|
|
+ dataTimeout *: LONGINT; (** Timeout value used for data line *)
|
|
|
+ flags *: SET; (** Command flags *)
|
|
|
+ response *: ARRAY 4 OF LONGINT; (** Response *)
|
|
|
+ END;
|
|
|
+
|
|
|
+ (** SWITCH_FUNC returned status *)
|
|
|
+ SwitchFuncStatus * = RECORD
|
|
|
+ current -: LONGINT; (** Current for specified config *)
|
|
|
+ functionGroups -: ARRAY 6 OF SET; (** Supported function in each group *)
|
|
|
+ functionStatus -: ARRAY 6 OF LONGINT; (** Function status *)
|
|
|
+ END;
|
|
|
+
|
|
|
+ (** Card event handler. 'card' is the card for which an event is reported (can be a new card object) and 'event' is one of 'On*' constants *)
|
|
|
+ EventHandler * = PROCEDURE {DELEGATE} (card: Card; event: LONGINT; param: ANY);
|
|
|
+
|
|
|
+ (** Host controller registers *)
|
|
|
+ HcRegisters * = POINTER {UNSAFE,UNTRACED} TO RECORD
|
|
|
+ SDMASystemAddress * {ALIGNED(1)}: LONGINT; (** offset = 0H *)
|
|
|
+ BlockSize * {ALIGNED(1)}, (** offset = 4H *)
|
|
|
+ BlockCount * {ALIGNED(1)}: INTEGER; (** offset = 6H *)
|
|
|
+ Argument1 * {ALIGNED(1)}: LONGINT; (** offset = 8H *)
|
|
|
+ TransferMode * {ALIGNED(1)}, (** offset = 0CH *)
|
|
|
+ Command * {ALIGNED(1)}: INTEGER; (** offset = 0EH *)
|
|
|
+ Response * {ALIGNED(1)}: ARRAY 4 OF LONGINT; (** offset = 10H *)
|
|
|
+ BufferData * {ALIGNED(1)}: LONGINT;
|
|
|
+ PresentState * {ALIGNED(1)}: SET;
|
|
|
+ HostControl1 * {ALIGNED(1)},
|
|
|
+ PowerControl * {ALIGNED(1)},
|
|
|
+ BlockGapControl * {ALIGNED(1)},
|
|
|
+ WakeupControl * {ALIGNED(1)}: SHORTINT;
|
|
|
+ ClockControl * {ALIGNED(1)}: INTEGER;
|
|
|
+ TimeoutControl * {ALIGNED(1)},
|
|
|
+ SoftwareReset * {ALIGNED(1)}: SHORTINT;
|
|
|
+ InterruptStatus * {ALIGNED(1)},
|
|
|
+ InterruptStatusEnable * {ALIGNED(1)},
|
|
|
+ InterruptSignalEnable * {ALIGNED(1)}: SET;
|
|
|
+ AutoCmdErrorStatus * {ALIGNED(1)},
|
|
|
+ HostControl2 * {ALIGNED(1)}: INTEGER;
|
|
|
+ Capabilities * {ALIGNED(1)}: ARRAY 2 OF SET;
|
|
|
+ MaximumCurrentCapabilities * {ALIGNED(1)}: HUGEINT;
|
|
|
+ ForceEventAutoCmdErrorStatus * {ALIGNED(1)},
|
|
|
+ ForceEventErrorInterruptStatus * {ALIGNED(1)}: INTEGER;
|
|
|
+ AdmaErrorStatus * {ALIGNED(1)}: SHORTINT;
|
|
|
+ padding0 * {ALIGNED(1)}: ARRAY 3 OF SHORTINT;
|
|
|
+ AdmaSystemAddress * {ALIGNED(1)}: HUGEINT;
|
|
|
+ PresetValues * {ALIGNED(1)}: ARRAY 8 OF INTEGER;
|
|
|
+ padding1 * {ALIGNED(1)}: ARRAY 28 OF LONGINT;
|
|
|
+ SharedBusControl * {ALIGNED(1)}: LONGINT;
|
|
|
+ padding2 * {ALIGNED(1)}: ARRAY 6 OF LONGINT;
|
|
|
+ SlotInterruptStatus * {ALIGNED(1)},
|
|
|
+ HostControllerVersion * {ALIGNED(1)}: INTEGER;
|
|
|
+ END;
|
|
|
+
|
|
|
+VAR
|
|
|
+ (** List of all host controllers *)
|
|
|
+ hcs: HostController;
|
|
|
+
|
|
|
+ (** Statistics *)
|
|
|
+ NbyteRead -,
|
|
|
+ NbyteWritten -,
|
|
|
+ Nread -,
|
|
|
+ Nwrite -: HUGEINT;
|
|
|
+ Tread -,
|
|
|
+ Twrite -: HUGEINT;
|
|
|
+ start, stop: HUGEINT;
|
|
|
+
|
|
|
+ (* ==================== Host Controller (Low-Level) Interface ==================== *)
|
|
|
+ (**
|
|
|
+ Create an host controller descriptor and initializes it with the given info.
|
|
|
+ 'baseAddress' is the base address of the IO registers.
|
|
|
+ 'extClockFreq' is an optional external clock frequency. It is used iff the host controller has no information about its clock frequency.
|
|
|
+ 'handler' is the event handler and 'param' is user parameter.
|
|
|
+ *)
|
|
|
+ PROCEDURE InitHostController * (hc: HostController; baseAddress: ADDRESS)(*: HostController*);
|
|
|
+ VAR
|
|
|
+ val: LONGINT;
|
|
|
+ BEGIN
|
|
|
+ hc.regs := baseAddress;
|
|
|
+ IF ~Reset(hc, TRUE, TRUE) THEN (*RETURN NIL*) END;
|
|
|
+ hc.baseFrequency := LSH(SYSTEM.VAL(LONGINT, hc.regs.Capabilities[0] * Capabilities_BaseClockFreqSdMask), -Capabilities_BaseClockFreqSdOfs);
|
|
|
+ hc.timeoutFrequency := LSH(SYSTEM.VAL(LONGINT, hc.regs.Capabilities[0] * Capabilities_TimeoutClockFrequencyMask), -Capabilities_TimeoutClockFrequencyOfs) * 1000;
|
|
|
+ IF Capabilities_TimeoutClockUnit IN hc.regs.Capabilities[0] THEN hc.timeoutFrequency := hc.timeoutFrequency * 1000 END;
|
|
|
+ (*SetBusClock(hc, InitialClockFrequency);
|
|
|
+ SetTimeout(hc, 100);*)
|
|
|
+
|
|
|
+ (* Power select 3.3V bus voltage *)
|
|
|
+ hc.regs.PowerControl := SYSTEM.VAL(SHORTINT, PowerControl_SDBusVoltage_33 + {PowerControl_SDBusPower});
|
|
|
+
|
|
|
+ (* Enable All Interrupts *)
|
|
|
+ hc.regs.InterruptStatusEnable := Interrupt_All;
|
|
|
+ hc.regs.InterruptSignalEnable := {Interrupt_Normal_CardInsertion, Interrupt_Normal_CardRemoval};
|
|
|
+ hc.regs.BlockGapControl := 0;
|
|
|
+
|
|
|
+ val := LONGINT(hc.regs.HostControllerVersion);
|
|
|
+ hc.version := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, val) * HostControllerVersion_SpecificationMask) + 1;
|
|
|
+
|
|
|
+ IF EnableTrace THEN
|
|
|
+ Log.String("[SD] New Host Controller v");
|
|
|
+ Log.Int(hc.version, 0);
|
|
|
+ Log.String(" at ");
|
|
|
+ Log.Address(baseAddress);
|
|
|
+ Log.Ln;
|
|
|
+ PrintCapabilities(hc);
|
|
|
+ END;
|
|
|
+
|
|
|
+ IF Synchronize THEN SdEnvironment.GetLock(hc.acquire, hc.release) END;
|
|
|
+
|
|
|
+ (* Select method according to DMA support *)
|
|
|
+ IF TxDma THEN
|
|
|
+ IF (Capabilities_ADMA2 IN hc.regs.Capabilities[0]) THEN
|
|
|
+ hc.transfer := ExecuteAdmaCommand;
|
|
|
+ hc.regs.HostControl1 := SYSTEM.VAL(SHORTINT, SYSTEM.VAL(SET, LONGINT(hc.regs.HostControl1)) + HostControl1_DmaSelect_32Adma);
|
|
|
+ ELSE
|
|
|
+ (*! ADMA1 and SDMA are not implemented yet *)
|
|
|
+ hc.transfer := ExecuteDataCommand
|
|
|
+ END
|
|
|
+ ELSE
|
|
|
+ hc.transfer := ExecuteDataCommand
|
|
|
+ END;
|
|
|
+ hc.execute := ExecuteCommand;
|
|
|
+ hc.block := SpinBlock; (* Use spin block by default *)
|
|
|
+
|
|
|
+ IF (hc.baseFrequency = 0) OR (hc.timeoutFrequency = 0) THEN
|
|
|
+ hc.state := HcConfiguring
|
|
|
+ ELSE
|
|
|
+ hc.state := HcOperational
|
|
|
+ END;
|
|
|
+
|
|
|
+ (* If there is a card already, initialize it *)
|
|
|
+ (*IF (PresentState_CardInserted IN hc.regs.PresentState) & (PresentState_CardStateStable IN hc.regs.PresentState) THEN
|
|
|
+ NEW(hc.cards);
|
|
|
+ IF InitCard(hc, hc.cards, result) & (hc.handle # NIL) THEN
|
|
|
+ hc.handle(hc.cards, OnInitialization, hc.handlerParam)
|
|
|
+ ELSIF hc.handle # NIL THEN
|
|
|
+ Log.String("[SD] Could not initialize inserted card: error ");
|
|
|
+ Log.Int(result, 0);
|
|
|
+ Log.Ln;
|
|
|
+ END
|
|
|
+ END;*)
|
|
|
+
|
|
|
+ hc.next := hcs;
|
|
|
+ hcs := hc;
|
|
|
+ END InitHostController;
|
|
|
+
|
|
|
+ (** Set external clock for a host controller. Host state must be HcConfiguring. bus is the SD bus clock frequency, timeout is the timeout clock frequency. *)
|
|
|
+ PROCEDURE SetExternalClock * (hc: HostController; bus, timeout: HUGEINT; VAR result: LONGINT): BOOLEAN;
|
|
|
+ BEGIN
|
|
|
+ IF hc.state # HcConfiguring THEN
|
|
|
+ result := ErrorInvalidParameters;
|
|
|
+ RETURN FALSE
|
|
|
+ END;
|
|
|
+ hc.baseFrequency := bus;
|
|
|
+ hc.timeoutFrequency := timeout;
|
|
|
+ hc.state := HcOperational;
|
|
|
+ RETURN TRUE
|
|
|
+ END SetExternalClock;
|
|
|
+
|
|
|
+ (** Set an event handler for a host controller *)
|
|
|
+ PROCEDURE SetEventHandler * (hc: HostController; handler: EventHandler; param: ANY);
|
|
|
+ VAR
|
|
|
+ result: LONGINT;
|
|
|
+ BEGIN
|
|
|
+ hc.handle := handler;
|
|
|
+ hc.handlerParam := param;
|
|
|
+
|
|
|
+ (* Handle events if necessary *)
|
|
|
+ IF (PresentState_CardInserted IN hc.regs.PresentState) & (PresentState_CardStateStable IN hc.regs.PresentState) THEN
|
|
|
+ NEW(hc.cards);
|
|
|
+ IF InitCard(hc, hc.cards, result) & (hc.handle # NIL) THEN
|
|
|
+ hc.handle(hc.cards, OnInitialization, hc.handlerParam)
|
|
|
+ ELSIF hc.handle # NIL THEN
|
|
|
+ Log.String("[SD] Could not initialize inserted card: error ");
|
|
|
+ Log.Int(result, 0);
|
|
|
+ Log.Ln;
|
|
|
+ END
|
|
|
+ END;
|
|
|
+ END SetEventHandler;
|
|
|
+
|
|
|
+ (** Change the wait for interrupt method *)
|
|
|
+ PROCEDURE SetBlocker * (hc: HostController; blocker: Blocker);
|
|
|
+ BEGIN
|
|
|
+ hc.block := blocker
|
|
|
+ END SetBlocker;
|
|
|
+
|
|
|
+ (** Turns the busy LED on or off *)
|
|
|
+ PROCEDURE SetLedState * (hc: HostController; on: BOOLEAN);
|
|
|
+ VAR
|
|
|
+ reg: SET;
|
|
|
+ BEGIN
|
|
|
+ reg := SYSTEM.VAL(SET, hc.regs.HostControl1);
|
|
|
+ IF on THEN
|
|
|
+ INCL(reg, HostControl1_LedControl)
|
|
|
+ ELSE
|
|
|
+ EXCL(reg, HostControl1_LedControl)
|
|
|
+ END;
|
|
|
+ hc.regs.HostControl1 := SYSTEM.VAL(SHORTINT, reg)
|
|
|
+ END SetLedState;
|
|
|
+
|
|
|
+ (** Get the state of the busy LED *)
|
|
|
+ PROCEDURE GetLedState * (hc: HostController): BOOLEAN;
|
|
|
+ BEGIN
|
|
|
+ RETURN HostControl1_LedControl IN SYSTEM.VAL(SET, hc.regs.HostControl1)
|
|
|
+ END GetLedState;
|
|
|
+
|
|
|
+ (**
|
|
|
+ Execute the command 'command', without data transfer. If you need data transfer, use 'ExecuteDataCommand'.
|
|
|
+ Performs all necessary steps for executing a command:
|
|
|
+ o Runs CMD55 if command is an application command
|
|
|
+ o Execute the command
|
|
|
+ o Wait for response
|
|
|
+ *)
|
|
|
+ PROCEDURE ExecuteCommand (VAR command: Command; VAR result: LONGINT): BOOLEAN;
|
|
|
+ VAR
|
|
|
+ status, r1: SET;
|
|
|
+ BEGIN
|
|
|
+ IF Synchronize THEN command.hc.acquire END;
|
|
|
+ result := ErrorNone;
|
|
|
+ (* Check Parameters *)
|
|
|
+ IF {FlagData, FlagRead, FlagAutoCmd12, FlagAutoCmd23, FlagMultipleBlockTx, FlagCountBlocks} * command.flags # {} THEN
|
|
|
+ result := ErrorInvalidParameters;
|
|
|
+ IF Synchronize THEN command.hc.release END;
|
|
|
+ RETURN FALSE
|
|
|
+ END;
|
|
|
+
|
|
|
+ IF (FlagApplicationCmd IN command.flags) THEN
|
|
|
+ IF ~StartCommand(command.hc, CMD_APP_CMD, LSH(command.rca, 16), ResponseR1, FlagRead IN command.flags, FALSE, FALSE, FALSE, status, result) THEN
|
|
|
+ IF Synchronize THEN command.hc.release END;
|
|
|
+ RETURN FALSE
|
|
|
+ END;
|
|
|
+ r1 := GetR1(command);
|
|
|
+ IF EnableTrace THEN PrintCardStatus(r1) END;
|
|
|
+ IF ~(FlagIgnoreIllegalCmd IN command.flags) & (CardStatus_Error IN r1) THEN
|
|
|
+ result := ErrorCard;
|
|
|
+ IF Synchronize THEN command.hc.release END;
|
|
|
+ RETURN FALSE
|
|
|
+ END
|
|
|
+ END;
|
|
|
+
|
|
|
+ IF ~StartCommand(
|
|
|
+ command.hc, command.command, command.argument, command.responseType, FlagRead IN command.flags, FALSE,
|
|
|
+ FALSE (*command.responseType = ResponseR1b*), FlagAbort IN command.flags, status, result) THEN
|
|
|
+ IF Synchronize THEN command.hc.release END;
|
|
|
+ RETURN FALSE
|
|
|
+ END;
|
|
|
+ GetResponse(command.hc, command.responseType, command.response);
|
|
|
+ IF command.command = CMD_SELECT_DESELECT_CARD THEN
|
|
|
+ command.hc.lastRca := LSH(command.argument, -16)
|
|
|
+ ELSIF command.command = CMD_GO_IDLE_STATE THEN
|
|
|
+ command.hc.lastRca := 0
|
|
|
+ END;
|
|
|
+ IF Synchronize THEN command.hc.release END;
|
|
|
+ RETURN TRUE
|
|
|
+ END ExecuteCommand;
|
|
|
+
|
|
|
+ (**
|
|
|
+ Execute command with data transfer using CPU.
|
|
|
+ Data is read from/written to [data[ofs], data[ofs + len]).
|
|
|
+ *)
|
|
|
+ PROCEDURE ExecuteDataCommand (VAR command: Command; VAR data: ARRAY OF SYSTEM.BYTE; ofs, len: LONGINT; VAR result: LONGINT): BOOLEAN;
|
|
|
+ TYPE
|
|
|
+ DataBytes = ARRAY 4 OF CHAR;
|
|
|
+ VAR
|
|
|
+ tmp: DataBytes;
|
|
|
+ i, stepLen: LONGINT;
|
|
|
+ hc: HostController;
|
|
|
+ r1, status, mask: SET;
|
|
|
+ BEGIN
|
|
|
+ ASSERT(ofs + len <= LEN(data), 7);
|
|
|
+ result := ErrorNone;
|
|
|
+ IF ~(FlagData IN command.flags) THEN
|
|
|
+ result := ErrorInvalidParameters;
|
|
|
+ RETURN FALSE
|
|
|
+ END;
|
|
|
+ hc := command.hc;
|
|
|
+
|
|
|
+ IF Synchronize THEN hc.acquire END;
|
|
|
+ (* Set timeout *)
|
|
|
+ SetTimeout(hc, command.dataTimeout);
|
|
|
+ (*IF (*~Reset(hc, TRUE, FALSE) OR*) ~Reset(hc, FALSE, TRUE) THEN
|
|
|
+ result := ErrorCard;
|
|
|
+ IF Synchronize THEN hc.release END;
|
|
|
+ RETURN FALSE
|
|
|
+ END;*)
|
|
|
+
|
|
|
+ IF (FlagApplicationCmd IN command.flags) THEN
|
|
|
+ IF ~StartCommand(hc, CMD_APP_CMD, LSH(command.rca, 16), ResponseR1, FlagRead IN command.flags, FALSE, FALSE, FALSE, status, result) THEN
|
|
|
+ IF Synchronize THEN hc.release END;
|
|
|
+ RETURN FALSE
|
|
|
+ END;
|
|
|
+ r1 := SYSTEM.VAL(SET, command.response[0]);
|
|
|
+ IF CardStatus_Error IN r1 THEN
|
|
|
+ IF Synchronize THEN hc.release END;
|
|
|
+ RETURN FALSE
|
|
|
+ END;
|
|
|
+ IF EnableTrace THEN
|
|
|
+ Log.String("[SD] CMD55 Status:");
|
|
|
+ Log.Ln;
|
|
|
+ PrintCardStatus(r1)
|
|
|
+ END
|
|
|
+ END;
|
|
|
+
|
|
|
+ (* 1 *)
|
|
|
+ IF ~(FlagApplicationCmd IN command.flags) &
|
|
|
+ ((command.command = CMD_READ_SINGLE_BLOCK) OR (command.command = CMD_READ_MULTIPLE_BLOCK) OR
|
|
|
+ (command.command = CMD_WRITE_BLOCK) OR (command.command = CMD_WRITE_MULTIPLE_BLOCK) OR
|
|
|
+ (command.command = 53)(* SDIO Command *)) THEN
|
|
|
+ IF len <= BlockSize THEN
|
|
|
+ hc.regs.BlockSize := INTEGER(len);
|
|
|
+ hc.regs.BlockCount := 1
|
|
|
+ ELSE
|
|
|
+ hc.regs.BlockSize := BlockSize;
|
|
|
+ hc.regs.BlockCount := INTEGER(len DIV BlockSize)
|
|
|
+ END;
|
|
|
+
|
|
|
+ IF EnableTrace THEN
|
|
|
+ Log.String("[SD] ");
|
|
|
+ IF FlagRead IN command.flags THEN Log.String("Read")
|
|
|
+ ELSE Log.String("Write") END;
|
|
|
+ Log.String(" parameters:"); Log.Ln;
|
|
|
+ Log.String("[SD] Block Size = "); Log.Int(hc.regs.BlockSize, 0); Log.Ln;
|
|
|
+ Log.String("[SD] Block Count = "); Log.Int(hc.regs.BlockCount, 0); Log.Ln;
|
|
|
+ Log.String("[SD] CMD"); Log.Int(command.command, 0); Log.Ln;
|
|
|
+ Log.String("[SD] Argument = "); Log.Address(command.argument); Log.Ln
|
|
|
+ END
|
|
|
+ END;
|
|
|
+
|
|
|
+ (* 3 - 8 *)
|
|
|
+ IF FlagRead IN command.flags THEN
|
|
|
+ (*REPEAT UNTIL (Interrupt_Normal_BufferReadReady IN hc.regs.InterruptStatus) OR (Interrupt_Normal_ErrorInterrupt IN hc.regs.InterruptStatus)*)
|
|
|
+ mask := {Interrupt_Normal_BufferReadReady, Interrupt_Normal_ErrorInterrupt}
|
|
|
+ ELSE
|
|
|
+ (*REPEAT UNTIL (Interrupt_Normal_BufferWriteReady IN hc.regs.InterruptStatus) OR (Interrupt_Normal_ErrorInterrupt IN hc.regs.InterruptStatus)*)
|
|
|
+ mask := {Interrupt_Normal_BufferWriteReady, Interrupt_Normal_ErrorInterrupt}
|
|
|
+ END;
|
|
|
+
|
|
|
+ IF ~StartCommand(hc, command.command, command.argument, ResponseR1, FlagRead IN command.flags, FALSE, TRUE, FALSE, status, result) THEN RETURN FALSE END;
|
|
|
+ r1 := SYSTEM.VAL(SET, command.response[0]);
|
|
|
+ IF CardStatus_Error IN r1 THEN
|
|
|
+ IF Synchronize THEN hc.release END;
|
|
|
+ RETURN FALSE
|
|
|
+ END;
|
|
|
+ IF EnableTrace THEN PrintCardStatus(r1) END;
|
|
|
+ WHILE len > 0 DO
|
|
|
+ (* 14 *)
|
|
|
+ IF ~hc.block(hc, mask, command.dataTimeout * 1000) THEN
|
|
|
+ Log.String("[SD] Error: interrupt timeout");
|
|
|
+ Log.Ln;
|
|
|
+ RETURN FALSE
|
|
|
+ END;
|
|
|
+ status := hc.regs.InterruptStatus;
|
|
|
+
|
|
|
+ IF Interrupt_Normal_ErrorInterrupt IN hc.regs.InterruptStatus THEN
|
|
|
+ IF ErrorRecovery(hc, result, status) THEN END;
|
|
|
+ IF Interrupt_Error_DataTimeout IN status THEN
|
|
|
+ result := ErrorDatTimeout
|
|
|
+ ELSIF Interrupt_Error_DataCrc IN status THEN
|
|
|
+ result := ErrorDatCrc
|
|
|
+ ELSIF Interrupt_Error_DataEndBit IN status THEN
|
|
|
+ result := ErrorCard
|
|
|
+ END;
|
|
|
+ IF Synchronize THEN hc.release END;
|
|
|
+ RETURN FALSE
|
|
|
+ END;
|
|
|
+ (* 15 *)
|
|
|
+ (*INCL(hc.regs.InterruptStatus, Interrupt_Normal_BufferReadReady);*)
|
|
|
+ hc.regs.InterruptStatus := {Interrupt_Normal_BufferReadReady};
|
|
|
+
|
|
|
+ (* 16 *)
|
|
|
+ stepLen := MIN(BlockSize, len);
|
|
|
+ IF FlagRead IN command.flags THEN
|
|
|
+ FOR i := 0 TO stepLen - 1 BY 4 DO
|
|
|
+ SYSTEM.PUT32(ADDRESSOF(data[ofs + i]), hc.regs.BufferData);
|
|
|
+ (*SYSTEM.VAL(LONGINT, tmp) := hc.regs.BufferData;
|
|
|
+ SYSTEM.VAL(DataBytes, data[ofs + i]) := tmp*)
|
|
|
+ END
|
|
|
+ ELSE
|
|
|
+ FOR i := 0 TO stepLen - 1 BY 4 DO
|
|
|
+ tmp := SYSTEM.VAL(DataBytes, data[ofs + i]);
|
|
|
+ hc.regs.BufferData := SYSTEM.VAL(LONGINT, tmp);
|
|
|
+ END
|
|
|
+ END;
|
|
|
+ (* 17 *)
|
|
|
+ INC(ofs, stepLen);
|
|
|
+ DEC(len, stepLen)
|
|
|
+ END;
|
|
|
+
|
|
|
+ (* 18 -> Not infinite block *)
|
|
|
+ (*REPEAT UNTIL Interrupt_Normal_TransferComplete IN hc.regs.InterruptStatus;*)
|
|
|
+ IF ~hc.block(hc, {Interrupt_Normal_TransferComplete}, command.dataTimeout * 1000) THEN
|
|
|
+ Log.String("[SD] Error: timeout interrupt");
|
|
|
+ Log.Ln;
|
|
|
+ RETURN FALSE
|
|
|
+ END;
|
|
|
+ (* 19 *)
|
|
|
+ (*INCL(hc.regs.InterruptStatus, Interrupt_Normal_TransferComplete);*)
|
|
|
+ hc.regs.InterruptStatus := {Interrupt_Normal_TransferComplete};
|
|
|
+
|
|
|
+ (*DEC(hc.regs.ClockControl, LSH(1, ClockControl_SdClockEnable));
|
|
|
+ hc.regs.SoftwareReset := SYSTEM.VAL(SHORTINT, {SoftwareResetCmd, SoftwareResetDat});
|
|
|
+ REPEAT UNTIL hc.regs.SoftwareReset = 0;
|
|
|
+ INC(hc.regs.ClockControl, LSH(1, ClockControl_SdClockEnable));*)
|
|
|
+
|
|
|
+ IF Synchronize THEN hc.release END;
|
|
|
+ RETURN TRUE (*Reset(hc, TRUE, FALSE) & Reset(hc, FALSE, TRUE)*)
|
|
|
+ END ExecuteDataCommand;
|
|
|
+
|
|
|
+ (**
|
|
|
+ Execute Command with data transfers using ADMA.
|
|
|
+ command: ADMA command
|
|
|
+ data, ofs, len: Buffer
|
|
|
+ result: error code
|
|
|
+ *)
|
|
|
+ PROCEDURE ExecuteAdmaCommand (VAR command: Command; VAR data: ARRAY OF SYSTEM.BYTE; ofs, len: LONGINT; VAR result: LONGINT): BOOLEAN;
|
|
|
+ VAR
|
|
|
+ tt: SdEnvironment.Time;
|
|
|
+ hc: HostController;
|
|
|
+ r1, status, flags: SET;
|
|
|
+ address, a: ADDRESS;
|
|
|
+ blocks, blockSize, desc, txlen, l, tx: LONGINT;
|
|
|
+ padd: BOOLEAN; (* Padd transfer to next block length? *)
|
|
|
+
|
|
|
+ PROCEDURE WriteAdma2Desc (address: ADDRESS; len: LONGINT; flags: SET): HUGEINT;
|
|
|
+ BEGIN
|
|
|
+ IF EnableTrace THEN
|
|
|
+ Log.String("[SD] ADMA2 Entry; address = "); Log.Hex(address, -8); Log.String(", size = "); Log.Int(len, 0);
|
|
|
+ Log.String(", flags = ");
|
|
|
+ IF flags * Adma2ActMask = Adma2Nop THEN Log.String("NOP ")
|
|
|
+ ELSIF flags * Adma2ActMask = Adma2Trans THEN Log.String("TRANS ")
|
|
|
+ ELSIF flags * Adma2ActMask = Adma2Link THEN Log.String("LINK ")
|
|
|
+ END;
|
|
|
+ IF Adma2Valid IN flags THEN Log.String("VALID ") END;
|
|
|
+ IF Adma2End IN flags THEN Log.String("END ") END;
|
|
|
+ IF Adma2Int IN flags THEN Log.String("INT ") END;
|
|
|
+ Log.Ln
|
|
|
+ END;
|
|
|
+ ASSERT(address MOD 4 = 0);
|
|
|
+ ASSERT((len > 0) OR (flags * Adma2ActMask # Adma2Trans));
|
|
|
+ RETURN LSH(HUGEINT(address), 32) + LSH(len MOD 65536, 16) + SYSTEM.VAL(LONGINT, flags)
|
|
|
+ END WriteAdma2Desc;
|
|
|
+
|
|
|
+ BEGIN
|
|
|
+ result := ErrorNone;
|
|
|
+ hc := command.hc;
|
|
|
+ IF Synchronize THEN hc.acquire END;
|
|
|
+
|
|
|
+ (* Setup descriptors *)
|
|
|
+ address := ADDRESSOF(data[ofs]);
|
|
|
+ (*IF ~(FlagRead IN command.flags) THEN*) SdEnvironment.FlushDCacheRange(address, len); (*END;*)
|
|
|
+ txlen := len;
|
|
|
+ flags := Adma2Trans + {Adma2Valid};
|
|
|
+ padd := FALSE; (*(len > BlockSize) & (len MOD BlockSize # 0);*)
|
|
|
+ l := len;
|
|
|
+ a := address;
|
|
|
+ WHILE l > 0 DO
|
|
|
+ IF ~padd & (l <= 65536) THEN flags := flags + {Adma2End, Adma2Int} END;
|
|
|
+ tx := MIN(l, 65536);
|
|
|
+ hc.desc[desc] := WriteAdma2Desc(a, tx, flags);
|
|
|
+ DEC(l, tx);
|
|
|
+ INC(a, tx);
|
|
|
+ INC(desc);
|
|
|
+ END;
|
|
|
+ (*hc.desc[desc] := WriteAdma2Desc(0, 0, {Adma2Valid, Adma2End, Adma2Int} + Adma2Nop); INC(desc);*)
|
|
|
+ SdEnvironment.FlushDCacheRange(ADDRESSOF(hc.desc[0]), (desc + 1) * 8);
|
|
|
+
|
|
|
+ (* Reset command and data lines *)
|
|
|
+ IF (*~Reset(hc, TRUE, FALSE) OR*) ~Reset(hc, FALSE, TRUE) THEN
|
|
|
+ result := ErrorCard;
|
|
|
+ IF Synchronize THEN hc.release END;
|
|
|
+ RETURN FALSE
|
|
|
+ END;
|
|
|
+
|
|
|
+ (* 1 *)
|
|
|
+ hc.regs.AdmaSystemAddress := ADDRESSOF(hc.desc[0]);
|
|
|
+
|
|
|
+ IF (FlagApplicationCmd IN command.flags) THEN
|
|
|
+ IF ~StartCommand(command.hc, CMD_APP_CMD, LSH(command.rca, 16), ResponseR1, FlagRead IN command.flags, FALSE, FALSE, FALSE, status, result) THEN
|
|
|
+ IF Synchronize THEN hc.release END;
|
|
|
+ RETURN FALSE
|
|
|
+ END;
|
|
|
+ r1 := SYSTEM.VAL(SET, command.response[0]);
|
|
|
+ IF EnableTrace THEN
|
|
|
+ Log.String("[SD] CMD55 Status:");
|
|
|
+ Log.Ln;
|
|
|
+ PrintCardStatus(r1)
|
|
|
+ END;
|
|
|
+ IF CardStatus_Error IN r1 THEN
|
|
|
+ result := ErrorCard;
|
|
|
+ IF Synchronize THEN hc.release END;
|
|
|
+ RETURN FALSE
|
|
|
+ END
|
|
|
+ END;
|
|
|
+
|
|
|
+ (* 2-3 *)
|
|
|
+ IF command.blockSize = 0 THEN
|
|
|
+ blockSize := BlockSize
|
|
|
+ ELSE
|
|
|
+ blockSize := command.blockSize
|
|
|
+ END;
|
|
|
+ IF FlagData IN command.flags THEN
|
|
|
+ IF txlen <= blockSize THEN
|
|
|
+ hc.regs.BlockSize := INTEGER(txlen);
|
|
|
+ hc.regs.BlockCount := 1;
|
|
|
+ blocks := 1
|
|
|
+ ELSE
|
|
|
+ hc.regs.BlockSize := INTEGER(blockSize);
|
|
|
+ blocks := txlen DIV blockSize;
|
|
|
+ hc.regs.BlockCount := INTEGER(blocks)
|
|
|
+ END;
|
|
|
+
|
|
|
+ (* Set data timeout *)
|
|
|
+ ASSERT(command.dataTimeout > 0);
|
|
|
+ SetTimeout(hc, command.dataTimeout);
|
|
|
+
|
|
|
+ IF EnableTrace THEN
|
|
|
+ Log.String("[SD] ");
|
|
|
+ IF FlagRead IN command.flags THEN Log.String("Read")
|
|
|
+ ELSE Log.String("Write") END;
|
|
|
+ Log.String(" parameters:"); Log.Ln;
|
|
|
+ Log.String("[SD] Block Size = "); Log.Int(hc.regs.BlockSize, 0); Log.Ln;
|
|
|
+ Log.String("[SD] Block Count = "); Log.Int(hc.regs.BlockCount, 0); Log.Ln;
|
|
|
+ Log.String("[SD] CMD"); Log.Int(command.command, 0); Log.Ln;
|
|
|
+ Log.String("[SD] Argument = "); Log.Address(command.argument); Log.Ln
|
|
|
+ END
|
|
|
+ END;
|
|
|
+
|
|
|
+ status := hc.regs.InterruptStatus;
|
|
|
+ ASSERT({Interrupt_Normal_TransferComplete, Interrupt_Normal_ErrorInterrupt} * status = {});
|
|
|
+ (*!start := SdEnvironment.GetTimeCounter();*)
|
|
|
+ IF ~StartCommand(command.hc, command.command, command.argument, command.responseType, FlagRead IN command.flags, TRUE, TRUE, FALSE, status, result) THEN
|
|
|
+ IF Synchronize THEN hc.release END;
|
|
|
+ RETURN FALSE
|
|
|
+ END;
|
|
|
+ r1 := SYSTEM.VAL(SET, command.response[0]);
|
|
|
+ IF CardStatus_Error IN r1 THEN
|
|
|
+ result := ErrorCard;
|
|
|
+ IF Synchronize THEN hc.release END;
|
|
|
+ RETURN FALSE
|
|
|
+ END;
|
|
|
+ IF EnableTrace THEN PrintCardStatus(r1) END;
|
|
|
+
|
|
|
+ (*t := SdEnvironment.GetTimeCounter();
|
|
|
+ tt := t + SdEnvironment.FromMilli(1000);*)
|
|
|
+ (*WHILE ({Interrupt_Normal_TransferComplete, Interrupt_Normal_ErrorInterrupt, Interrupt_Error_Adma} * status = {}) (*& (SdEnvironment.GetTimeCounter() <= tt)*) DO
|
|
|
+ status := hc.regs.InterruptStatus
|
|
|
+ END;*)
|
|
|
+ (*IF SdEnvironment.GetTimeCounter() > tt(*{Interrupt_Normal_TransferComplete, Interrupt_Normal_ErrorInterrupt} * status = {}*) THEN*)
|
|
|
+ IF ~hc.block(hc, {Interrupt_Normal_TransferComplete, Interrupt_Normal_ErrorInterrupt}, command.dataTimeout * 1000) THEN
|
|
|
+ Log.String("[SD] Error: timeout has expired!"); Log.Ln;
|
|
|
+ PrintHcRegisters(hc.regs);
|
|
|
+ IF Synchronize THEN hc.release END;
|
|
|
+ HALT(512);
|
|
|
+ RETURN FALSE;
|
|
|
+ END;
|
|
|
+ (*!stop := SdEnvironment.GetTimeCounter();*)
|
|
|
+ status := hc.regs.InterruptStatus;
|
|
|
+
|
|
|
+ IF Interrupt_Normal_ErrorInterrupt IN status THEN
|
|
|
+ IF ErrorRecovery(hc, result, status) THEN END;
|
|
|
+ IF Interrupt_Error_DataTimeout IN status THEN
|
|
|
+ result := ErrorDatTimeout;
|
|
|
+ IF ~Reset(hc, FALSE, TRUE) THEN END
|
|
|
+ ELSIF Interrupt_Error_DataCrc IN status THEN
|
|
|
+ result := ErrorDatCrc
|
|
|
+ ELSIF Interrupt_Error_DataEndBit IN status THEN
|
|
|
+ result := ErrorCard
|
|
|
+ ELSIF Interrupt_Error_Adma IN status THEN
|
|
|
+ HALT(182)
|
|
|
+ END;
|
|
|
+ IF Synchronize THEN hc.release END;
|
|
|
+ RETURN FALSE
|
|
|
+ END;
|
|
|
+ hc.regs.InterruptStatus := status;
|
|
|
+ IF FlagRead IN command.flags THEN
|
|
|
+ SdEnvironment.InvalidateDCacheRange(address, len)
|
|
|
+ END;
|
|
|
+ (*!IF FlagRead IN command.flags THEN
|
|
|
+ INC(Tread, stop - start);
|
|
|
+ ELSE
|
|
|
+ INC(Twrite, stop - start);
|
|
|
+ END;*)
|
|
|
+ IF Synchronize THEN hc.release END;
|
|
|
+ RETURN TRUE
|
|
|
+ END ExecuteAdmaCommand;
|
|
|
+
|
|
|
+ (** Get the response registers in 'response' *)
|
|
|
+ PROCEDURE GetResponse (hc: HostController; responseType: LONGINT; VAR response: ARRAY 4 OF LONGINT);
|
|
|
+ BEGIN
|
|
|
+ response := hc.regs.Response
|
|
|
+ END GetResponse;
|
|
|
+
|
|
|
+ (** Issue an SD Card Transaction. [Simplified specs. 3.7.1.1 pp. 106-108] *)
|
|
|
+ PROCEDURE StartCommand (hc: HostController; cmd, argument, responseType: LONGINT; read, dma, busy, abort: BOOLEAN; VAR status: SET; VAR result: LONGINT): BOOLEAN;
|
|
|
+ VAR
|
|
|
+ t: HUGEINT;
|
|
|
+ reg: LONGINT;
|
|
|
+ flags, txFlags: SET;
|
|
|
+ BEGIN
|
|
|
+ IF EnableTraceCmd THEN
|
|
|
+ Log.String("[SD] Sending Command CMD"); Log.Int(cmd, 0); Log.Ln;
|
|
|
+ Log.String("[SD] Argument: "); Log.Hex(argument, -8); Log.Ln
|
|
|
+ END;
|
|
|
+
|
|
|
+ (* 1 *)
|
|
|
+ t := SdEnvironment.GetTimeCounter() + SdEnvironment.FromMilli(1000);
|
|
|
+ WHILE (PresentState_CommandInhibitCmd IN hc.regs.PresentState) & (t > SdEnvironment.GetTimeCounter()) DO END;
|
|
|
+ IF t < SdEnvironment.GetTimeCounter() THEN
|
|
|
+ Log.String("[SD] Timeout error in StartCommand (1)");
|
|
|
+ Log.Ln;
|
|
|
+ PrintHcRegisters(hc);
|
|
|
+ RETURN FALSE
|
|
|
+ END;
|
|
|
+
|
|
|
+ (* 2 *)
|
|
|
+ IF busy THEN
|
|
|
+ (* 3 *)
|
|
|
+ IF ~abort THEN
|
|
|
+ (* 4 *)
|
|
|
+ t := SdEnvironment.GetTimeCounter() + SdEnvironment.FromMilli(1000);
|
|
|
+ WHILE (PresentState_CommandInhibitDat IN hc.regs.PresentState) & (t > SdEnvironment.GetTimeCounter()) DO END;
|
|
|
+ IF t < SdEnvironment.GetTimeCounter() THEN
|
|
|
+ Log.String("[SD] Timeout error in StartCommand (2)");
|
|
|
+ Log.Ln;
|
|
|
+ PrintHcRegisters(hc);
|
|
|
+ RETURN FALSE
|
|
|
+ END;
|
|
|
+ END
|
|
|
+ END;
|
|
|
+
|
|
|
+ (* 5 *)
|
|
|
+ hc.regs.Argument1 := argument;
|
|
|
+
|
|
|
+ (* 6 *)
|
|
|
+ (* The response type determines the response-type and CRC/Index checks enabling *)
|
|
|
+ CASE responseType OF
|
|
|
+ ResponseNone:
|
|
|
+ reg := Command_ResponseType_None;
|
|
|
+ IF EnableTraceCmd THEN
|
|
|
+ Log.String("[SD] No Response Expected");
|
|
|
+ Log.Ln
|
|
|
+ END
|
|
|
+ |ResponseR1, ResponseR5, ResponseR6, ResponseR7:
|
|
|
+ reg := Command_ResponseType_48b;
|
|
|
+ flags := {Command_CrcCheckEnable, Command_IndexCheckEnable};
|
|
|
+ IF EnableTraceCmd THEN
|
|
|
+ Log.String("[SD] 48 Bit Response"); Log.Ln;
|
|
|
+ Log.String("[SD] Enabling CRC Check and Index Check"); Log.Ln
|
|
|
+ END
|
|
|
+ |ResponseR3, ResponseR4:
|
|
|
+ reg := Command_ResponseType_48b;
|
|
|
+ IF EnableTraceCmd THEN Log.String("[SD] 48 Bit Response"); Log.Ln END
|
|
|
+ |ResponseR1b, ResponseR5b:
|
|
|
+ reg := Command_ResponseType_48bBusy;
|
|
|
+ flags := {Command_CrcCheckEnable, Command_IndexCheckEnable};
|
|
|
+ IF EnableTraceCmd THEN
|
|
|
+ Log.String("[SD] 48 Bit Response"); Log.Ln;
|
|
|
+ Log.String("[SD] Enabling Index Check"); Log.Ln
|
|
|
+ END
|
|
|
+ |ResponseR2:
|
|
|
+ reg := Command_ResponseType_136b;
|
|
|
+ flags := {Command_CrcCheckEnable};
|
|
|
+ IF EnableTraceCmd THEN
|
|
|
+ Log.String("[SD] 136 Bit Response"); Log.Ln;
|
|
|
+ Log.String("[SD] Enabling Command CRC Check"); Log.Ln
|
|
|
+ END
|
|
|
+ END;
|
|
|
+
|
|
|
+ (* Command determines data-enable *)
|
|
|
+ IF busy THEN
|
|
|
+ INCL(flags, Command_DataPresent);
|
|
|
+ IF EnableTraceCmd THEN Log.String("[SD] Using DAT Line"); Log.Ln END;
|
|
|
+
|
|
|
+ txFlags := {};
|
|
|
+ IF (*(cmd = CMD_READ_SINGLE_BLOCK) OR (cmd = CMD_READ_MULTIPLE_BLOCK) OR (cmd = ACMD_SEND_SCR)*) read THEN
|
|
|
+ IF EnableTraceCmd THEN Log.String("[SD] Data Read"); Log.Ln END;
|
|
|
+ INCL(txFlags, TransferMode_DataTxDirection)
|
|
|
+ ELSIF EnableTraceCmd THEN
|
|
|
+ Log.String("[SD] Data Write");
|
|
|
+ Log.Ln
|
|
|
+ END;
|
|
|
+ IF (cmd = CMD_READ_MULTIPLE_BLOCK) OR (cmd = CMD_WRITE_MULTIPLE_BLOCK) THEN
|
|
|
+ IF EnableTraceCmd THEN Log.String("[SD] Multiple blocks: using Auto CMD12 & activating block count"); Log.Ln END;
|
|
|
+ txFlags := txFlags + TransferMode_AutoCmd_Cmd12 + {TransferMode_MultipleBlocks, TransferMode_BlockCountEnable}
|
|
|
+ ELSIF EnableTraceCmd THEN
|
|
|
+ Log.String("[SD] Single Block");
|
|
|
+ Log.Ln
|
|
|
+ END;
|
|
|
+ IF dma THEN INCL(txFlags, TransferMode_DmaEnable) END;
|
|
|
+ hc.regs.TransferMode := SYSTEM.VAL(INTEGER, txFlags)
|
|
|
+ END;
|
|
|
+ hc.regs.Command := SYSTEM.VAL(INTEGER, SYSTEM.VAL(SET, reg + LSH(cmd, Command_CommandIndexOffset)) + flags);
|
|
|
+
|
|
|
+ (* 7 *)
|
|
|
+ RETURN WaitForCompletion(hc, ~abort, status, result)
|
|
|
+ (* Steps 7, 8 and 9 of WaitForCompletion have to be done by caller *)
|
|
|
+ END StartCommand;
|
|
|
+
|
|
|
+ (**
|
|
|
+ Perform error recovery as specified by the triggered error interrupts.
|
|
|
+ Returns an error code (result) and the interrupt status register before recovery (status)
|
|
|
+ *)
|
|
|
+ PROCEDURE ErrorRecovery (hc: HostController; VAR result: LONGINT; VAR status: SET): BOOLEAN;
|
|
|
+ BEGIN
|
|
|
+ (* 1 is done *)
|
|
|
+ status := hc.regs.InterruptStatus;
|
|
|
+ hc.regs.InterruptStatusEnable := {0 .. 14};
|
|
|
+ EXCL(hc.regs.InterruptStatusEnable, Interrupt_Normal_ErrorInterrupt);
|
|
|
+ EXCL(hc.regs.InterruptStatusEnable, Interrupt_Normal_ErrorInterrupt);
|
|
|
+ EXCL(hc.regs.InterruptStatusEnable, Interrupt_Normal_ErrorInterrupt);
|
|
|
+
|
|
|
+ (* 2 *)
|
|
|
+ IF (Interrupt_Error_CommandTimeout IN status) OR (Interrupt_Error_CommandCrc IN status) OR (Interrupt_Error_CommandEndBit IN status)
|
|
|
+ OR (Interrupt_Error_CommandIndex IN status) THEN
|
|
|
+ (* 3 & 4 *)
|
|
|
+ IF ~Reset(hc, TRUE, FALSE) THEN
|
|
|
+ result := ErrorUnrecoverable;
|
|
|
+ RETURN FALSE
|
|
|
+ END
|
|
|
+ END;
|
|
|
+
|
|
|
+ (* 5 *)
|
|
|
+ IF Interrupt_Error_DataTimeout IN status THEN
|
|
|
+ Log.String("[SD] Got data timeout error"); Log.Ln
|
|
|
+ END;
|
|
|
+ IF (Interrupt_Error_DataTimeout IN status) OR (Interrupt_Error_DataCrc IN status) OR (Interrupt_Error_DataEndBit IN status) THEN
|
|
|
+ (* 6 & 7 *)
|
|
|
+ IF ~Reset(hc, FALSE, TRUE) THEN
|
|
|
+ result := ErrorUnrecoverable;
|
|
|
+ RETURN FALSE
|
|
|
+ END
|
|
|
+ END;
|
|
|
+
|
|
|
+ (* 8 & 9 *)
|
|
|
+ hc.regs.InterruptStatus := status;
|
|
|
+
|
|
|
+ (* 10 & 11 *)
|
|
|
+ (*IF ~Command(hc, CMD_STOP_TRANSMISSION, 0, ResponseR1b, FALSE, TRUE, result) THEN
|
|
|
+ (* 12 *)
|
|
|
+ IF (Interrupt_Error_CommandTimeout IN status) OR (Interrupt_Error_CommandCrc IN status) OR (Interrupt_Error_CommandEndBit IN status)
|
|
|
+ OR (Interrupt_Error_CommandIndex IN status) THEN
|
|
|
+ TRACE('CMD12 CMD LINE ERROR');
|
|
|
+ result := ErrorUnrecoverable;
|
|
|
+ RETURN FALSE
|
|
|
+ END;
|
|
|
+ (* 13 *)
|
|
|
+ IF ~WaitForTransfer(hc, result) THEN
|
|
|
+ result := ErrorUnrecoverable;
|
|
|
+ RETURN FALSE
|
|
|
+ END;
|
|
|
+ TRACE(hc.regs.BufferData);
|
|
|
+ END;
|
|
|
+ (* 15 *)
|
|
|
+ IF hc.regs.PresentState * PresentState_DatLineSignalLevelMask # PresentState_DatLineSignalLevelMask THEN
|
|
|
+ TRACE('CMD12 DAT LINE ERROR');
|
|
|
+ result := ErrorUnrecoverable;
|
|
|
+ RETURN FALSE
|
|
|
+ END;*)
|
|
|
+ hc.regs.InterruptStatusEnable := {0 .. 31};
|
|
|
+ INCL(hc.regs.InterruptStatusEnable, Interrupt_Normal_ErrorInterrupt);
|
|
|
+ result := ErrorNone;
|
|
|
+ RETURN TRUE
|
|
|
+ END ErrorRecovery;
|
|
|
+
|
|
|
+ (** Wait for completion of an SD command [Simplified specs. 3.7.1.2 pp. 109-110 *)
|
|
|
+ PROCEDURE WaitForCompletion (hc: HostController; tryRecover: BOOLEAN; VAR status: SET; VAR result: LONGINT): BOOLEAN;
|
|
|
+ BEGIN
|
|
|
+ result := ErrorNone;
|
|
|
+
|
|
|
+ (* 1 *)
|
|
|
+ IF ~hc.block(hc, {Interrupt_Normal_CommandComplete, Interrupt_Normal_ErrorInterrupt}, DefaultTimeout) THEN
|
|
|
+ Log.String("[SD] Timeout while waiting for completion");
|
|
|
+ Log.Ln;
|
|
|
+ PrintHcRegisters(hc);
|
|
|
+ RETURN FALSE
|
|
|
+ END;
|
|
|
+ status := hc.regs.InterruptStatus;
|
|
|
+
|
|
|
+ IF Interrupt_Normal_ErrorInterrupt IN status THEN
|
|
|
+ IF ~tryRecover THEN
|
|
|
+ hc.regs.InterruptStatus := status;
|
|
|
+ result := ErrorUnrecoverable;
|
|
|
+ RETURN Reset(hc, TRUE, FALSE) & Reset(hc, FALSE, TRUE)
|
|
|
+ END;
|
|
|
+ IF ~ErrorRecovery(hc, result, status) THEN RETURN FALSE END;
|
|
|
+ IF Interrupt_Error_CommandTimeout IN status THEN
|
|
|
+ IF EnableTraceCmd THEN Log.String("[SD] Timeout in command"); Log.Ln END;
|
|
|
+ result := ErrorCmdTimeout;
|
|
|
+ IF ~Reset(hc, TRUE, FALSE) THEN END;
|
|
|
+ ELSIF Interrupt_Error_CommandCrc IN status THEN
|
|
|
+ result := ErrorCmdCrc;
|
|
|
+ ELSE
|
|
|
+ result := ErrorCard;
|
|
|
+ END;
|
|
|
+ RETURN FALSE
|
|
|
+ END;
|
|
|
+
|
|
|
+ (* 2 *)
|
|
|
+ hc.regs.InterruptStatus := {Interrupt_Normal_CommandComplete};
|
|
|
+
|
|
|
+ IF EnableTraceCmd THEN Log.String("[SD] Command successful"); Log.Ln END;
|
|
|
+ RETURN TRUE
|
|
|
+ END WaitForCompletion;
|
|
|
+
|
|
|
+ (** Wait for transfer complete interrupt *)
|
|
|
+ PROCEDURE WaitForTransfer (hc: HostController; VAR result: LONGINT): BOOLEAN;
|
|
|
+ VAR
|
|
|
+ status: SET;
|
|
|
+ BEGIN
|
|
|
+ (* 5 *)
|
|
|
+ IF ~hc.block(hc, {Interrupt_Normal_TransferComplete, Interrupt_Normal_ErrorInterrupt}, DefaultTimeout) THEN
|
|
|
+ Log.String("[SD] Timeout failed in WaitForTransfer");
|
|
|
+ RETURN FALSE;
|
|
|
+ END;
|
|
|
+ (*REPEAT UNTIL (Interrupt_Normal_TransferComplete IN hc.regs.InterruptStatus) OR (Interrupt_Normal_ErrorInterrupt IN hc.regs.InterruptStatus);*)
|
|
|
+ status := hc.regs.InterruptStatus;
|
|
|
+
|
|
|
+ IF Interrupt_Normal_ErrorInterrupt IN hc.regs.InterruptStatus THEN
|
|
|
+ IF ~ErrorRecovery(hc, result, status) THEN RETURN FALSE END;
|
|
|
+ result := ErrorCard;
|
|
|
+ RETURN FALSE
|
|
|
+ END;
|
|
|
+
|
|
|
+ (* 6 *)
|
|
|
+ (*INCL(hc.regs.InterruptStatus, Interrupt_Normal_TransferComplete)*)
|
|
|
+ hc.regs.InterruptStatus := {Interrupt_Normal_TransferComplete}
|
|
|
+ END WaitForTransfer;
|
|
|
+
|
|
|
+ PROCEDURE SpinBlock (hc: HostController; mask: SET; timeout: LONGINT): BOOLEAN;
|
|
|
+ VAR
|
|
|
+ deadline: SdEnvironment.Time;
|
|
|
+ BEGIN
|
|
|
+ deadline := SdEnvironment.GetTimeCounter() + SdEnvironment.FromMilli(timeout);
|
|
|
+ REPEAT UNTIL (mask * hc.regs.InterruptStatus # {}) OR (SdEnvironment.GetTimeCounter() > deadline);
|
|
|
+ RETURN mask * hc.regs.InterruptStatus # {}
|
|
|
+ END SpinBlock;
|
|
|
+
|
|
|
+ (** Sends CMD7 if necessary to select the given card *)
|
|
|
+ PROCEDURE SelectCard * (hc: HostController; card: LONGINT; VAR result: LONGINT): BOOLEAN;
|
|
|
+ VAR
|
|
|
+ command: Command;
|
|
|
+ status: SET;
|
|
|
+ BEGIN
|
|
|
+ result := ErrorNone;
|
|
|
+ IF hc.lastRca = card THEN
|
|
|
+ IF EnableTrace THEN
|
|
|
+ Log.String("[SD] Card Already Selected");
|
|
|
+ Log.Ln
|
|
|
+ END;
|
|
|
+ RETURN TRUE
|
|
|
+ END;
|
|
|
+
|
|
|
+ IF EnableTrace THEN Log.String("[SD] Selecting Card "); Log.Int(card, 0); Log.Ln END;
|
|
|
+ command.hc := hc;
|
|
|
+ command.command := CMD_SELECT_DESELECT_CARD;
|
|
|
+ command.argument := LSH(card, 16);
|
|
|
+ command.responseType := ResponseR1b;
|
|
|
+ IF ~hc.execute(command, result) THEN RETURN FALSE END;
|
|
|
+ status := GetR1(command);
|
|
|
+ IF CardStatus_Error IN status THEN result := ErrorCard; RETURN FALSE END;
|
|
|
+ RETURN TRUE
|
|
|
+ END SelectCard;
|
|
|
+
|
|
|
+ (** Deselects all cards *)
|
|
|
+ PROCEDURE DeselectCards * (hc: HostController; VAR result: LONGINT): BOOLEAN;
|
|
|
+ VAR
|
|
|
+ command: Command;
|
|
|
+ ignoreRes: LONGINT;
|
|
|
+ ignoreBool: BOOLEAN;
|
|
|
+ BEGIN
|
|
|
+ result := ErrorNone;
|
|
|
+ IF hc.lastRca = 0 THEN
|
|
|
+ IF EnableTrace THEN Log.String("[SD] No card selected"); Log.Ln END;
|
|
|
+ RETURN TRUE
|
|
|
+ END;
|
|
|
+
|
|
|
+ IF EnableTrace THEN Log.String("[SD] Deselecting cards"); Log.Ln END;
|
|
|
+ command.hc := hc;
|
|
|
+ command.command := CMD_SELECT_DESELECT_CARD;
|
|
|
+ command.responseType := ResponseR1b;
|
|
|
+ ignoreBool := hc.execute(command, ignoreRes);
|
|
|
+ hc.lastRca := 0;
|
|
|
+ RETURN TRUE
|
|
|
+ END DeselectCards;
|
|
|
+
|
|
|
+ (** Read response R1 or R1b from command record *)
|
|
|
+ PROCEDURE GetR1 * (CONST command: Command): SET;
|
|
|
+ VAR
|
|
|
+ idx: LONGINT;
|
|
|
+ BEGIN
|
|
|
+ idx := 0;
|
|
|
+ (*IF (FlagAutoCmd12 IN command.flags) OR (FlagAutoCmd23 IN command.flags) THEN
|
|
|
+ idx := 3
|
|
|
+ END;*)
|
|
|
+ RETURN SYSTEM.VAL(SET, command.response[idx])
|
|
|
+ END GetR1;
|
|
|
+
|
|
|
+ (** Read response R2 from command record *)
|
|
|
+ PROCEDURE GetR2 * (CONST command: Command; VAR response: ARRAY OF LONGINT);
|
|
|
+ VAR
|
|
|
+ i: LONGINT;
|
|
|
+ BEGIN
|
|
|
+ FOR i := 0 TO 3 DO
|
|
|
+ response[i] := command.response[i]
|
|
|
+ END
|
|
|
+ END GetR2;
|
|
|
+
|
|
|
+ (** Read response R3 from command record *)
|
|
|
+ PROCEDURE GetR3 * (CONST command: Command): LONGINT;
|
|
|
+ BEGIN
|
|
|
+ RETURN command.response[0]
|
|
|
+ END GetR3;
|
|
|
+
|
|
|
+ (** Read response R4 from command record *)
|
|
|
+ PROCEDURE GetR4 * (CONST command: Command): LONGINT;
|
|
|
+ BEGIN
|
|
|
+ RETURN command.response[0]
|
|
|
+ END GetR4;
|
|
|
+
|
|
|
+ (** Read response R5 from command record *)
|
|
|
+ PROCEDURE GetR5 * (CONST command: Command): LONGINT;
|
|
|
+ BEGIN
|
|
|
+ RETURN command.response[0]
|
|
|
+ END GetR5;
|
|
|
+
|
|
|
+ (** Read response R6 from command record *)
|
|
|
+ PROCEDURE GetR6 * (CONST command: Command): LONGINT;
|
|
|
+ BEGIN
|
|
|
+ RETURN command.response[0]
|
|
|
+ END GetR6;
|
|
|
+
|
|
|
+ (** Read response R7 from command record *)
|
|
|
+ PROCEDURE GetR7 * (CONST command: Command): LONGINT;
|
|
|
+ BEGIN
|
|
|
+ RETURN command.response[0]
|
|
|
+ END GetR7;
|
|
|
+
|
|
|
+ (** Reset command and/or data lines of a host controller *)
|
|
|
+ PROCEDURE Reset (hc: HostController; cmd, dat: BOOLEAN): BOOLEAN;
|
|
|
+ VAR
|
|
|
+ val: SHORTINT;
|
|
|
+ BEGIN
|
|
|
+ IF cmd & dat THEN
|
|
|
+ val := SoftwareResetAll
|
|
|
+ ELSIF cmd THEN
|
|
|
+ val := SoftwareResetCmd
|
|
|
+ ELSIF dat THEN
|
|
|
+ val := SoftwareResetDat
|
|
|
+ ELSE
|
|
|
+ RETURN FALSE
|
|
|
+ END;
|
|
|
+ hc.regs.SoftwareReset := val;
|
|
|
+ REPEAT
|
|
|
+ UNTIL hc.regs.SoftwareReset # val;
|
|
|
+ RETURN TRUE
|
|
|
+ END Reset;
|
|
|
+
|
|
|
+ (** Set host controller bust clock *)
|
|
|
+ PROCEDURE SetBusClock (hc: HostController; freq: LONGINT);
|
|
|
+ VAR
|
|
|
+ val: LONGINT;
|
|
|
+ BEGIN
|
|
|
+ hc.regs.ClockControl := 0;
|
|
|
+
|
|
|
+ IF freq < hc.baseFrequency THEN
|
|
|
+
|
|
|
+ val := 1;
|
|
|
+ WHILE (val < 8) & (LSH(hc.baseFrequency,-val) > freq) DO
|
|
|
+ INC(val);
|
|
|
+ END;
|
|
|
+
|
|
|
+ IF EnableTrace THEN
|
|
|
+ Log.String(" [SD] baseFreq=");
|
|
|
+ Log.Int(hc.baseFrequency, 0);
|
|
|
+ Log.String(", val=");
|
|
|
+ Log.Int(val, 0);
|
|
|
+ Log.Ln
|
|
|
+ END;
|
|
|
+
|
|
|
+ hc.regs.ClockControl := INTEGER(LSH(LSH(LONGINT(1), val-1), 8) + SYSTEM.VAL(INTEGER, {ClockControl_InternalClockEnable}));
|
|
|
+ hc.frequency := LSH(hc.baseFrequency, -val);
|
|
|
+ ELSE
|
|
|
+ hc.regs.ClockControl := SYSTEM.VAL(INTEGER, {ClockControl_InternalClockEnable});
|
|
|
+ hc.frequency := hc.baseFrequency;
|
|
|
+ END;
|
|
|
+
|
|
|
+ REPEAT val := hc.regs.ClockControl UNTIL ClockControl_InternalClockState IN SYSTEM.VAL(SET, val);
|
|
|
+ val := hc.regs.ClockControl;
|
|
|
+ hc.regs.ClockControl := SYSTEM.VAL(INTEGER, SYSTEM.VAL(SET, val) + {ClockControl_SdClockEnable});
|
|
|
+ IF EnableTrace THEN
|
|
|
+ Log.String("[SD] Selecting Bus Clock Frequency: ");
|
|
|
+ Log.Int(hc.frequency, 0); Log.String(" Hz");
|
|
|
+ Log.Ln
|
|
|
+ END;
|
|
|
+ END SetBusClock;
|
|
|
+
|
|
|
+ (** Set host controller data timeout, in ms *)
|
|
|
+ PROCEDURE SetTimeout (hc: HostController; timeout: LONGINT);
|
|
|
+ VAR
|
|
|
+ ratio, val: LONGINT;
|
|
|
+ BEGIN
|
|
|
+ EXCL(hc.regs.InterruptStatusEnable, Interrupt_Error_DataTimeout);
|
|
|
+ ratio := LSH(LONGINT(hc.timeoutFrequency * timeout DIV 1000), -13);
|
|
|
+ val := 0;
|
|
|
+ WHILE (val < 15) & (LSH(LONGINT(1), val) < ratio) DO INC(val) END;
|
|
|
+ ASSERT(LSH(LONGINT(1), val) >= ratio);
|
|
|
+ hc.regs.TimeoutControl := SYSTEM.VAL(SHORTINT, val);
|
|
|
+ INCL(hc.regs.InterruptStatusEnable, Interrupt_Error_DataTimeout)
|
|
|
+ END SetTimeout;
|
|
|
+
|
|
|
+ (** Set the data bus width for a given RCA. *)
|
|
|
+ PROCEDURE SetBusWidth (hc: HostController; card: Card; width: LONGINT; VAR result: LONGINT): BOOLEAN;
|
|
|
+ VAR
|
|
|
+ command: Command;
|
|
|
+ val: LONGINT;
|
|
|
+ BEGIN
|
|
|
+ IF ~SelectCard(card.hc, card.rca, result) THEN RETURN FALSE END;
|
|
|
+
|
|
|
+ (*command.hc := card.hc;
|
|
|
+ command.command := ACMD_SET_CLR_CARD_DETECT;
|
|
|
+ command.argument := 0;
|
|
|
+ command.responseType := ResponseR1;
|
|
|
+ command.flags := {FlagApplicationCmd};
|
|
|
+ command.rca := card.rca;
|
|
|
+ IF ~ExecuteCommand(command, result) THEN RETURN FALSE END;*)
|
|
|
+
|
|
|
+ (* 1 *)
|
|
|
+ EXCL(hc.regs.InterruptStatusEnable, Interrupt_Normal_CardInterrupt);
|
|
|
+
|
|
|
+ (* 2: driver supports SD cards only *)
|
|
|
+ (* 3: not implemented *)
|
|
|
+
|
|
|
+ (* 4 *)
|
|
|
+ command.command := ACMD_SET_BUS_WIDTH;
|
|
|
+ CASE width OF
|
|
|
+ 1: command.argument := 0
|
|
|
+ |4: command.argument := 2
|
|
|
+ END;
|
|
|
+ command.hc := hc;
|
|
|
+ command.rca := card.rca;
|
|
|
+ command.flags := {FlagApplicationCmd};
|
|
|
+ command.responseType := ResponseR1;
|
|
|
+ IF ~ExecuteCommand(command, result) THEN RETURN FALSE END;
|
|
|
+
|
|
|
+ (* 5 *)
|
|
|
+ val := hc.regs.HostControl1;
|
|
|
+ CASE width OF
|
|
|
+ 1: EXCL(SYSTEM.VAL(SET, val), HostControl1_DataTransferWidth)
|
|
|
+ |4: INCL(SYSTEM.VAL(SET, val), HostControl1_DataTransferWidth)
|
|
|
+ END;
|
|
|
+ hc.regs.HostControl1 := SHORTINT(val);
|
|
|
+
|
|
|
+ (* 6: SD card only *)
|
|
|
+ (* 8 *)
|
|
|
+ INCL(hc.regs.InterruptStatusEnable, Interrupt_Normal_CardInterrupt);
|
|
|
+ RETURN TRUE
|
|
|
+ END SetBusWidth;
|
|
|
+
|
|
|
+ (** Executes a switch function command *)
|
|
|
+ PROCEDURE SwitchFunc (card: Card; switch: BOOLEAN; funcs: ARRAY 6 OF LONGINT; VAR sts: SwitchFuncStatus; VAR res: LONGINT): BOOLEAN;
|
|
|
+ VAR
|
|
|
+ status: ARRAY 64 OF CHAR;
|
|
|
+ command: Command;
|
|
|
+
|
|
|
+ PROCEDURE GetStatus (CONST status: ARRAY OF CHAR; VAR sts: SwitchFuncStatus);
|
|
|
+ VAR
|
|
|
+ i: LONGINT;
|
|
|
+ BEGIN
|
|
|
+ sts.current := LONGINT(ORD(status[0])) * 100H + LONGINT(ORD(status[1]));
|
|
|
+ FOR i := 0 TO 5 DO
|
|
|
+ sts.functionGroups[i] := SYSTEM.VAL(SET, LONGINT(ORD(status[2 + 2 * (5 - i)])) * 100H + LONGINT(ORD(status[1 + 2 + 2 * (5 - i)])))
|
|
|
+ END;
|
|
|
+ FOR i := 0 TO 5 BY 2 DO
|
|
|
+ sts.functionStatus[i] := ORD(status[14 + 2 - i DIV 2]) MOD 10H;
|
|
|
+ sts.functionStatus[i + 1] := ORD(status[14 + 2 - i DIV 2]) DIV 10H
|
|
|
+ END;
|
|
|
+ END GetStatus;
|
|
|
+
|
|
|
+ BEGIN
|
|
|
+ IF ~SelectCard(card.hc, card.rca, res) THEN RETURN FALSE END;
|
|
|
+ command.hc := card.hc;
|
|
|
+ command.command := CMD_SWITCH_FUNC;
|
|
|
+ command.responseType := ResponseR1;
|
|
|
+ command.argument := (funcs[0] MOD 10H) + 10H * (funcs[1] MOD 10H) + 100H * (funcs[2] MOD 10H) + 1000H * (funcs[3] MOD 10H) + 10000H * (funcs[4] MOD 10H) + 100000H * (funcs[5] MOD 10H);
|
|
|
+ IF switch THEN INCL(SYSTEM.VAL(SET, command.argument), 31) END;
|
|
|
+ command.flags := {FlagRead, FlagData};
|
|
|
+ command.dataTimeout := card.readTimeout;
|
|
|
+ IF ~card.hc.transfer(command, status, 0, 64, res) THEN RETURN FALSE END;
|
|
|
+ GetStatus(status, sts);
|
|
|
+ RETURN TRUE
|
|
|
+ END SwitchFunc;
|
|
|
+
|
|
|
+ (** Outputs the switch function status on the log *)
|
|
|
+ PROCEDURE PrintSwitchFuncStatus(CONST status: SwitchFuncStatus);
|
|
|
+ VAR
|
|
|
+ i: LONGINT;
|
|
|
+ BEGIN
|
|
|
+ Log.String("Switch Func Status");
|
|
|
+ Log.Ln;
|
|
|
+ Log.String("Current = ");
|
|
|
+ IF status.current = 0 THEN
|
|
|
+ Log.String("ERROR")
|
|
|
+ ELSE
|
|
|
+ Log.Int(status.current, 0);
|
|
|
+ Log.String(" mA");
|
|
|
+ Log.Ln
|
|
|
+ END;
|
|
|
+ FOR i := 0 TO 5 DO
|
|
|
+ Log.String("Function Group #");
|
|
|
+ Log.Int(i, 0);
|
|
|
+ Log.String(": functions = ");
|
|
|
+ Log.Set(status.functionGroups[i]);
|
|
|
+ Log.String(", status = ");
|
|
|
+ CASE status.functionStatus[i] OF
|
|
|
+ 0: Log.String("switchable")
|
|
|
+ |1: Log.String("switched")
|
|
|
+ |0FH: Log.String("ERROR")
|
|
|
+ END;
|
|
|
+ Log.Ln
|
|
|
+ END
|
|
|
+ END PrintSwitchFuncStatus;
|
|
|
+
|
|
|
+ (** Interrupt handler *)
|
|
|
+ PROCEDURE HandleInterrupt * (hc: HostController);
|
|
|
+ VAR
|
|
|
+ card: Card;
|
|
|
+ result: LONGINT;
|
|
|
+ interruptStatus: SET;
|
|
|
+ BEGIN
|
|
|
+ interruptStatus := hc.regs.InterruptStatus;
|
|
|
+
|
|
|
+ (*IF {Interrupt_Normal_TransferComplete, Interrupt_Normal_ErrorInterrupt} * interruptStatus # {} THEN
|
|
|
+ BEGIN {EXCLUSIVE} continue := TRUE END;
|
|
|
+ RETURN
|
|
|
+ END;*)
|
|
|
+
|
|
|
+ (*!TODO: make interrupt handling as quick as possible - handle all events in SdControllers asynchronously! *)
|
|
|
+ IF Interrupt_Normal_CardInsertion IN interruptStatus THEN
|
|
|
+ hc.regs.InterruptStatus := {Interrupt_Normal_CardInsertion};
|
|
|
+ IF EnableTrace THEN Log.String("[SD] Card Insertion"); Log.Ln END;
|
|
|
+ NEW(card);
|
|
|
+ IF InitCard(hc, card, result) & (hc.handle # NIL) THEN
|
|
|
+ hc.handle(card, OnInitialization, hc.handlerParam)
|
|
|
+ ELSIF EnableTrace THEN
|
|
|
+ Log.String("[SD] Could not initialize card"); Log.Ln
|
|
|
+ END
|
|
|
+ ELSIF Interrupt_Normal_CardRemoval IN interruptStatus THEN
|
|
|
+ hc.regs.InterruptStatus := {Interrupt_Normal_CardRemoval};
|
|
|
+ IF EnableTrace THEN Log.String("[SD] Card Removal"); Log.Ln END;
|
|
|
+ card := hc.cards;
|
|
|
+ IF hc.handle # NIL THEN hc.handle(card, OnRemoval, hc.handlerParam) END;
|
|
|
+ hc.cards := hc.cards.next
|
|
|
+ END;
|
|
|
+ END HandleInterrupt;
|
|
|
+
|
|
|
+(* ==================== Card (High-Level) Interface ==================== *)
|
|
|
+TYPE
|
|
|
+ (** Card descriptor *)
|
|
|
+ Card * = POINTER TO CardDesc;
|
|
|
+ CardDesc * = RECORD
|
|
|
+ state -, (** Card state. Currently not used *)
|
|
|
+ rca -: LONGINT; (** Card RCA *)
|
|
|
+ cid -: Cid; (** CID *)
|
|
|
+ csd -: Csd; (** CSD *)
|
|
|
+ scr -: Scr; (** SCR *)
|
|
|
+ sdStatus -: SdStatus; (** Current SD status for this card *)
|
|
|
+ hc -: HostController; (** Host controller on which the card is attached *)
|
|
|
+ acquire *, release *: PROCEDURE {DELEGATE}; (** Used for synchronization *)
|
|
|
+ readTimeout, (** Read timeout computed for this card, in ms *)
|
|
|
+ writeTimeout, (** Write timeout computed for this card, in ms *)
|
|
|
+ eraseTimeout: LONGINT; (** Erase timeout computed for this card, in ms *)
|
|
|
+ next: Card; (** Internal linked list of cards attached to the same HC *)
|
|
|
+ END;
|
|
|
+
|
|
|
+ (** SD Status *)
|
|
|
+ SdStatus * = RECORD
|
|
|
+ dataBusWidth -: LONGINT; (** Number of DAT bus lines currently used by card *)
|
|
|
+ securedMode -: BOOLEAN; (** Is card in secured mode? *)
|
|
|
+ cardType -: LONGINT; (** Card type, one of Category* *)
|
|
|
+ sizeProtArea -: HUGEINT; (** Size of the protected area, in bytes *)
|
|
|
+ perfMove -: LONGINT; (** Performance of move, in MB/s. 0 means 'sequential write', 255 means 'infinity' *)
|
|
|
+ eraseSize -: LONGINT; (** Numbers of AUs erased at a time. 0 means not supported *)
|
|
|
+ eraseTimeout -: LONGINT; (** Erase timeout value in second, per erase unit. 0 means not supported *)
|
|
|
+ eraseOffset -: LONGINT; (** Fixed timeout for erase timeout, in second *)
|
|
|
+ speedClass -: LONGINT; (** 3.x V speed class: 0 is 'unknown', 2, 4, 6 or 10 *)
|
|
|
+ auSize -: LONGINT; (** AU size for 3.x V *)
|
|
|
+ uhsSpeedGrade -: LONGINT; (** UHS speed grade: 0 is 'not supported', 1 or 3 *)
|
|
|
+ uhsAuSize -: LONGINT; (** AU size in UHS mode *)
|
|
|
+ vscSpeedClass -: LONGINT; (** Video mode speed class: 0 is 'not supported', 6, 10, 30, 60 or 90 *)
|
|
|
+ vscAuSize -: LONGINT; (** AU size in video mode *)
|
|
|
+ suspensionAdr -: LONGINT; (** Current suspension address, in blocks. See CMD20 *)
|
|
|
+ appPerfClass -: LONGINT; (** Application performance class: 0 is 'not supported', 1 or 2 *)
|
|
|
+ perfEnhance -: SET; (** Performance enhancing features of the card. Contains Performance* *)
|
|
|
+ commandQueueDepth -: LONGINT; (** Command queue depth. Valid only if PerformanceCommandQueue IN perfEnhance *)
|
|
|
+ discardSupport -: BOOLEAN; (** Card supports discard? *)
|
|
|
+ fuleSupport -: BOOLEAN; (** card supports FULE? *)
|
|
|
+ END;
|
|
|
+
|
|
|
+ (** Card Identification register *)
|
|
|
+ Cid * = RECORD
|
|
|
+ manufacturingDate -: RECORD (** Manufacturing date, with year and month *)
|
|
|
+ year -,
|
|
|
+ month -: LONGINT
|
|
|
+ END;
|
|
|
+ productSerialNumber -: ADDRESS; (** Serial number *)
|
|
|
+ productRevision -: RECORD (** Revision n.m *)
|
|
|
+ n-, m-: LONGINT
|
|
|
+ END;
|
|
|
+ productName -: ARRAY 6 OF CHAR; (** Product name *)
|
|
|
+ oemId -: ARRAY 3 OF CHAR; (** OEM/Application ID *)
|
|
|
+ manufacturerId -: LONGINT; (** Manufacturer identifier *)
|
|
|
+ END;
|
|
|
+
|
|
|
+ (* Card Registers *)
|
|
|
+ Csd * = RECORD
|
|
|
+ (* Card properties *)
|
|
|
+ capacity -: HUGEINT; (** Card capacity in bytes *)
|
|
|
+ commandClasses -: SET; (** Command classes supported by the card *)
|
|
|
+
|
|
|
+ (* Timing info *)
|
|
|
+ r2w -: LONGINT; (** Read to write time factor = read time / write time *)
|
|
|
+ taac -: REAL; (** Asynchronous access time, in s *)
|
|
|
+ nsac -: LONGINT; (** Worst-case clock dependent access time, in clock cycles *)
|
|
|
+ txSpeed -: LONGINT; (** Max transfer speed in bit/s *)
|
|
|
+
|
|
|
+ (* Block read properties *)
|
|
|
+ partialRead -,
|
|
|
+ misalignedRead -: BOOLEAN;
|
|
|
+ readBlockSize -: LONGINT;
|
|
|
+
|
|
|
+ (* Block write properties *)
|
|
|
+ partialWrite -,
|
|
|
+ misalignedWrite -: BOOLEAN;
|
|
|
+ writeBlockSize -: LONGINT;
|
|
|
+ END;
|
|
|
+
|
|
|
+ (** SCR register *)
|
|
|
+ Scr * = RECORD
|
|
|
+ version -, (** Card physical spec. version: one of Version* *)
|
|
|
+ security -: LONGINT; (** Card security type: one of Type* *)
|
|
|
+ busWidth -: SET; (** Bus widths supported by the card *)
|
|
|
+ commandSupport -: SET; (** Supported commands *)
|
|
|
+ END;
|
|
|
+
|
|
|
+ (**
|
|
|
+ Initializes a new card on the host controller.
|
|
|
+ Executes all commands until either an error occurs or the card is ready for data transfers.
|
|
|
+ *)
|
|
|
+ PROCEDURE InitCard * (hc: HostController; card: Card; VAR result: LONGINT): BOOLEAN;
|
|
|
+ VAR
|
|
|
+ response: LONGINT;
|
|
|
+ status: SET;
|
|
|
+ f8, sdio: BOOLEAN;
|
|
|
+ command: Command;
|
|
|
+ BEGIN
|
|
|
+ SetBusClock(hc, InitialClockFrequency);
|
|
|
+
|
|
|
+ FOR response := 0 TO 10000000 DO END;
|
|
|
+ command.hc := hc;
|
|
|
+
|
|
|
+ (* 1 *)
|
|
|
+ command.command := CMD_GO_IDLE_STATE;
|
|
|
+ command.argument := 0;
|
|
|
+ command.responseType := ResponseNone;
|
|
|
+ command.flags := {};
|
|
|
+ IF ~ExecuteCommand(command, result) THEN RETURN FALSE END;
|
|
|
+ (* 2 *)
|
|
|
+ command.command := CMD_SEND_IF_COND;
|
|
|
+ command.argument := 1AAH;
|
|
|
+ command.responseType := ResponseR7;
|
|
|
+ command.flags := {};
|
|
|
+ IF ~ExecuteCommand(command, result) THEN RETURN FALSE END;
|
|
|
+ (* 3 *)
|
|
|
+ response := GetR7(command);
|
|
|
+ IF response # 1AAH THEN
|
|
|
+ result := ErrorCard;
|
|
|
+ RETURN FALSE
|
|
|
+ END;
|
|
|
+ f8 := TRUE;
|
|
|
+
|
|
|
+ (* 5 *)
|
|
|
+ command.command := CMD_IO_SEND_OP_COND;
|
|
|
+ command.argument := 0;
|
|
|
+ command.responseType := ResponseR4;
|
|
|
+ command.flags := {};
|
|
|
+ IF ~ExecuteCommand(command, result) & (result = ErrorCmdTimeout) THEN
|
|
|
+ sdio := FALSE;
|
|
|
+ result := ErrorNone;
|
|
|
+ IF ~Reset(hc ,TRUE, FALSE) THEN RETURN FALSE END;
|
|
|
+ ELSIF result # ErrorNone THEN
|
|
|
+ RETURN FALSE
|
|
|
+ ELSE
|
|
|
+ sdio := TRUE;
|
|
|
+ END;
|
|
|
+ IF EnableTrace THEN Log.String("[SD] Card is SDIO: "); Log.Boolean(sdio); Log.Ln END;
|
|
|
+
|
|
|
+ (* 6 *)
|
|
|
+ IF sdio THEN
|
|
|
+ (*! NOT IMPLEMENTED YET *)
|
|
|
+ HALT(100);
|
|
|
+ (* 7 *)
|
|
|
+ (*IF ~StartCommand(hc, CMD_IO_SEND_OP_COND, 800H, ResponseR4, FALSE, FALSE, res) THEN
|
|
|
+ RETURN FALSE
|
|
|
+ END;
|
|
|
+ RETURN TRUE*)
|
|
|
+ END;
|
|
|
+
|
|
|
+ (* A *)
|
|
|
+ (* 12 & 19 *)
|
|
|
+ command.command := ACMD_SD_SEND_OP_COND;
|
|
|
+ command.argument := 0;
|
|
|
+ command.responseType := ResponseR3;
|
|
|
+ command.flags := {FlagApplicationCmd, FlagIgnoreIllegalCmd};
|
|
|
+ IF ~ExecuteCommand(command, result) THEN RETURN FALSE END;
|
|
|
+ response := GetR3(command);
|
|
|
+ IF EnableTrace THEN
|
|
|
+ Log.String("[SD] VDD: Ranges Supported by Card:"); Log.Ln;
|
|
|
+ IF CardOcr_Vdd27_28 IN SYSTEM.VAL(SET, response) THEN Log.String("[SD] 2.7 - 2.8 V"); Log.Ln END;
|
|
|
+ IF CardOcr_Vdd28_29 IN SYSTEM.VAL(SET, response) THEN Log.String("[SD] 2.8 - 2.9 V"); Log.Ln END;
|
|
|
+ IF CardOcr_Vdd29_30 IN SYSTEM.VAL(SET, response) THEN Log.String("[SD] 2.9 - 3.0 V"); Log.Ln END;
|
|
|
+ IF CardOcr_Vdd30_31 IN SYSTEM.VAL(SET, response) THEN Log.String("[SD] 3.0 - 3.1 V"); Log.Ln END;
|
|
|
+ IF CardOcr_Vdd31_32 IN SYSTEM.VAL(SET, response) THEN Log.String("[SD] 3.1 - 3.2 V"); Log.Ln END;
|
|
|
+ IF CardOcr_Vdd32_33 IN SYSTEM.VAL(SET, response) THEN Log.String("[SD] 3.2 - 3.3 V"); Log.Ln END;
|
|
|
+ IF CardOcr_Vdd33_34 IN SYSTEM.VAL(SET, response) THEN Log.String("[SD] 3.3 - 3.4 V"); Log.Ln END;
|
|
|
+ IF CardOcr_Vdd34_35 IN SYSTEM.VAL(SET, response) THEN Log.String("[SD] 3.4 - 3.5 V"); Log.Ln END;
|
|
|
+ IF CardOcr_Vdd35_36 IN SYSTEM.VAL(SET, response) THEN Log.String("[SD] 3.5 - 3.6 V"); Log.Ln END
|
|
|
+ END;
|
|
|
+
|
|
|
+ status := {30};
|
|
|
+ IF Capabilities_Voltage30 IN hc.regs.Capabilities[0] THEN
|
|
|
+ IF EnableTrace THEN Log.String("[SD] Selecting 3.0 V"); Log.Ln END;
|
|
|
+ INCL(status, CardOcr_Vdd30_31)
|
|
|
+ ELSIF Capabilities_Voltage33 IN hc.regs.Capabilities[0] THEN
|
|
|
+ IF EnableTrace THEN Log.String("[SD] Selecting 3.3 V"); Log.Ln END;
|
|
|
+ INCL(status, CardOcr_Vdd32_33)
|
|
|
+ END;
|
|
|
+
|
|
|
+ command.command := ACMD_SD_SEND_OP_COND;
|
|
|
+ command.argument := SYSTEM.VAL(LONGINT, status);
|
|
|
+ command.responseType := ResponseR3;
|
|
|
+ command.flags := {FlagApplicationCmd, FlagIgnoreIllegalCmd};
|
|
|
+ REPEAT
|
|
|
+ IF ~ExecuteCommand(command, result) THEN RETURN FALSE END;
|
|
|
+ status := SYSTEM.VAL(SET, GetR3(command));
|
|
|
+ UNTIL (CardOcr_PowerUpStatus IN status);
|
|
|
+ IF EnableTrace & (CardOcr_S18A IN status) THEN
|
|
|
+ Log.String("[SD] Card supports 1.8V");
|
|
|
+ Log.Ln
|
|
|
+ END;
|
|
|
+ IF f8 & (CardOcr_CardCapacityStatus IN status) THEN
|
|
|
+ IF EnableTrace THEN Log.String("[SD] Card: SDHC or SDXC") END;
|
|
|
+ card.scr.security := TypeSDHC
|
|
|
+ ELSIF f8 THEN
|
|
|
+ IF EnableTrace THEN Log.String("[SD] Card: SDSC v2 or v3") END;
|
|
|
+ card.scr.security := TypeSDSC
|
|
|
+ ELSE
|
|
|
+ IF EnableTrace THEN Log.String("[SD] Card: SDSC v1.0 or v1.1") END;
|
|
|
+ card.scr.security := TypeSDSC
|
|
|
+ END;
|
|
|
+ IF EnableTrace THEN Log.Ln END;
|
|
|
+
|
|
|
+ (* 32 *)
|
|
|
+ command.command := CMD_ALL_SEND_CID;
|
|
|
+ command.argument := 0;
|
|
|
+ command.responseType := ResponseR2;
|
|
|
+ command.flags := {};
|
|
|
+ IF ~ExecuteCommand(command, result) THEN RETURN FALSE END;
|
|
|
+ DecodeCid(command.response, card.cid);
|
|
|
+ PrintCardCid(card.cid);
|
|
|
+
|
|
|
+ (* 33 *)
|
|
|
+ command.command := CMD_SEND_RELATIVE_ADDR;
|
|
|
+ command.argument := 0;
|
|
|
+ command.responseType := ResponseR6;
|
|
|
+ command.flags := {};
|
|
|
+ REPEAT
|
|
|
+ IF ~ExecuteCommand(command, result) THEN RETURN FALSE END;
|
|
|
+ card.rca := LSH(GetR6(command), -16)
|
|
|
+ UNTIL card.rca # 0;
|
|
|
+ IF EnableTrace THEN Log.String("[SD] New Card with RCA: "); Log.Hex(card.rca, -4); Log.Ln END;
|
|
|
+ status := SYSTEM.VAL(SET, GetR6(command));
|
|
|
+ status := status * {0 .. 15};
|
|
|
+ (* status is a modified CardStatus: reform corresponding card status *)
|
|
|
+ IF 15 IN status THEN EXCL(status, 15); INCL(status, 23) END;
|
|
|
+ IF 14 IN status THEN EXCL(status, 14); INCL(status, 22) END;
|
|
|
+ IF 13 IN status THEN EXCL(status, 13); INCL(status, 19) END;
|
|
|
+ IF EnableTrace THEN PrintCardStatus(status) END;
|
|
|
+ card.hc := hc;
|
|
|
+ card.next := hc.cards;
|
|
|
+ hc.cards := card;
|
|
|
+ IF Synchronize THEN SdEnvironment.GetLock(card.acquire, card.release) END;
|
|
|
+
|
|
|
+ (* Get Additional card registers: CSD *)
|
|
|
+ IF ~ReadCsd(card, result) THEN RETURN FALSE END;
|
|
|
+
|
|
|
+ SetBusClock(hc, card.csd.txSpeed);
|
|
|
+ (* Reasonable default timeout values, later rewritten with the ones given by the card *)
|
|
|
+ card.readTimeout := 100;
|
|
|
+ card.writeTimeout := 250;
|
|
|
+ card.eraseTimeout := 250;
|
|
|
+
|
|
|
+ (*IF ~Reset(hc, TRUE, FALSE) OR ~Reset(hc, FALSE, TRUE) THEN RETURN FALSE END;*)
|
|
|
+ IF ~ReadScr(card, result) THEN RETURN FALSE END;
|
|
|
+ ComputeTimeouts(card);
|
|
|
+
|
|
|
+ IF (card.scr.version >= Version1p1) THEN
|
|
|
+ IF EnableTrace THEN
|
|
|
+ Log.String("[SD] Enabling high-speed mode");
|
|
|
+ Log.Ln
|
|
|
+ END;
|
|
|
+ IF ~SelectSpeedMode(card, TRUE, result) THEN RETURN FALSE END;
|
|
|
+ IF EnableTrace THEN
|
|
|
+ Log.String("[SD] High-speed mode enabled");
|
|
|
+ Log.Ln
|
|
|
+ END;
|
|
|
+
|
|
|
+ (* Get CSD again: transfer speed might have changed *)
|
|
|
+ IF ~ReadCsd(card, result) THEN RETURN FALSE END;
|
|
|
+ ComputeTimeouts(card);
|
|
|
+ SetBusClock(hc, card.csd.txSpeed)
|
|
|
+ END;
|
|
|
+
|
|
|
+ IF 4 IN card.scr.busWidth THEN
|
|
|
+
|
|
|
+ IF EnableTrace THEN
|
|
|
+ Log.String("[SD] Changing bus width to 4 bits");
|
|
|
+ Log.Ln
|
|
|
+ END;
|
|
|
+
|
|
|
+ IF ~SetBusWidth(card.hc, card, 4, result) THEN RETURN FALSE END;
|
|
|
+ IF EnableTrace THEN
|
|
|
+ Log.String("[SD] Bus width changed");
|
|
|
+ Log.Ln
|
|
|
+ END
|
|
|
+ END;
|
|
|
+ IF ~Reset(hc, TRUE, FALSE) OR ~Reset(hc, FALSE, TRUE) THEN RETURN FALSE END;
|
|
|
+ GetSdStatus(card);
|
|
|
+
|
|
|
+ (*ResetStatistics; NEW(testbuf);
|
|
|
+ FOR response := 1 TO 100 DO
|
|
|
+ IF ~Read(card, 33554432, LEN(testbuf), testbuf^, 0, result) THEN RETURN FALSE END;
|
|
|
+ IF ~Write(card, 33554432, LEN(testbuf), testbuf^, 0, result) THEN RETURN FALSE END;
|
|
|
+ END;
|
|
|
+ Statistics(Commands.GetContext());
|
|
|
+ HALT(512);*)
|
|
|
+ RETURN TRUE
|
|
|
+ END InitCard;
|
|
|
+
|
|
|
+ (** Write 'data[ofs, ofs + len)' to 'card', starting at block 'firstBlock'. *)
|
|
|
+ PROCEDURE Write * (card: Card; firstBlock, len: LONGINT; VAR data: ARRAY OF SYSTEM.BYTE; ofs: LONGINT; VAR result: LONGINT): BOOLEAN;
|
|
|
+ VAR
|
|
|
+ command: Command;
|
|
|
+ start, stop: HUGEINT;
|
|
|
+ ret: BOOLEAN;
|
|
|
+ BEGIN
|
|
|
+ result := ErrorNone;
|
|
|
+ IF Synchronize THEN card.acquire END;
|
|
|
+ start := SdEnvironment.GetTimeCounter();
|
|
|
+ IF ~SelectCard(card.hc, card.rca, result) THEN
|
|
|
+ IF Synchronize THEN card.release END;
|
|
|
+ RETURN FALSE
|
|
|
+ END;
|
|
|
+
|
|
|
+ command.hc := card.hc;
|
|
|
+ command.rca := card.rca;
|
|
|
+
|
|
|
+ (* Pre-erase blocks *)
|
|
|
+ command.command := ACMD_SET_WR_BLK_ERASE_COUNT;
|
|
|
+ command.flags := {FlagApplicationCmd};
|
|
|
+ command.responseType := ResponseR1;
|
|
|
+ command.argument := (len + BlockSize - 1) DIV BlockSize;
|
|
|
+ IF ~ExecuteCommand(command, result) THEN
|
|
|
+ IF Synchronize THEN card.release END;
|
|
|
+ RETURN FALSE
|
|
|
+ END;
|
|
|
+
|
|
|
+ command.argument := firstBlock;
|
|
|
+ command.responseType := ResponseR1;
|
|
|
+ command.flags := {FlagData};
|
|
|
+ command.dataTimeout := card.writeTimeout;
|
|
|
+ command.blockSize := card.csd.writeBlockSize;
|
|
|
+ IF len > card.csd.writeBlockSize THEN
|
|
|
+ INCL(command.flags, FlagMultipleBlockTx);
|
|
|
+ INCL(command.flags, FlagCountBlocks);
|
|
|
+ INCL(command.flags, FlagAutoCmd12);
|
|
|
+ command.command := CMD_WRITE_MULTIPLE_BLOCK;
|
|
|
+ ELSE
|
|
|
+ command.command := CMD_WRITE_BLOCK
|
|
|
+ END;
|
|
|
+ ret := command.hc.transfer(command, data, ofs, len, result);
|
|
|
+ stop := SdEnvironment.GetTimeCounter();
|
|
|
+ IF Synchronize THEN card.release END;
|
|
|
+ IF ret THEN
|
|
|
+ INC(Nwrite);
|
|
|
+ INC(NbyteWritten, len);
|
|
|
+ INC(Twrite, stop - start)
|
|
|
+ END;
|
|
|
+ RETURN ret
|
|
|
+ END Write;
|
|
|
+
|
|
|
+ (** Read 'len' bytes starting from 'firstBlock' of 'card' to 'data[ofs, ofs + len)' *)
|
|
|
+ PROCEDURE Read * (card: Card; firstBlock, len: LONGINT; VAR data: ARRAY OF SYSTEM.BYTE; ofs: LONGINT; VAR result: LONGINT): BOOLEAN;
|
|
|
+ VAR
|
|
|
+ command: Command;
|
|
|
+ start, stop: HUGEINT;
|
|
|
+ ret: BOOLEAN;
|
|
|
+ BEGIN
|
|
|
+ IF Synchronize THEN card.acquire END;
|
|
|
+ start := SdEnvironment.GetTimeCounter();
|
|
|
+ IF ~SelectCard(card.hc, card.rca, result) THEN
|
|
|
+ IF Synchronize THEN card.release END;
|
|
|
+ RETURN FALSE
|
|
|
+ END;
|
|
|
+
|
|
|
+ command.hc := card.hc;
|
|
|
+ command.argument := firstBlock;
|
|
|
+ command.responseType := ResponseR1;
|
|
|
+ (*command.flags := {FlagData, FlagRead};*)
|
|
|
+ command.rca := card.rca;
|
|
|
+ command.dataTimeout := card.readTimeout;
|
|
|
+ command.blockSize := card.csd.readBlockSize;
|
|
|
+ IF len > card.csd.readBlockSize THEN
|
|
|
+ command.flags := {FlagData, FlagRead, FlagMultipleBlockTx, FlagCountBlocks, FlagAutoCmd12};
|
|
|
+ (*INCL(command.flags, FlagMultipleBlockTx);
|
|
|
+ INCL(command.flags, FlagCountBlocks);
|
|
|
+ INCL(command.flags, FlagAutoCmd12);*)
|
|
|
+ command.command := CMD_READ_MULTIPLE_BLOCK
|
|
|
+ ELSE
|
|
|
+ command.flags := {FlagData, FlagRead};
|
|
|
+ command.command := CMD_READ_SINGLE_BLOCK
|
|
|
+ END;
|
|
|
+ ret := command.hc.transfer(command, data, ofs, len, result) & ~(CardStatus_Error IN GetR1(command));
|
|
|
+ stop := SdEnvironment.GetTimeCounter();
|
|
|
+ IF Synchronize THEN card.release END;
|
|
|
+ IF ret THEN
|
|
|
+ INC(Nread);
|
|
|
+ INC(NbyteRead, len);
|
|
|
+ INC(Tread, stop - start)
|
|
|
+ END;
|
|
|
+ RETURN ret
|
|
|
+ END Read;
|
|
|
+
|
|
|
+ (** Erase blocks [block, block + num) on specified card. *)
|
|
|
+ PROCEDURE Erase * (card: Card; block, num: LONGINT; VAR result: LONGINT): BOOLEAN;
|
|
|
+ VAR
|
|
|
+ command: Command;
|
|
|
+ r1: SET;
|
|
|
+ BEGIN
|
|
|
+ command.hc := card.hc;
|
|
|
+ command.flags := {};
|
|
|
+ command.rca := card.rca;
|
|
|
+ command.dataTimeout := card.eraseTimeout;
|
|
|
+
|
|
|
+ command.responseType := ResponseR1;
|
|
|
+ command.argument := block;
|
|
|
+ command.command := CMD_ERASE_WR_BLK_START;
|
|
|
+ IF ~ExecuteCommand(command, result) & (CardStatus_Error IN GetR1(command)) THEN RETURN FALSE END;
|
|
|
+
|
|
|
+ command.argument := block + num - 1;
|
|
|
+ command.command := CMD_ERASE_WR_BLK_END;
|
|
|
+ IF ~ExecuteCommand(command, result) & (CardStatus_Error IN GetR1(command)) THEN RETURN FALSE END;
|
|
|
+
|
|
|
+ command.argument := 0;
|
|
|
+ command.command := CMD_ERASE;
|
|
|
+ command.responseType := ResponseR1b;
|
|
|
+ IF ~ExecuteCommand(command, result) & (CardStatus_Error IN GetR1(command)) THEN RETURN FALSE END;
|
|
|
+
|
|
|
+ (*WHILE 20 IN card.hc.regs.PresentState DO END;*)
|
|
|
+ command.command := CMD_SEND_STATUS;
|
|
|
+ command.responseType := ResponseR1;
|
|
|
+ REPEAT
|
|
|
+ IF ~ExecuteCommand(command, result) THEN RETURN FALSE END;
|
|
|
+ r1 := GetR1(command);
|
|
|
+ IF CardStatus_Error IN r1 THEN RETURN FALSE END
|
|
|
+ UNTIL CardStatus_ReadyForData IN r1;
|
|
|
+ RETURN TRUE
|
|
|
+ END Erase;
|
|
|
+
|
|
|
+ PROCEDURE ReadCsd (card: Card; VAR result: LONGINT): BOOLEAN;
|
|
|
+ VAR
|
|
|
+ command: Command;
|
|
|
+ csd: ARRAY 4 OF LONGINT;
|
|
|
+ BEGIN
|
|
|
+ result := ErrorNone;
|
|
|
+ IF ~DeselectCards(card.hc, result) THEN RETURN FALSE END;
|
|
|
+
|
|
|
+ command.hc := card.hc;
|
|
|
+ command.command := CMD_SEND_CSD;
|
|
|
+ command.argument := LSH(card.rca, 16);
|
|
|
+ command.responseType := ResponseR2;
|
|
|
+ command.flags := {};
|
|
|
+ IF ~ExecuteCommand(command, result) THEN RETURN FALSE END;
|
|
|
+ GetR2(command, csd);
|
|
|
+ DecodeCsd(csd, card.csd);
|
|
|
+ IF EnableTrace THEN
|
|
|
+ PrintCardCsd(csd);
|
|
|
+ Log.String("[SD] Card Capacity: ");
|
|
|
+ PrintSize(card.csd.capacity);
|
|
|
+ Log.Ln;
|
|
|
+ Log.String("[SD] Bus frequency: "); Log.Int(card.csd.txSpeed, 0); Log.String(" Hz"); Log.Ln
|
|
|
+ END;
|
|
|
+ RETURN TRUE
|
|
|
+ END ReadCsd;
|
|
|
+
|
|
|
+ (** Read the SD card Configuration Register of a card *)
|
|
|
+ PROCEDURE ReadScr (card: Card; VAR result: LONGINT): BOOLEAN;
|
|
|
+ VAR
|
|
|
+ command: Command;
|
|
|
+ ofs: LONGINT;
|
|
|
+ scr: ARRAY 8 OF CHAR;
|
|
|
+ BEGIN
|
|
|
+ IF ~SelectCard(card.hc, card.rca, result) THEN RETURN FALSE END;
|
|
|
+
|
|
|
+ (*
|
|
|
+ disconnect the pull-up resistor on D3 line (important for high-speed mode with 4 lines)
|
|
|
+ *)
|
|
|
+ (*command.hc := card.hc;
|
|
|
+ command.command := ACMD_SET_CLR_CARD_DETECT;
|
|
|
+ command.argument := 0;
|
|
|
+ command.responseType := ResponseR1;
|
|
|
+ command.flags := {FlagApplicationCmd};
|
|
|
+ command.rca := card.rca;
|
|
|
+ IF ~ExecuteCommand(command, result) THEN RETURN FALSE END;*)
|
|
|
+
|
|
|
+ (* Get Card Register: SCR *)
|
|
|
+ command.hc := card.hc;
|
|
|
+ command.command := ACMD_SEND_SCR;
|
|
|
+ command.argument := 0;
|
|
|
+ command.responseType := ResponseR1;
|
|
|
+ command.flags := {FlagApplicationCmd, FlagData, FlagRead};
|
|
|
+ command.rca := card.rca;
|
|
|
+ command.dataTimeout := card.readTimeout;
|
|
|
+ (*ofs := 32 - ADDRESSOF(scr[0]) MOD 32;*)
|
|
|
+ IF ~card.hc.transfer(command, scr, ofs, 8, result) THEN RETURN FALSE END;
|
|
|
+ IF CardStatus_Error IN GetR1(command) THEN result := ErrorCard; RETURN FALSE END;
|
|
|
+ DecodeScr(scr, ofs, card.scr);
|
|
|
+ IF EnableTrace THEN PrintCardScr(card.scr) END;
|
|
|
+ RETURN TRUE
|
|
|
+ END ReadScr;
|
|
|
+
|
|
|
+ PROCEDURE GetSdStatus (card: Card);
|
|
|
+ TYPE Bitfield = ARRAY 16 OF LONGINT;
|
|
|
+ VAR
|
|
|
+ status: ARRAY 64 OF CHAR;
|
|
|
+ command: Command;
|
|
|
+ ignore: LONGINT;
|
|
|
+ bitfield: POINTER {UNSAFE,UNTRACED} TO Bitfield;
|
|
|
+ c: CHAR;
|
|
|
+ i: LONGINT;
|
|
|
+ BEGIN
|
|
|
+ ASSERT(SelectCard(card.hc, card.rca, ignore));
|
|
|
+ command.hc := card.hc;
|
|
|
+ command.rca := card.rca;
|
|
|
+ command.command := ACMD_SD_STATUS;
|
|
|
+ command.flags := {FlagApplicationCmd, FlagData, FlagRead};
|
|
|
+ command.responseType := ResponseR1;
|
|
|
+ command.dataTimeout := card.readTimeout;
|
|
|
+ IF ~card.hc.transfer(command, status, 0, LEN(status), ignore) THEN RETURN END;
|
|
|
+
|
|
|
+ FOR i := 0 TO LEN(status) DIV 2 DO
|
|
|
+ c := status[i];
|
|
|
+ status[i] := status[LEN(status) - 1 - i];
|
|
|
+ status[LEN(status) - 1 - i] := c
|
|
|
+ END;
|
|
|
+
|
|
|
+ bitfield := ADDRESSOF(status[0]);
|
|
|
+ DecodeSdStatus(bitfield^, card.sdStatus);
|
|
|
+ PrintSdStatus(card.sdStatus);
|
|
|
+ END GetSdStatus;
|
|
|
+
|
|
|
+ PROCEDURE GetCid (card: Card);
|
|
|
+ VAR
|
|
|
+ cid: Cid;
|
|
|
+ command: Command;
|
|
|
+ res: LONGINT;
|
|
|
+ BEGIN
|
|
|
+ IF ~DeselectCards(card.hc, res) THEN HALT(512) END;
|
|
|
+ command.hc := card.hc;
|
|
|
+ command.command := CMD_SEND_CID;
|
|
|
+ command.argument := LSH(card.rca, 16);
|
|
|
+ command.flags := {};
|
|
|
+ command.responseType := ResponseR2;
|
|
|
+ IF ~ExecuteCommand(command, res) THEN HALT(512) END;
|
|
|
+ DecodeCid(command.response, cid);
|
|
|
+ PrintCardCid(cid)
|
|
|
+ END GetCid;
|
|
|
+
|
|
|
+ (**
|
|
|
+ Computes data timeouts for read, write and erase operations according to SD specifications.
|
|
|
+ Stores them in card.readTimeout, writeTimeout, eraseTimeout.
|
|
|
+ *)
|
|
|
+ PROCEDURE ComputeTimeouts (card: Card);
|
|
|
+ VAR
|
|
|
+ readTime: LONGINT;
|
|
|
+ BEGIN
|
|
|
+ (*
|
|
|
+ Read timeout is the lower of:
|
|
|
+ o (CSD.TAAC + CSD.NSAC / busfreq * 100) * TimeoutReadFactor
|
|
|
+ o TimeoutReadFix
|
|
|
+ for a normal SD card and
|
|
|
+ TimeoutReadFix
|
|
|
+ for a SDHC card.
|
|
|
+
|
|
|
+ Write timeout is very similar to read timeout, except that (CSD.TAAC + CSD.NSAC) * CSD.R2W is used.
|
|
|
+ *)
|
|
|
+ IF card.scr.version >= TypeSDHC THEN
|
|
|
+ card.readTimeout := TimeoutReadFix;
|
|
|
+ card.writeTimeout := TimeoutWriteFix
|
|
|
+ ELSE
|
|
|
+ readTime := 100 * LONGINT((card.csd.taac + REAL(card.csd.nsac * 100) / REAL(card.hc.frequency)) * 1000.0);
|
|
|
+ card.readTimeout := MIN(readTime, TimeoutReadFix);
|
|
|
+ card.writeTimeout := MIN(readTime * card.csd.r2w, TimeoutWriteFix);
|
|
|
+ END;
|
|
|
+ card.eraseTimeout := TimeoutErase;
|
|
|
+
|
|
|
+ IF EnableTrace THEN
|
|
|
+ Log.String("[SD] Read timeout = "); Log.Int(card.readTimeout, 0); Log.String(" ms"); Log.Ln;
|
|
|
+ Log.String("[SD] Write timeout = "); Log.Int(card.writeTimeout, 0); Log.String(" ms"); Log.Ln;
|
|
|
+ Log.String("[SD] Erase timeout = "); Log.Int(card.eraseTimeout, 0); Log.String(" ms"); Log.Ln
|
|
|
+ END
|
|
|
+ END ComputeTimeouts;
|
|
|
+
|
|
|
+ (** Select card speed mode. It is necessary to set it to high in order to use higher bus clock frequencies. *)
|
|
|
+ PROCEDURE SelectSpeedMode(card: Card; high: BOOLEAN; VAR res: LONGINT): BOOLEAN;
|
|
|
+ VAR
|
|
|
+ funcs: ARRAY 6 OF LONGINT;
|
|
|
+ status: SwitchFuncStatus;
|
|
|
+ val: SET;
|
|
|
+ BEGIN
|
|
|
+ funcs[0] := 1;
|
|
|
+ IF ~SwitchFunc(card, FALSE, funcs, status, res) THEN RETURN FALSE END;
|
|
|
+ IF EnableTrace THEN
|
|
|
+ Log.String("[SD] Select speed, before:");
|
|
|
+ Log.Ln;
|
|
|
+ PrintSwitchFuncStatus(status)
|
|
|
+ END;
|
|
|
+ IF ~(1 IN status.functionGroups[0]) THEN
|
|
|
+ Log.String("[SD] HIGH-SPEED MODE NOT SUPPORTED");
|
|
|
+ Log.Ln;
|
|
|
+ RETURN TRUE
|
|
|
+ END;
|
|
|
+ IF ~SwitchFunc(card, TRUE, funcs, status, res) THEN RETURN FALSE END;
|
|
|
+ IF EnableTrace THEN
|
|
|
+ Log.String("[SD] Select speed, after:");
|
|
|
+ Log.Ln;
|
|
|
+ PrintSwitchFuncStatus(status)
|
|
|
+ END;
|
|
|
+ val := SYSTEM.VAL(SET, LONGINT(card.hc.regs.HostControl1));
|
|
|
+ IF high THEN
|
|
|
+ INCL(val, HostControl1_HighSpeedEnable)
|
|
|
+ ELSE
|
|
|
+ EXCL(val, HostControl1_HighSpeedEnable)
|
|
|
+ END;
|
|
|
+ card.hc.regs.HostControl1 := SYSTEM.VAL(SHORTINT, val);
|
|
|
+ RETURN TRUE
|
|
|
+ END SelectSpeedMode;
|
|
|
+
|
|
|
+ PROCEDURE DecodeSdStatus (CONST raw: ARRAY OF LONGINT; VAR status: SdStatus);
|
|
|
+ VAR
|
|
|
+ val: LONGINT;
|
|
|
+ BEGIN
|
|
|
+ val := ReadBitfield(raw, SdStatus_DatBusWidthOfs, SdStatus_DatBusWidthWidth);
|
|
|
+ CASE val OF
|
|
|
+ 0: status.dataBusWidth := 1
|
|
|
+ |2: status.dataBusWidth := 4
|
|
|
+ ELSE
|
|
|
+ status.dataBusWidth := 0
|
|
|
+ END;
|
|
|
+ status.securedMode := ReadBit(raw, SdStatus_SecuredMode);
|
|
|
+ val := ReadBitfield(raw, SdStatus_SdCardTypeOfs, SdStatus_SdCardTypeWidth);
|
|
|
+ CASE val OF
|
|
|
+ 0: status.cardType := CategoryRW
|
|
|
+ |1: status.cardType := CategoryRO
|
|
|
+ |2: status.cardType := CategoryOTP
|
|
|
+ END;
|
|
|
+ status.sizeProtArea := ReadBitfield(raw, SdStatus_SizeOfProtectedAreaOfs, SdStatus_SizeOfProtectedAreaWidth);
|
|
|
+ (*! TODO: multiply sizeProtArea by MULT * BLOCK_LEN if SDSC *)
|
|
|
+ val := ReadBitfield(raw, SdStatus_SpeedClassOfs, SdStatus_SpeedClassWidth);
|
|
|
+ CASE val OF
|
|
|
+ 0 .. 3: status.speedClass := 2 * val
|
|
|
+ |4: status.speedClass := 10
|
|
|
+ END;
|
|
|
+ status.perfMove := ReadBitfield(raw, SdStatus_PerformanceMoveOfs, SdStatus_PerformanceMoveWidth);
|
|
|
+ status.auSize := ReadBitfield(raw, SdStatus_AuSizeOfs, SdStatus_AuSizeWidth) * 16 * 1024;
|
|
|
+ status.eraseSize := ReadBitfield(raw, SdStatus_EraseSizeOfs, SdStatus_EraseSizeWidth);
|
|
|
+ status.eraseTimeout := ReadBitfield(raw, SdStatus_EraseTimeoutOfs, SdStatus_EraseTimeoutWidth);
|
|
|
+ status.eraseOffset := ReadBitfield(raw, SdStatus_EraseOffsetOfs, SdStatus_EraseOffsetWidth);
|
|
|
+ val := ReadBitfield(raw, SdStatus_UhsSpeedGradeOfs, SdStatus_UhsSpeedGradeWidth);
|
|
|
+ CASE val OF
|
|
|
+ 0, 1, 3: status.uhsSpeedGrade := val
|
|
|
+ END;
|
|
|
+ status.uhsAuSize := ReadBitfield(raw, SdStatus_UhsAuSizeOfs, SdStatus_UhsAuSizeWidth) * 1024 * 1024;
|
|
|
+ status.vscSpeedClass := ReadBitfield(raw, SdStatus_VideoSpeedClassOfs, SdStatus_VideoSpeedClassWidth);
|
|
|
+ status.vscAuSize := ReadBitfield(raw, SdStatus_VscAuSizeOfs, SdStatus_VscAuSizeWidth) * 1024 * 1024;
|
|
|
+ status.suspensionAdr := ReadBitfield(raw, SdStatus_SusAddrOfs, SdStatus_SusAddrWidth);
|
|
|
+ status.appPerfClass := ReadBitfield(raw, SdStatus_AppPerfClassOfs, SdStatus_AppPerfClassWidth);
|
|
|
+ val := ReadBitfield(raw, SdStatus_PerformanceEnhanceOfs, SdStatus_PerformanceEnhanceWidth);
|
|
|
+ status.perfEnhance := SYSTEM.VAL(SET, val) * {PerformanceCardMaintenance .. PerformanceCache};
|
|
|
+ IF SYSTEM.VAL(SET, val) * {3 .. MAX(SET)} # {} THEN
|
|
|
+ INCL(status.perfEnhance, PerformanceQueue);
|
|
|
+ status.commandQueueDepth := LSH(val, -3) + 1
|
|
|
+ END;
|
|
|
+ status.discardSupport := ReadBit(raw, SdStatus_DiscardSupport);
|
|
|
+ status.fuleSupport := ReadBit(raw, SdStatus_FuleSupport)
|
|
|
+ END DecodeSdStatus;
|
|
|
+
|
|
|
+ PROCEDURE DecodeCid (CONST raw: ARRAY OF LONGINT; VAR cid: Cid);
|
|
|
+ VAR
|
|
|
+ val: LONGINT;
|
|
|
+ BEGIN
|
|
|
+ val := ReadBitfield(raw, CardCid_ProductManufacturingDateOfs, CardCid_ProductManufacturingDateWidth);
|
|
|
+ cid.manufacturingDate.year := val DIV 10H + 2000;
|
|
|
+ cid.manufacturingDate.month := val MOD 10H;
|
|
|
+ cid.productSerialNumber := SYSTEM.VAL(ADDRESS, ReadBitfield(raw, CardCid_ProductSerialNbOfs, CardCid_ProductSerialNbWidth));
|
|
|
+ val := ReadBitfield(raw, CardCid_ProductRevisionOfs, CardCid_ProductRevisionWidth);
|
|
|
+ cid.productRevision.n := val DIV 10H;
|
|
|
+ cid.productRevision.m := val MOD 10H;
|
|
|
+ SYSTEM.MOVE(ADDRESSOF(raw[0]) + CardCid_ProductNameOfs DIV 8, ADDRESSOF(cid.productName), CardCid_ProductNameWidth DIV 8);
|
|
|
+ SYSTEM.MOVE(ADDRESSOF(raw[0]) + CardCid_OEM_ApplicationIdOfs DIV 8, ADDRESSOF(cid.oemId), CardCid_OEM_ApplicationIdWidth DIV 8);
|
|
|
+ cid.manufacturerId := ReadBitfield(raw, CardCid_ManufacturerIdOfs, CardCid_ManufacturerIdWidth)
|
|
|
+ END DecodeCid;
|
|
|
+
|
|
|
+ (** Fills a Csd record from the raw csd bytes *)
|
|
|
+ PROCEDURE DecodeCsd (CONST raw: ARRAY OF LONGINT; VAR csd: Csd);
|
|
|
+ VAR
|
|
|
+ sizeMult, val, version: LONGINT;
|
|
|
+ real: REAL;
|
|
|
+ BEGIN
|
|
|
+ version := ReadBitfield(raw, CardCsd_CsdStructureOfs, CardCsd_CsdStructureWidth) + 1;
|
|
|
+
|
|
|
+ IF version = 1 THEN
|
|
|
+ sizeMult := LSH(LONGINT(2), ReadBitfield(raw, CardCsd_CSizeMultOfs1, CardCsd_CSizeMultWidth1));
|
|
|
+ csd.capacity := sizeMult * (1 + ReadBitfield(raw, CardCsd_CSizeOfs1, CardCsd_CSizeWidth1)) * 512;
|
|
|
+ ELSE
|
|
|
+ csd.capacity := 512 * 1024 * (HUGEINT(ReadBitfield(raw, CardCsd_CSizeOfs2, CardCsd_CSizeWidth2)) + 1);
|
|
|
+ END;
|
|
|
+
|
|
|
+ csd.commandClasses := SYSTEM.VAL(SET, ReadBitfield(raw, CardCsd_CccOfs, CardCsd_CccWidth));
|
|
|
+ csd.nsac := ReadBitfield(raw, CardCsd_NsacOfs, CardCsd_NsacWidth) * 100;
|
|
|
+
|
|
|
+ val := ReadBitfield(raw, CardCsd_R2wFactorOfs, CardCsd_R2wFactorWidth);
|
|
|
+ IF val >= 6 THEN
|
|
|
+ csd.r2w := 0
|
|
|
+ ELSE
|
|
|
+ csd.r2w := LSH(LONGINT(1), val);
|
|
|
+ END;
|
|
|
+
|
|
|
+ val := ReadBitfield(raw, CardCsd_TranSpeedOfs, CardCsd_TranSpeedWidth);
|
|
|
+ CASE val DIV 8 OF
|
|
|
+ 1: csd.txSpeed := 10
|
|
|
+ |2: csd.txSpeed := 12
|
|
|
+ |3: csd.txSpeed := 13
|
|
|
+ |4: csd.txSpeed := 15
|
|
|
+ |5: csd.txSpeed := 20
|
|
|
+ |6: csd.txSpeed := 25
|
|
|
+ |7: csd.txSpeed := 30
|
|
|
+ |8: csd.txSpeed := 35
|
|
|
+ |9: csd.txSpeed := 40
|
|
|
+ |10: csd.txSpeed := 45
|
|
|
+ |11: csd.txSpeed := 50
|
|
|
+ |12: csd.txSpeed := 55
|
|
|
+ |13: csd.txSpeed := 60
|
|
|
+ |14: csd.txSpeed := 70
|
|
|
+ |15: csd.txSpeed := 80
|
|
|
+ ELSE
|
|
|
+ csd.txSpeed := 00
|
|
|
+ END;
|
|
|
+ csd.txSpeed := csd.txSpeed * 100;
|
|
|
+ CASE val MOD 8 OF
|
|
|
+ 0: csd.txSpeed := csd.txSpeed * 100
|
|
|
+ |1: csd.txSpeed := csd.txSpeed * 1000
|
|
|
+ |2: csd.txSpeed := csd.txSpeed * 10000
|
|
|
+ |3: csd.txSpeed := csd.txSpeed * 100000
|
|
|
+ END;
|
|
|
+
|
|
|
+ val := ReadBitfield(raw, CardCsd_TaacOfs, CardCsd_TaacWidth);
|
|
|
+ CASE val DIV 8 OF
|
|
|
+ 1: real := 1.0
|
|
|
+ |2: real := 1.2
|
|
|
+ |3: real := 1.3
|
|
|
+ |4: real := 1.5
|
|
|
+ |5: real := 2.0
|
|
|
+ |6: real := 2.5
|
|
|
+ |7: real := 3.0
|
|
|
+ |8: real := 3.5
|
|
|
+ |9: real := 4.0
|
|
|
+ |10: real := 4.5
|
|
|
+ |11: real := 5.0
|
|
|
+ |12: real := 5.5
|
|
|
+ |13: real := 6.0
|
|
|
+ |14: real := 7.0
|
|
|
+ |15: real := 8.0
|
|
|
+ ELSE
|
|
|
+ real := 0.0
|
|
|
+ END;
|
|
|
+ CASE val MOD 8 OF
|
|
|
+ 0: real := real * 1.0E-9
|
|
|
+ |1: real := real * 1.0E-8
|
|
|
+ |2: real := real * 1.0E-7
|
|
|
+ |3: real := real * 1.0E-6
|
|
|
+ |4: real := real * 1.0E-5
|
|
|
+ |5: real := real * 1.0E-4
|
|
|
+ |6: real := real * 1.0E-3
|
|
|
+ |7: real := real * 1.0E-2
|
|
|
+ END;
|
|
|
+ csd.taac := real;
|
|
|
+
|
|
|
+ csd.partialRead := ReadBitfield(raw, CardCsd_ReadBlPartial, 1) = 1;
|
|
|
+ csd.misalignedRead := ReadBitfield(raw, CardCsd_ReadBlkMisalign, 1) = 1;
|
|
|
+ val := ReadBitfield(raw, CardCsd_ReadBlLenOfs, CardCsd_ReadBlLenWidth);
|
|
|
+ IF (val <= 8) OR (val >= 12) THEN
|
|
|
+ csd.readBlockSize := BlockSize
|
|
|
+ ELSE
|
|
|
+ csd.readBlockSize := LSH(LONGINT(1), val)
|
|
|
+ END;
|
|
|
+
|
|
|
+ csd.partialWrite := ReadBitfield(raw, CardCsd_WriteBlPartial, 1) = 1;
|
|
|
+ csd.misalignedWrite := ReadBitfield(raw, CardCsd_WriteBlkMisalign, 1) = 1;
|
|
|
+ val := ReadBitfield(raw, CardCsd_WriteBlLenOfs, CardCsd_WriteBlLenWidth);
|
|
|
+ IF (val <= 8) OR (val >= 12) THEN
|
|
|
+ csd.writeBlockSize := BlockSize
|
|
|
+ ELSE
|
|
|
+ csd.writeBlockSize := LSH(LONGINT(1), val)
|
|
|
+ END
|
|
|
+ END DecodeCsd;
|
|
|
+
|
|
|
+ (** Fills a SCR record from raw data bytes *)
|
|
|
+ PROCEDURE DecodeScr (CONST raw: ARRAY OF CHAR; ofs: LONGINT; VAR scr: Scr);
|
|
|
+ TYPE
|
|
|
+ Array = ARRAY 8 OF CHAR;
|
|
|
+ VAR
|
|
|
+ bfield: ARRAY 2 OF LONGINT;
|
|
|
+ i: LONGINT;
|
|
|
+ val: SET;
|
|
|
+ BEGIN
|
|
|
+ FOR i := 0 TO 7 DO SYSTEM.VAL(Array, bfield)[7-i] := raw[ofs + i] END;
|
|
|
+ IF ReadBitfield(bfield, CardScr_StructureOfs, CardScr_StructureWidth) # 0 THEN RETURN END;
|
|
|
+ scr.version := ReadBitfield(bfield, CardScr_SpecVersionOfs, CardScr_SpecVersionWidth);
|
|
|
+ IF ReadBitfield(bfield, CardScr_SpecV3, 1) # 0 THEN scr.version := Version3 END;
|
|
|
+ IF ReadBitfield(bfield, CardScr_SpecV4, 1) # 0 THEN scr.version := Version4 END;
|
|
|
+ i := ReadBitfield(bfield, CardScr_SpecVXOfs, CardScr_SpecVXWidth);
|
|
|
+ IF i = CardScr_SpecVX_v5 THEN scr.version := Version5
|
|
|
+ ELSIF i = CardScr_SpecVX_v6 THEN scr.version := Version6
|
|
|
+ END;
|
|
|
+
|
|
|
+ CASE ReadBitfield(bfield, CardScr_SecurityOfs, CardScr_SecurityWidth) OF
|
|
|
+ 0: scr.security := TypeNone
|
|
|
+ |2: scr.security := TypeSDSC
|
|
|
+ |3: scr.security := TypeSDHC
|
|
|
+ |4: scr.security := TypeSDXC
|
|
|
+ END;
|
|
|
+ val := SYSTEM.VAL(SET, ReadBitfield(bfield, CardScr_BusWidthsOfs, CardScr_BusWidthsWidth));
|
|
|
+ FOR i := 0 TO MAX(SET) - 1 DO
|
|
|
+ IF i IN val THEN
|
|
|
+ INCL(scr.busWidth, LSH(LONGINT(1), i))
|
|
|
+ END
|
|
|
+ END
|
|
|
+ END DecodeScr;
|
|
|
+
|
|
|
+ (**
|
|
|
+ Print the state of all Host Controller registers
|
|
|
+ *)
|
|
|
+ PROCEDURE PrintHcRegisters * (regs: HcRegisters);
|
|
|
+ BEGIN
|
|
|
+ IF EnableTrace THEN
|
|
|
+ Log.String("[SD] HC registers status:"); Log.Ln;
|
|
|
+ Log.String("[SD] SDMASystemAddress: 0x"); Log.Hex(regs.SDMASystemAddress,-SIZEOF(LONGINT)*2); Log.Ln;
|
|
|
+ Log.String("[SD] BlockSize: 0x"); Log.Hex(regs.BlockSize,-SIZEOF(INTEGER)*2); Log.Ln;
|
|
|
+ Log.String("[SD] BlockCount: 0x"); Log.Hex(regs.BlockCount,-SIZEOF(INTEGER)*2); Log.Ln;
|
|
|
+ Log.String("[SD] Argument1: 0x"); Log.Hex(regs.Argument1,-SIZEOF(LONGINT)*2); Log.Ln;
|
|
|
+ Log.String("[SD] TransferMode: 0x"); Log.Hex(regs.TransferMode,-SIZEOF(INTEGER)*2); Log.Ln;
|
|
|
+ Log.String("[SD] Command: 0x"); Log.Hex(regs.Command,-SIZEOF(INTEGER)*2); Log.Ln;
|
|
|
+
|
|
|
+ Log.String("[SD] Response: 0x");
|
|
|
+ Log.Hex(regs.Response[0],-SIZEOF(LONGINT)*2); Log.String(" 0x");
|
|
|
+ Log.Hex(regs.Response[1],-SIZEOF(LONGINT)*2); Log.String(" 0x");
|
|
|
+ Log.Hex(regs.Response[2],-SIZEOF(LONGINT)*2); Log.String(" 0x");
|
|
|
+ Log.Hex(regs.Response[3],-SIZEOF(LONGINT)*2); Log.Ln;
|
|
|
+
|
|
|
+ Log.String("[SD] BufferData: 0x"); Log.Hex(regs.BufferData,-SIZEOF(LONGINT)*2); Log.Ln;
|
|
|
+ Log.String("[SD] PresentState: "); Log.Set(regs.PresentState); Log.Ln;
|
|
|
+ Log.String("[SD] HostControl1: 0x"); Log.Hex(regs.HostControl1,-SIZEOF(SHORTINT)*2); Log.Ln;
|
|
|
+ Log.String("[SD] PowerControl: 0x"); Log.Hex(regs.PowerControl,-SIZEOF(SHORTINT)*2); Log.Ln;
|
|
|
+ Log.String("[SD] BlockGapControl: 0x"); Log.Hex(regs.BlockGapControl,-SIZEOF(SHORTINT)*2); Log.Ln;
|
|
|
+ Log.String("[SD] WakeupControl: 0x"); Log.Hex(regs.WakeupControl,-SIZEOF(SHORTINT)*2); Log.Ln;
|
|
|
+ Log.String("[SD] ClockControl: 0x"); Log.Hex(regs.ClockControl,-SIZEOF(INTEGER)*2); Log.Ln;
|
|
|
+ Log.String("[SD] TimeoutControl: 0x"); Log.Hex(regs.TimeoutControl,-SIZEOF(SHORTINT)*2); Log.Ln;
|
|
|
+ Log.String("[SD] SoftwareReset: 0x"); Log.Hex(regs.SoftwareReset,-SIZEOF(SHORTINT)*2); Log.Ln;
|
|
|
+ Log.String("[SD] InterruptStatus: "); Log.Set(regs.InterruptStatus); Log.Ln;
|
|
|
+ Log.String("[SD] InterruptStatusEnable: "); Log.Set(regs.InterruptStatusEnable); Log.Ln;
|
|
|
+ Log.String("[SD] InterruptSignalEnable: "); Log.Set(regs.InterruptSignalEnable); Log.Ln;
|
|
|
+ Log.String("[SD] AutoCmdErrorStatus: 0x"); Log.Hex(regs.AutoCmdErrorStatus,-SIZEOF(INTEGER)*2); Log.Ln;
|
|
|
+ Log.String("[SD] HostControl2: 0x"); Log.Hex(regs.HostControl2,-SIZEOF(INTEGER)*2); Log.Ln;
|
|
|
+
|
|
|
+ Log.String("[SD] Capabilities: ");
|
|
|
+ Log.Set(regs.Capabilities[0]); Log.String(" ");
|
|
|
+ Log.Set(regs.Capabilities[1]); Log.Ln;
|
|
|
+
|
|
|
+ (*Log.String("[SD] MaximumCurrentCapabilities: 0x"); Log.Hex(regs.MaximumCurrentCapabilities,-SIZEOF(HUGEINT)*2); Log.Ln;
|
|
|
+ Log.String("[SD] ForceEventAutoCmdErrorStatus: 0x"); Log.Hex(regs.ForceEventAutoCmdErrorStatus,-SIZEOF(INTEGER)*2); Log.Ln;
|
|
|
+ Log.String("[SD] ForceEventErrorInterruptStatus: 0x"); Log.Hex(regs.ForceEventErrorInterruptStatus,-SIZEOF(INTEGER)*2); Log.Ln;
|
|
|
+ Log.String("[SD] AdmaErrorStatus: 0x"); Log.Hex(regs.AdmaErrorStatus,-SIZEOF(SHORTINT)*2); Log.Ln;
|
|
|
+ Log.String("[SD] AdmaSystemAddress: 0x"); Log.Hex(regs.AdmaSystemAddress,-SIZEOF(HUGEINT)*2); Log.Ln;*)
|
|
|
+
|
|
|
+ Log.String("[SD] PresetValues: 0x");
|
|
|
+ Log.Hex(regs.PresetValues[0],-SIZEOF(INTEGER)*2); Log.String(" 0x");
|
|
|
+ Log.Hex(regs.PresetValues[1],-SIZEOF(INTEGER)*2); Log.String(" 0x");
|
|
|
+ Log.Hex(regs.PresetValues[2],-SIZEOF(INTEGER)*2); Log.String(" 0x");
|
|
|
+ Log.Hex(regs.PresetValues[3],-SIZEOF(INTEGER)*2); Log.String(" 0x");
|
|
|
+ Log.Hex(regs.PresetValues[4],-SIZEOF(INTEGER)*2); Log.String(" 0x");
|
|
|
+ Log.Hex(regs.PresetValues[5],-SIZEOF(INTEGER)*2); Log.String(" 0x");
|
|
|
+ Log.Hex(regs.PresetValues[6],-SIZEOF(INTEGER)*2); Log.String(" 0x");
|
|
|
+ Log.Hex(regs.PresetValues[7],-SIZEOF(INTEGER)*2); Log.Ln;
|
|
|
+
|
|
|
+ Log.String("[SD] SharedBusControl: 0x"); Log.Hex(regs.SharedBusControl,-SIZEOF(LONGINT)*2); Log.Ln;
|
|
|
+ Log.String("[SD] SlotInterruptStatus: 0x"); Log.Hex(regs.SlotInterruptStatus,-SIZEOF(INTEGER)*2); Log.Ln;
|
|
|
+ Log.String("[SD] HostControllerVersion: 0x"); Log.Hex(regs.HostControllerVersion,-SIZEOF(INTEGER)*2); Log.Ln
|
|
|
+ END
|
|
|
+ END PrintHcRegisters;
|
|
|
+
|
|
|
+ (** Outputs the capabilities of a host controller on the log. *)
|
|
|
+ PROCEDURE PrintCapabilities * (hc: HostController);
|
|
|
+ VAR
|
|
|
+ c0, c1: SET;
|
|
|
+ BEGIN
|
|
|
+ IF EnableTrace THEN
|
|
|
+ c0 := hc.regs.Capabilities[0];
|
|
|
+ c1 := hc.regs.Capabilities[1];
|
|
|
+ Log.String("[SD] "); Log.String("Host Capabilities:"); Log.Ln;
|
|
|
+ Log.String("[SD] "); Log.String(" Timeout Clock Frequency: ");
|
|
|
+ IF c0 * Capabilities_TimeoutClockFrequencyMask = {} THEN
|
|
|
+ Log.String("Unknown")
|
|
|
+ ELSE
|
|
|
+ Log.Int(SYSTEM.VAL(LONGINT, c0 * Capabilities_TimeoutClockFrequencyMask), 0);
|
|
|
+ IF Capabilities_TimeoutClockUnit IN c0 THEN
|
|
|
+ Log.String(" MHz")
|
|
|
+ ELSE
|
|
|
+ Log.String(" kHz")
|
|
|
+ END
|
|
|
+ END;
|
|
|
+ Log.Ln;
|
|
|
+
|
|
|
+ Log.String("[SD] "); Log.String(" Base Clock Frequency: ");
|
|
|
+ IF c0 * Capabilities_BaseClockFreqSdMask = {} THEN
|
|
|
+ Log.String("Unknown")
|
|
|
+ ELSE
|
|
|
+ Log.Int(LSH(SYSTEM.VAL(LONGINT, c0 * Capabilities_BaseClockFreqSdMask), -Capabilities_BaseClockFreqSdOfs), 0)
|
|
|
+ END;
|
|
|
+ Log.Ln;
|
|
|
+
|
|
|
+ Log.String("[SD] "); Log.String(" Max Block Length: ");
|
|
|
+ Log.Int(512 * (1 + LSH(SYSTEM.VAL(LONGINT, c0 * Capabilities_MaxBlockLenMask), -Capabilities_MaxBlockLenOfs)), 0);
|
|
|
+ Log.String(" B");
|
|
|
+ Log.Ln;
|
|
|
+
|
|
|
+ Log.String("[SD] "); Log.String(" 8 Bit Support for Embedded Device: ");
|
|
|
+ Log.Boolean(Capabilities_8BitEmbedded IN c0);
|
|
|
+ Log.Ln;
|
|
|
+
|
|
|
+ Log.String("[SD] "); Log.String(" Support for ADMA2: ");
|
|
|
+ Log.Boolean(Capabilities_ADMA2 IN c0);
|
|
|
+ Log.Ln;
|
|
|
+
|
|
|
+ Log.String("[SD] "); Log.String(" Support for High Speed: ");
|
|
|
+ Log.Boolean(Capabilities_HighSpeed IN c0);
|
|
|
+ Log.Ln;
|
|
|
+
|
|
|
+ Log.String("[SD] "); Log.String(" Support for SDMA: ");
|
|
|
+ Log.Boolean(Capabilities_SDMA IN c0);
|
|
|
+ Log.Ln;
|
|
|
+
|
|
|
+ Log.String("[SD] "); Log.String(" Support for Suspend/Resume: ");
|
|
|
+ Log.Boolean(Capabilities_SuspendResume IN c0);
|
|
|
+ Log.Ln;
|
|
|
+
|
|
|
+ Log.String("[SD] "); Log.String(" Voltage Support for 3.3 V: ");
|
|
|
+ Log.Boolean(Capabilities_Voltage33 IN c0);
|
|
|
+ Log.Ln;
|
|
|
+
|
|
|
+ Log.String("[SD] "); Log.String(" Voltage Support for 3.0 V: ");
|
|
|
+ Log.Boolean(Capabilities_Voltage30 IN c0);
|
|
|
+ Log.Ln;
|
|
|
+
|
|
|
+ Log.String("[SD] "); Log.String(" Voltage Support for 1.8 V: ");
|
|
|
+ Log.Boolean(Capabilities_Voltage18 IN c0);
|
|
|
+ Log.Ln;
|
|
|
+
|
|
|
+ Log.String("[SD] "); Log.String(" Support for 64 Bit Bus: ");
|
|
|
+ Log.Boolean(Capabilities_64BitBus IN c0);
|
|
|
+ Log.Ln;
|
|
|
+
|
|
|
+ Log.String("[SD] "); Log.String(" Support for Asynchronous Interrupts: ");
|
|
|
+ Log.Boolean(Capabilities_AsyncInterrupt IN c0);
|
|
|
+ Log.Ln;
|
|
|
+
|
|
|
+ Log.String("[SD] "); Log.String(" Slot Type: ");
|
|
|
+ IF c0 * Capabilities_SlotTypeMask = Capabilities_SlotType_Removable THEN
|
|
|
+ Log.String("Removable Card Slot")
|
|
|
+ ELSIF c0 * Capabilities_SlotTypeMask = Capabilities_SlotType_Embedded THEN
|
|
|
+ Log.String("Embedded Slot for One Device")
|
|
|
+ ELSIF c0 * Capabilities_SlotTypeMask = Capabilities_SlotType_SharedBus THEN
|
|
|
+ Log.String("Shared Bus Slot")
|
|
|
+ END;
|
|
|
+ Log.Ln;
|
|
|
+
|
|
|
+ IF hc.version = 3 THEN
|
|
|
+ Log.String("[SD] "); Log.String(" Support for SDR50: ");
|
|
|
+ Log.Boolean(Capabilities_SDR50 IN c1);
|
|
|
+ Log.Ln;
|
|
|
+
|
|
|
+ Log.String("[SD] "); Log.String(" Support for SDR104: ");
|
|
|
+ Log.Boolean(Capabilities_SDR104 IN c1);
|
|
|
+ Log.Ln;
|
|
|
+
|
|
|
+ Log.String("[SD] "); Log.String(" Support for DDR50: ");
|
|
|
+ Log.Boolean(Capabilities_DDR50 IN c1);
|
|
|
+ Log.Ln;
|
|
|
+
|
|
|
+ Log.String("[SD] "); Log.String(" Support for Driver Type A: ");
|
|
|
+ Log.Boolean(Capabilities_DriverTypeA IN c1);
|
|
|
+ Log.Ln;
|
|
|
+
|
|
|
+ Log.String("[SD] "); Log.String(" Support for Driver Type C: ");
|
|
|
+ Log.Boolean(Capabilities_DriverTypeC IN c1);
|
|
|
+ Log.Ln;
|
|
|
+
|
|
|
+ Log.String("[SD] "); Log.String(" Support for Driver Type D: ");
|
|
|
+ Log.Boolean(Capabilities_DriverTypeD IN c1);
|
|
|
+ Log.Ln;
|
|
|
+
|
|
|
+ Log.String("[SD] "); Log.String(" Timer Count for Retuning: ");
|
|
|
+ IF c1 * Capabilities_TimerCountRetuningMask = Capabilities_TimerCountRetuningMask THEN
|
|
|
+ Log.String("Unknown")
|
|
|
+ ELSIF c1 * Capabilities_TimerCountRetuningMask = {} THEN
|
|
|
+ Log.String("Disabled")
|
|
|
+ ELSE
|
|
|
+ Log.Int(LSH(LONGINT(1), LSH(SYSTEM.VAL(LONGINT, c1 * Capabilities_TimerCountRetuningMask), -Capabilities_TimerCountRetuningOfs)), 0);
|
|
|
+ Log.String(" s")
|
|
|
+ END;
|
|
|
+ Log.Ln;
|
|
|
+
|
|
|
+ Log.String("[SD] "); Log.String(" SDR50 Requires Retuning: ");
|
|
|
+ Log.Boolean(Capabilities_TuningSDR50 IN c1);
|
|
|
+ Log.Ln;
|
|
|
+
|
|
|
+ Log.String("[SD] "); Log.String(" Retuning Mode: ");
|
|
|
+ Log.Int(LSH(SYSTEM.VAL(LONGINT, c1 * Capabilities_RetuningModesMask), -Capabilities_RetuningModesOfs), 0);
|
|
|
+ Log.Ln;
|
|
|
+
|
|
|
+ Log.String("[SD] "); Log.String(" Clock Multiplier: ");
|
|
|
+ IF c1 * Capabilities_ClockMultiplierMask = {} THEN
|
|
|
+ Log.String("Not Supported")
|
|
|
+ ELSE
|
|
|
+ Log.Int(LSH(SYSTEM.VAL(LONGINT, c1 * Capabilities_ClockMultiplierMask), -Capabilities_ClockMultiplierOfs) + 1, 0)
|
|
|
+ END;
|
|
|
+ Log.Ln
|
|
|
+ END
|
|
|
+ END
|
|
|
+ END PrintCapabilities;
|
|
|
+
|
|
|
+ (** Print a card status response on the log. *)
|
|
|
+ PROCEDURE PrintCardStatus * (status: SET);
|
|
|
+ BEGIN
|
|
|
+ IF EnableTrace THEN
|
|
|
+ Log.String("[SD] Card Status:"); Log.Ln;
|
|
|
+ Log.String("[SD] AKE Error: "); Log.Boolean(CardStatus_AkeSpecError IN status); Log.Ln;
|
|
|
+ Log.String("[SD] App Command: "); Log.Boolean(CardStatus_AppCmd IN status); Log.Ln;
|
|
|
+ Log.String("[SD] Ready For Data: "); Log.Boolean(CardStatus_ReadyForData IN status); Log.Ln;
|
|
|
+ Log.String("[SD] Card State: ");
|
|
|
+ CASE LSH(SYSTEM.VAL(LONGINT, status * CardStatus_CurrentStateMask), -CardStatus_CurrentStateOffset) OF
|
|
|
+ CardIdle: Log.String("Idle")
|
|
|
+ |CardReady: Log.String("Ready")
|
|
|
+ |CardIdentification: Log.String("Identification")
|
|
|
+ |CardStandby: Log.String("Standby")
|
|
|
+ |CardTransfer: Log.String("Transfer")
|
|
|
+ |CardData: Log.String("Sending Data")
|
|
|
+ |CardReceive: Log.String("Receiving Data")
|
|
|
+ |CardProgram: Log.String("Programming")
|
|
|
+ |CardDisabled: Log.String("Disabled")
|
|
|
+ END;
|
|
|
+ Log.Ln;
|
|
|
+ Log.String("[SD] Erase Reset: "); Log.Boolean(CardStatus_EraseReset IN status); Log.Ln;
|
|
|
+ Log.String("[SD] Internal ECC Enable: "); Log.Boolean(CardStatus_CardEccDisable IN status); Log.Ln;
|
|
|
+ Log.String("[SD] Write_Protection Erase Skip: "); Log.Boolean(CardStatus_WpEraseSkip IN status); Log.Ln;
|
|
|
+ Log.String("[SD] CSD Overwrite: "); Log.Boolean(CardStatus_CsdOverwrite IN status); Log.Ln;
|
|
|
+ Log.String("[SD] Error: "); Log.Boolean(CardStatus_Error IN status); Log.Ln;
|
|
|
+ Log.String("[SD] Card Controller Error: "); Log.Boolean(CardStatus_CcError IN status); Log.Ln;
|
|
|
+ Log.String("[SD] Card ECC Failed: "); Log.Boolean(CardStatus_CardEccFailed IN status); Log.Ln;
|
|
|
+ Log.String("[SD] Illegal Command: "); Log.Boolean(CardStatus_IllegalCommand IN status); Log.Ln;
|
|
|
+ Log.String("[SD] Command CRC Error: "); Log.Boolean(CardStatus_ComCrcError IN status); Log.Ln;
|
|
|
+ Log.String("[SD] Lock/Unlock Failed: "); Log.Boolean(CardStatus_LockUnlockFailed IN status); Log.Ln;
|
|
|
+ Log.String("[SD] Card is Locked: "); Log.Boolean(CardStatus_CardIsLocked IN status); Log.Ln;
|
|
|
+ Log.String("[SD] Write-Protection Violation: "); Log.Boolean(CardStatus_WpViolation IN status); Log.Ln;
|
|
|
+ Log.String("[SD] Invalid Erase Parameters: "); Log.Boolean(CardStatus_EraseParam IN status); Log.Ln;
|
|
|
+ Log.String("[SD] Erase Sequence Error: "); Log.Boolean(CardStatus_EraseSeqError IN status); Log.Ln;
|
|
|
+ Log.String("[SD] Block Length Error: "); Log.Boolean(CardStatus_BlockLenError IN status); Log.Ln;
|
|
|
+ Log.String("[SD] Address Error: "); Log.Boolean(CardStatus_AddressError IN status); Log.Ln;
|
|
|
+ Log.String("[SD] Argument Out of Range: "); Log.Boolean(CardStatus_OutOfRange IN status); Log.Ln
|
|
|
+ END
|
|
|
+ END PrintCardStatus;
|
|
|
+
|
|
|
+ (** Print SD Status record *)
|
|
|
+ PROCEDURE PrintSdStatus * (status: SdStatus);
|
|
|
+ BEGIN
|
|
|
+ IF EnableTrace THEN
|
|
|
+ Log.String("[SD] SD Status"); Log.Ln;
|
|
|
+ Log.String("[SD] data bus width: "); Log.Int(status.dataBusWidth, 0); Log.Ln;
|
|
|
+ Log.String("[SD] secured mode enabled: "); Log.Boolean(status.securedMode); Log.Ln;
|
|
|
+ Log.String("[SD] cardType: ");
|
|
|
+ CASE status.cardType OF
|
|
|
+ CategoryRW: Log.String("normal RW card")
|
|
|
+ |CategoryRO: Log.String("read-only card")
|
|
|
+ |CategoryOTP: Log.String("one-time programmable card")
|
|
|
+ END; Log.Ln;
|
|
|
+ Log.String("[SD] size of protected area: "); PrintSize(status.sizeProtArea); Log.Ln;
|
|
|
+ Log.String("[SD] move performance: ");
|
|
|
+ CASE status.perfMove OF
|
|
|
+ 0: Log.String("sequential write")
|
|
|
+ |255: Log.String("infinite")
|
|
|
+ ELSE
|
|
|
+ Log.Int(status.perfMove, 0);
|
|
|
+ Log.String(" MB/s")
|
|
|
+ END; Log.Ln;
|
|
|
+ Log.String("[SD] erase size: "); Log.Int(status.eraseSize, 0); Log.String(" AUs"); Log.Ln;
|
|
|
+ Log.String("[SD] erase timeout: ");
|
|
|
+ IF status.eraseTimeout = 0 THEN
|
|
|
+ Log.String("not supported")
|
|
|
+ ELSE
|
|
|
+ Log.Int(status.eraseTimeout, 0); Log.String(" s")
|
|
|
+ END; Log.Ln;
|
|
|
+ Log.String("[SD] erase timeout offset: "); Log.Int(status.eraseOffset, 0); Log.String(" s"); Log.Ln;
|
|
|
+ Log.String("[SD] speed class: "); Log.Int(status.speedClass, 0); Log.Ln;
|
|
|
+ Log.String("[SD] AU size: "); PrintSize(status.auSize); Log.Ln;
|
|
|
+ Log.String("[SD] UHS speed grade: "); Log.Int(status.uhsSpeedGrade, 0); Log.Ln;
|
|
|
+ Log.String("[SD] UHS AU size: "); PrintSize(status.uhsAuSize); Log.Ln;
|
|
|
+ Log.String("[SD] video speed class: "); Log.Int(status.vscSpeedClass, 0); Log.Ln;
|
|
|
+ Log.String("[SD] VSC AU size: "); PrintSize(status.vscAuSize); Log.Ln;
|
|
|
+ Log.String("[SD] suspension address: "); Log.Int(status.suspensionAdr, 0); Log.Ln;
|
|
|
+ Log.String("[SD] application performance class: ");
|
|
|
+ IF status.appPerfClass = 0 THEN
|
|
|
+ Log.String("not supported")
|
|
|
+ ELSE
|
|
|
+ Log.String("A");
|
|
|
+ Log.Int(status.appPerfClass, 0)
|
|
|
+ END; Log.Ln;
|
|
|
+ Log.String("[SD] performance enhance: ");
|
|
|
+ IF status.perfEnhance = {} THEN Log.String("none") END;
|
|
|
+ IF PerformanceCardMaintenance IN status.perfEnhance THEN Log.String("card-initiated maintenance; ") END;
|
|
|
+ IF PerformanceHostMaintenance IN status.perfEnhance THEN Log.String("host-initiated maintenance; ") END;
|
|
|
+ IF PerformanceCache IN status.perfEnhance THEN Log.String("cache; ") END;
|
|
|
+ IF PerformanceQueue IN status.perfEnhance THEN
|
|
|
+ Log.String("command queue;"); Log.Ln;
|
|
|
+ Log.String("[SD] command queue depth: ");
|
|
|
+ Log.Int(status.commandQueueDepth, 0)
|
|
|
+ END; Log.Ln;
|
|
|
+ Log.String("[SD] discard supported: "); Log.Boolean(status.discardSupport); Log.Ln;
|
|
|
+ Log.String("[SD] FULE supported: "); Log.Boolean(status.fuleSupport); Log.Ln
|
|
|
+ END
|
|
|
+ END PrintSdStatus;
|
|
|
+
|
|
|
+ (** Print the CID of a card to the log. *)
|
|
|
+ PROCEDURE PrintCardCid * (cid: Cid);
|
|
|
+ BEGIN
|
|
|
+ IF EnableTrace THEN
|
|
|
+ Log.String("[SD] CID"); Log.Ln;
|
|
|
+ Log.String("[SD] manufacturing date: "); Log.Int(cid.manufacturingDate.month, 0); Log.String("/"); Log.Int(cid.manufacturingDate.year, 0); Log.Ln;
|
|
|
+ Log.String("[SD] product serial number: "); Log.Int(cid.productSerialNumber, 0); Log.Ln;
|
|
|
+ Log.String("[SD] product revision: "); Log.Int(cid.productRevision.n, 0); Log.String("."); Log.Int(cid.productRevision.m, 0); Log.Ln;
|
|
|
+ Log.String("[SD] product name: "); Log.String(cid.productName); Log.Ln;
|
|
|
+ Log.String("[SD] OEM/application id: "); Log.String(cid.oemId); Log.Ln;
|
|
|
+ Log.String("[SD] manufacturer id: "); Log.Int(cid.manufacturerId, 0); Log.Ln
|
|
|
+ END
|
|
|
+ END PrintCardCid;
|
|
|
+
|
|
|
+ (** Print the CSD of a card to the log. *)
|
|
|
+ PROCEDURE PrintCardCsd * (CONST csd: ARRAY OF LONGINT);
|
|
|
+ VAR
|
|
|
+ cap: HUGEINT;
|
|
|
+ val, version, sizeMult: LONGINT;
|
|
|
+ BEGIN
|
|
|
+ IF EnableTrace THEN
|
|
|
+ version := ReadBitfield(csd, CardCsd_CsdStructureOfs, CardCsd_CsdStructureWidth) + 1;
|
|
|
+ Log.String("[SD] "); Log.String("Card CSD:"); Log.Ln;
|
|
|
+ Log.String("[SD] "); Log.String(" Version: "); Log.Int(version, 0); Log.Ln;
|
|
|
+
|
|
|
+ (* Common Fields *)
|
|
|
+ Log.String("[SD] "); Log.String(" File Format: ");
|
|
|
+ val := ReadBitfield(csd, CardCsd_FileFormatOfs, CardCsd_FileFormatWidth);
|
|
|
+ IF ReadBitfield(csd, CardCsd_FileFormatGrp, 1) = 1 THEN
|
|
|
+ Log.String("Unknown Value (");
|
|
|
+ Log.Int(val, 0);
|
|
|
+ Log.String(")")
|
|
|
+ ELSIF val = 0 THEN
|
|
|
+ Log.String("Hard-disk file system with partition table")
|
|
|
+ ELSIF val = 1 THEN
|
|
|
+ Log.String("FAT")
|
|
|
+ ELSIF val = 2 THEN
|
|
|
+ Log.String("Universal File Format")
|
|
|
+ ELSE
|
|
|
+ Log.String("Other")
|
|
|
+ END;
|
|
|
+ Log.Ln;
|
|
|
+
|
|
|
+ Log.String("[SD] "); Log.String(" Temporary Write Protection: "); Log.Boolean(ReadBitfield(csd, CardCsd_TmpWriteProtect, 1) = 1); Log.Ln;
|
|
|
+ Log.String("[SD] "); Log.String(" Permanent Write Protection: "); Log.Boolean(ReadBitfield(csd, CardCsd_PermWriteProtect, 1) = 1); Log.Ln;
|
|
|
+ Log.String("[SD] "); Log.String(" Copy: "); Log.Boolean(ReadBitfield(csd, CardCsd_Copy, 1) = 1); Log.Ln;
|
|
|
+ Log.String("[SD] "); Log.String(" Partial Block Write: "); Log.Boolean(ReadBitfield(csd, CardCsd_WriteBlPartial, 1) = 1); Log.Ln;
|
|
|
+ Log.String("[SD] "); Log.String(" Maximum Write Block Length: ");
|
|
|
+ val := ReadBitfield(csd, CardCsd_WriteBlLenOfs, CardCsd_WriteBlLenWidth);
|
|
|
+ IF (val <= 8) OR (val >= 12) THEN
|
|
|
+ Log.String("Unknown Value (");
|
|
|
+ Log.Int(val, 0);
|
|
|
+ Log.String(")")
|
|
|
+ ELSE
|
|
|
+ Log.Int(LSH(LONGINT(1), val), 0);
|
|
|
+ END;
|
|
|
+ Log.Ln;
|
|
|
+ Log.String("[SD] "); Log.String(" Block Program Time / Block Read Time: ");
|
|
|
+ val := ReadBitfield(csd, CardCsd_R2wFactorOfs, CardCsd_R2wFactorWidth);
|
|
|
+ IF val >= 6 THEN
|
|
|
+ Log.String("Unknown Value (");
|
|
|
+ Log.Int(val, 0);
|
|
|
+ Log.String(")")
|
|
|
+ ELSE
|
|
|
+ Log.Int(LSH(LONGINT(1), val), 0);
|
|
|
+ END;
|
|
|
+ Log.Ln;
|
|
|
+ Log.String("[SD] "); Log.String(" Group Write Protection: "); Log.Boolean(ReadBitfield(csd, CardCsd_WpGrpEnable, 1) = 1); Log.Ln;
|
|
|
+ Log.String("[SD] "); Log.String(" WpGrpSize: "); Log.Int(ReadBitfield(csd, CardCsd_WpGrpSizeOfs, CardCsd_WpGrpSizeWidth) + 1, 0); Log.String(" sectors"); Log.Ln;
|
|
|
+ Log.String("[SD] "); Log.String(" Sector Size: "); Log.Int(ReadBitfield(csd, CardCsd_SectorSizeOfs, CardCsd_SectorSizeWidth) + 1, 0); Log.Ln;
|
|
|
+ Log.String("[SD] "); Log.String(" Erase Block Enable: "); Log.Boolean(ReadBitfield(csd, CardCsd_EraseBlkEn, 1) = 1); Log.Ln;
|
|
|
+
|
|
|
+ IF version = 1 THEN
|
|
|
+ sizeMult := LSH(LONGINT(2), ReadBitfield(csd, CardCsd_CSizeMultOfs1, CardCsd_CSizeMultWidth1));
|
|
|
+ cap := sizeMult * (1 + ReadBitfield(csd, CardCsd_CSizeOfs1, CardCsd_CSizeWidth1)) * 512;
|
|
|
+ ELSE
|
|
|
+ cap := 512 * 1024 * (HUGEINT(ReadBitfield(csd, CardCsd_CSizeOfs2, CardCsd_CSizeWidth2)) + 1);
|
|
|
+ END;
|
|
|
+ Log.String("[SD] "); Log.String(" Card Capacity: "); Log.Int(cap, 0); Log.String(" B"); Log.Ln;
|
|
|
+ Log.String("[SD] "); Log.String(" DSR Implemented: "); Log.Boolean(ReadBitfield(csd, CardCsd_DsrImp, 1) = 1); Log.Ln;
|
|
|
+ Log.String("[SD] "); Log.String(" Misaligned Block Read: "); Log.Boolean(ReadBitfield(csd, CardCsd_ReadBlkMisalign, 1) = 1); Log.Ln;
|
|
|
+ Log.String("[SD] "); Log.String(" Misaligned Block Write: "); Log.Boolean(ReadBitfield(csd, CardCsd_WriteBlkMisalign, 1) = 1); Log.Ln;
|
|
|
+ Log.String("[SD] "); Log.String(" Partial Block Read: "); Log.Boolean(ReadBitfield(csd, CardCsd_ReadBlPartial, 1) = 1); Log.Ln;
|
|
|
+ (*ASSERT(ReadBitfield(csd, CardCsd_ReadBlPartial, 1) = 1);*)
|
|
|
+ Log.String("[SD] "); Log.String(" Maximal Block Read Length: ");
|
|
|
+ val := ReadBitfield(csd, CardCsd_ReadBlLenOfs, CardCsd_ReadBlLenWidth);
|
|
|
+ IF (val <= 8) OR (val >= 12) THEN
|
|
|
+ Log.String("Unknown Value (");
|
|
|
+ Log.Int(val, 0);
|
|
|
+ Log.String(")")
|
|
|
+ ELSE
|
|
|
+ Log.Int(LSH(LONGINT(1), val), 0); Log.String(" B")
|
|
|
+ END;
|
|
|
+ Log.Ln;
|
|
|
+ Log.String("[SD] "); Log.String(" Supported Command Classes: "); Log.Set(SYSTEM.VAL(SET, ReadBitfield(csd, CardCsd_CccOfs, CardCsd_CccWidth))); Log.Ln;
|
|
|
+ Log.String("[SD] "); Log.String(" Transfer Speed: ");
|
|
|
+ val := ReadBitfield(csd, CardCsd_TranSpeedOfs, CardCsd_TranSpeedWidth);
|
|
|
+ CASE val DIV 8 OF
|
|
|
+ 1: Log.String("1.0")
|
|
|
+ |2: Log.String("1.2")
|
|
|
+ |3: Log.String("1.3")
|
|
|
+ |4: Log.String("1.5")
|
|
|
+ |5: Log.String("2.0")
|
|
|
+ |6: Log.String("2.0")
|
|
|
+ |7: Log.String("2.5")
|
|
|
+ |8: Log.String("3.5")
|
|
|
+ |9: Log.String("4.0")
|
|
|
+ |10: Log.String("4.5")
|
|
|
+ |11: Log.String("5.0")
|
|
|
+ |12: Log.String("5.5")
|
|
|
+ |13: Log.String("6.0")
|
|
|
+ |14: Log.String("7.0")
|
|
|
+ |15: Log.String("8.0")
|
|
|
+ ELSE
|
|
|
+ Log.String("Unknown Value (");
|
|
|
+ Log.Int(val, 0);
|
|
|
+ Log.String(")")
|
|
|
+ END;
|
|
|
+ Log.String(" * 1");
|
|
|
+ CASE val MOD 8 OF
|
|
|
+ 0: Log.String("00 k")
|
|
|
+ |1: Log.String(" M")
|
|
|
+ |2: Log.String("0 M")
|
|
|
+ |3: Log.String("00 M")
|
|
|
+ END;
|
|
|
+ Log.String("Bit/s");
|
|
|
+ Log.Ln;
|
|
|
+
|
|
|
+ Log.String("[SD] "); Log.String(" Clock-Dependent Access Time: "); Log.Int(ReadBitfield(csd, CardCsd_NsacOfs, CardCsd_NsacWidth) * 100, 0); Log.String("000 clock cycles"); Log.Ln;
|
|
|
+ Log.String("[SD] "); Log.String(" Asynchronous Access Time: ");
|
|
|
+ val := ReadBitfield(csd, CardCsd_TaacOfs, CardCsd_TaacWidth);
|
|
|
+ CASE val DIV 8 OF
|
|
|
+ 1: Log.String("1.0")
|
|
|
+ |2: Log.String("1.2")
|
|
|
+ |3: Log.String("1.3")
|
|
|
+ |4: Log.String("1.5")
|
|
|
+ |5: Log.String("2.0")
|
|
|
+ |6: Log.String("2.5")
|
|
|
+ |7: Log.String("3.0")
|
|
|
+ |8: Log.String("3.5")
|
|
|
+ |9: Log.String("4.0")
|
|
|
+ |10: Log.String("4.5")
|
|
|
+ |11: Log.String("5.0")
|
|
|
+ |12: Log.String("5.5")
|
|
|
+ |13: Log.String("6.0")
|
|
|
+ |14: Log.String("7.0")
|
|
|
+ |15: Log.String("8.0")
|
|
|
+ ELSE
|
|
|
+ Log.String("Unknown Value (");
|
|
|
+ Log.Int(val, 0);
|
|
|
+ Log.String(")")
|
|
|
+ END;
|
|
|
+ Log.String(" * 1");
|
|
|
+ CASE val MOD 8 OF
|
|
|
+ 0: Log.String(" ns")
|
|
|
+ |1: Log.String("0 ns")
|
|
|
+ |2: Log.String("00 ns")
|
|
|
+ |3: Log.String(" microsecond")
|
|
|
+ |4: Log.String("0 microsecond")
|
|
|
+ |5: Log.String("00 microsecond")
|
|
|
+ |6: Log.String(" ms")
|
|
|
+ |7: Log.String("0 ms")
|
|
|
+ END;
|
|
|
+ Log.Ln
|
|
|
+ END
|
|
|
+ END PrintCardCsd;
|
|
|
+
|
|
|
+ PROCEDURE PrintCardScr * (CONST scr: Scr);
|
|
|
+ VAR i: LONGINT;
|
|
|
+ BEGIN
|
|
|
+ IF EnableTrace THEN
|
|
|
+ Log.String("[SD] Card SCR"); Log.Ln;
|
|
|
+ Log.String("[SD] physical layer version: ");
|
|
|
+ CASE scr.version OF
|
|
|
+ Version1: Log.String("1")
|
|
|
+ |Version1p1: Log.String("1.1")
|
|
|
+ |Version2: Log.String("2")
|
|
|
+ |Version3: Log.String("3")
|
|
|
+ |Version4: Log.String("4")
|
|
|
+ |Version5: Log.String("5")
|
|
|
+ |Version6: Log.String("6")
|
|
|
+ ELSE
|
|
|
+ Log.String("unknown")
|
|
|
+ END;
|
|
|
+ Log.Ln;
|
|
|
+ Log.String("[SD] security support: ");
|
|
|
+ CASE scr.security OF
|
|
|
+ TypeNone: Log.String("none")
|
|
|
+ |TypeSDSC: Log.String("SDSC")
|
|
|
+ |TypeSDHC: Log.String("SDHC")
|
|
|
+ |TypeSDXC: Log.String("SDXC")
|
|
|
+ END;
|
|
|
+ Log.Ln;
|
|
|
+ Log.String("[SD] supported bus widths: "); Log.Set(scr.busWidth); Log.Ln
|
|
|
+ END
|
|
|
+ END PrintCardScr;
|
|
|
+
|
|
|
+ (** Helper to write a size in a human-readable format *)
|
|
|
+ PROCEDURE PrintSize (size: HUGEINT);
|
|
|
+ VAR
|
|
|
+ prefix: ARRAY 8 OF CHAR;
|
|
|
+ i: LONGINT;
|
|
|
+
|
|
|
+ BEGIN
|
|
|
+ IF size < 1024 THEN
|
|
|
+ Log.Int(size, 0);
|
|
|
+ Log.String(" ")
|
|
|
+ ELSE
|
|
|
+ prefix := 'kMGT';
|
|
|
+ i := 0;
|
|
|
+ size := size DIV 1024;
|
|
|
+ WHILE size > 1024 DO
|
|
|
+ size := size DIV 1024;
|
|
|
+ INC(i)
|
|
|
+ END;
|
|
|
+ Log.Int(size, 0);
|
|
|
+ Log.String(" ");
|
|
|
+ Log.Char(prefix[i])
|
|
|
+ END;
|
|
|
+ Log.String("B")
|
|
|
+ END PrintSize;
|
|
|
+
|
|
|
+ (**
|
|
|
+ Helper procedure to read bit fields in a wide register.
|
|
|
+ field: large bitfield
|
|
|
+ ofs: offset of first bit to extract
|
|
|
+ width: number of bits to extract
|
|
|
+ Returns the bits as a LONGINT.
|
|
|
+ *)
|
|
|
+ PROCEDURE ReadBitfield (CONST field: ARRAY OF LONGINT; ofs, width: LONGINT): LONGINT;
|
|
|
+ VAR
|
|
|
+ adr, bits: ADDRESS;
|
|
|
+ BEGIN
|
|
|
+ ASSERT(ofs MOD 8 + width <= 32);
|
|
|
+ adr := ADDRESSOF(field[0]) + ofs DIV 8;
|
|
|
+ bits := SYSTEM.GET8(adr) MOD 100H;
|
|
|
+ IF ofs MOD 8 + width > 8 THEN
|
|
|
+ bits := bits + LSH(ADDRESS(SYSTEM.GET8(adr + 1)) MOD 100H, 8);
|
|
|
+ END;
|
|
|
+ IF ofs MOD 8 + width > 16 THEN
|
|
|
+ bits := bits + LSH(ADDRESS(SYSTEM.GET8(adr + 2)) MOD 100H, 16);
|
|
|
+ END;
|
|
|
+ IF ofs MOD 8 + width > 24 THEN
|
|
|
+ bits := bits + LSH(ADDRESS(SYSTEM.GET8(adr + 3)) MOD 100H, 24)
|
|
|
+ END;
|
|
|
+ RETURN SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, LSH(bits, -(ofs MOD 8))) * {0 .. width - 1})
|
|
|
+ END ReadBitfield;
|
|
|
+
|
|
|
+ PROCEDURE ReadBit (CONST field: ARRAY OF LONGINT; bit: LONGINT): BOOLEAN;
|
|
|
+ BEGIN
|
|
|
+ RETURN SYSTEM.VAL(BOOLEAN, LSH(SYSTEM.GET8(ADDRESSOF(field[0])) + bit DIV 8, -(bit MOD 8)))
|
|
|
+ END ReadBit;
|
|
|
+
|
|
|
+ PROCEDURE ResetStatistics *;
|
|
|
+ BEGIN
|
|
|
+ NbyteRead := 0;
|
|
|
+ Nread := 0;
|
|
|
+ Tread := 0;
|
|
|
+ NbyteWritten := 0;
|
|
|
+ Nwrite := 0;
|
|
|
+ Twrite := 0
|
|
|
+ END ResetStatistics;
|
|
|
+END Sd.
|