648 lines
14 KiB
C
648 lines
14 KiB
C
/*
|
|
* Copyright (c) 2008-2010 Apple Inc. All rights reserved.
|
|
*
|
|
* This document is the property of Apple Inc.
|
|
* It is considered confidential and proprietary.
|
|
*
|
|
* This document may not be reproduced or transmitted in any form,
|
|
* in whole or in part, without the express written permission of
|
|
* Apple Inc.
|
|
*/
|
|
|
|
|
|
|
|
#include "sdhc_registers.h"
|
|
|
|
|
|
// TODO: Endianness
|
|
|
|
#pragma mark -
|
|
#pragma mark Macros
|
|
|
|
#ifndef min
|
|
#define min(a, b) ((a) < (b) ? (a) : (b))
|
|
#endif
|
|
|
|
|
|
|
|
#pragma mark -
|
|
#pragma mark Capabilities
|
|
|
|
enum SDHCCapabilitiesFieldOffset
|
|
{
|
|
kSDHCCapabilityBaseClockRateOffset = 8,
|
|
kSDHCCapabilityMaxBlockLengthOffset = 16,
|
|
kSDHCCapabilityHighSpeedSupportOffset = 21,
|
|
kSDHCCapabilitySuspendResumeSupportOffset = 23
|
|
};
|
|
|
|
enum SDHCCapabilitiesField
|
|
{
|
|
kSDHCCapabilityBaseClockRate = (0x3F << kSDHCCapabilityBaseClockRateOffset),
|
|
kSDHCCapabilityMaxBlockLength = (0x3 << kSDHCCapabilityMaxBlockLengthOffset),
|
|
kSDHCCapabilityHighSpeedSupport = (1 << kSDHCCapabilityHighSpeedSupportOffset),
|
|
kSDHCCapabilitySuspendResumeSupport = (1 << kSDHCCapabilitySuspendResumeSupportOffset),
|
|
};
|
|
|
|
|
|
|
|
UInt16
|
|
sdhc_getMaxBlockLength(const SDHCRegisters_t *sdhc)
|
|
{
|
|
static UInt16 mapBlkLenCapToLen[] = {512, 1024, 2048, 0};
|
|
|
|
UInt8 cap = (sdhc->capabilities & kSDHCCapabilityMaxBlockLength) >> kSDHCCapabilityMaxBlockLengthOffset;
|
|
|
|
return mapBlkLenCapToLen[cap];
|
|
}
|
|
|
|
UInt32
|
|
sdhc_getBaseClockFrequencyHz(const SDHCRegisters_t *sdhc)
|
|
{
|
|
UInt32 rateMHz = (sdhc->capabilities & kSDHCCapabilityBaseClockRate) >> kSDHCCapabilityBaseClockRateOffset;
|
|
return rateMHz * 1000000;
|
|
}
|
|
|
|
bool
|
|
sdhc_isSuspendResumeSupported(const SDHCRegisters_t *sdhc)
|
|
{
|
|
return sdhc->capabilities & kSDHCCapabilitySuspendResumeSupport;
|
|
}
|
|
|
|
bool
|
|
sdhc_isHighSpeedSupported(const SDHCRegisters_t *sdhc)
|
|
{
|
|
return sdhc->capabilities & kSDHCCapabilityHighSpeedSupport;
|
|
}
|
|
|
|
|
|
|
|
#pragma mark -
|
|
#pragma mark Present State
|
|
|
|
enum PresentStateFieldOffsets
|
|
{
|
|
kCmdInhibitOffset = 0,
|
|
kDataInhibitOffset = 1,
|
|
};
|
|
|
|
enum PresentStateField
|
|
{
|
|
kCmdInhibit = (1 << kCmdInhibitOffset),
|
|
kDataInhibit = (1 << kDataInhibitOffset),
|
|
};
|
|
|
|
|
|
bool
|
|
sdhc_isCommandInhibitedOnCmd(const SDHCRegisters_t *sdhc)
|
|
{
|
|
return sdhc->presentState & kCmdInhibit;
|
|
}
|
|
|
|
bool
|
|
sdhc_isCommandInhibitedOnData(const SDHCRegisters_t *sdhc)
|
|
{
|
|
return sdhc->presentState & kDataInhibit;
|
|
}
|
|
|
|
|
|
|
|
#pragma mark -
|
|
#pragma mark Clocks
|
|
|
|
|
|
#define kMaxClockDivisor (256)
|
|
|
|
enum ClkControlFieldOffsets
|
|
{
|
|
kInternalClkEnableOffset = 0,
|
|
kInternalClkStableOffset = 1,
|
|
kSDClkEnableOffset = 2,
|
|
kSDClkFrequencySelectOffset = 8
|
|
};
|
|
|
|
enum ClkControlFields
|
|
{
|
|
kInternalClkEnable = (1 << kInternalClkEnableOffset),
|
|
kInternalClkStable = (1 << kInternalClkStableOffset),
|
|
kSDClkEnable = (1 << kSDClkEnableOffset),
|
|
kSDClkFrequencySelect = (0xFF << kSDClkFrequencySelectOffset)
|
|
};
|
|
|
|
UInt32
|
|
sdhc_setClockDividerRate(SDHCRegisters_t *sdhc, UInt32 targetSDClkRateHz, UInt32 inputClkRateHz)
|
|
{
|
|
UInt16 divisor = 1;
|
|
|
|
while(inputClkRateHz / divisor > targetSDClkRateHz) {
|
|
divisor *= 2;
|
|
}
|
|
|
|
divisor = min(divisor, kMaxClockDivisor);
|
|
|
|
sdhc_setClockDivider(sdhc, divisor);
|
|
|
|
return inputClkRateHz / divisor;
|
|
}
|
|
|
|
void
|
|
sdhc_setClockDivider(SDHCRegisters_t *sdhc, UInt16 divisor)
|
|
{
|
|
divisor >>= 1; // divisor is offset, 0x00 == clk / 1
|
|
divisor <<= kSDClkFrequencySelectOffset; // shift to correct offset
|
|
|
|
sdhc->clockControl &= ~kSDClkFrequencySelect;
|
|
|
|
// Note: must enable internal clock here or SDIO block will lock up
|
|
sdhc->clockControl |= (divisor | kInternalClkEnable);
|
|
}
|
|
|
|
UInt16
|
|
sdhc_getClockDivider(const SDHCRegisters_t *sdhc)
|
|
{
|
|
UInt16 clkControl = sdhc->clockControl & kSDClkFrequencySelect;
|
|
|
|
clkControl >>= kSDClkFrequencySelectOffset; // shift to correct offset
|
|
|
|
// divisor is offset, 0x00 == clk / 1
|
|
clkControl <<= 1;
|
|
if(0 == clkControl) {
|
|
clkControl = 1;
|
|
}
|
|
|
|
return clkControl;
|
|
}
|
|
|
|
void
|
|
sdhc_enableInternalClock(SDHCRegisters_t *sdhc, bool enable)
|
|
{
|
|
UInt16 clkControl = sdhc->clockControl;
|
|
|
|
if(enable) {
|
|
clkControl |= kInternalClkEnable;
|
|
} else {
|
|
clkControl &= ~kInternalClkEnable;
|
|
}
|
|
|
|
sdhc->clockControl = clkControl;
|
|
}
|
|
|
|
bool
|
|
sdhc_isInternalClockStable(const SDHCRegisters_t *sdhc)
|
|
{
|
|
return sdhc->clockControl & kInternalClkStable;
|
|
}
|
|
|
|
void
|
|
sdhc_enableSDClock(SDHCRegisters_t *sdhc, bool enable)
|
|
{
|
|
UInt16 clkControl = sdhc->clockControl;
|
|
|
|
if(enable) {
|
|
clkControl |= kSDClkEnable;
|
|
} else {
|
|
clkControl &= ~kSDClkEnable;
|
|
}
|
|
|
|
sdhc->clockControl = clkControl;
|
|
}
|
|
|
|
bool
|
|
sdhc_isSDClockEnabled(const SDHCRegisters_t *sdhc)
|
|
{
|
|
return sdhc->clockControl & kSDClkEnable;
|
|
}
|
|
|
|
|
|
|
|
#pragma mark -
|
|
#pragma mark Block Size
|
|
|
|
|
|
enum SDHCBlockSizeOffset
|
|
{
|
|
kSDHCTransferSizeOffset = 0,
|
|
kSDHCHostSDMABufferSizeOffset = 12,
|
|
kSDHCTransferSizeHighOffset = 15,
|
|
};
|
|
|
|
enum SDHCBlockSizeMask
|
|
{
|
|
kSDHCTransferSize = (0xFFF << kSDHCTransferSizeOffset),
|
|
kSDHCHostSDMABufferSize = (0x7 << kSDHCTransferSizeOffset),
|
|
kSDHCTransferSizeHigh = (1 << kSDHCTransferSizeHighOffset),
|
|
|
|
};
|
|
|
|
|
|
void
|
|
sdhc_setBlockSize(SDHCRegisters_t *sdhc, UInt16 size)
|
|
{
|
|
UInt16 blockSize = sdhc->blockSize & ~(kSDHCTransferSizeHigh | kSDHCTransferSize);
|
|
|
|
|
|
blockSize |= size & kSDHCTransferSize;
|
|
|
|
// High bit
|
|
blockSize |= (size & (1 << kSDHCHostSDMABufferSizeOffset))
|
|
<< (kSDHCTransferSizeHighOffset - kSDHCHostSDMABufferSizeOffset);
|
|
|
|
sdhc->blockSize = blockSize;
|
|
}
|
|
|
|
|
|
|
|
#pragma mark -
|
|
#pragma mark Commands
|
|
|
|
void
|
|
sdhc_copyCommandResponse(const SDHCRegisters_t *sdhc, struct SDIOCommandResponse *response)
|
|
{
|
|
for(UInt16 i=0; i < 4; ++i) {
|
|
response->response[i] = sdhc->response[i];
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#pragma mark -
|
|
#pragma mark Command State
|
|
|
|
|
|
enum NormalIntFieldOffsets
|
|
{
|
|
kCmdCompleteIntOffset = 0,
|
|
kTransferCompleteIntOffset = 1,
|
|
kBlockGapIntOffset = 2,
|
|
kDMAIntOffset = 3,
|
|
kBufferWriteReadyIntOffset = 4,
|
|
kBufferReadReadyIntOffset = 5,
|
|
kCardInsertionIntOffset = 6,
|
|
kCardRemovalIntOffset = 7,
|
|
kCardIntOffset = 8,
|
|
|
|
kErrorIntOffset = 15
|
|
};
|
|
|
|
enum NormalIntFields
|
|
{
|
|
kCmdCompleteInt = (1 << kCmdCompleteIntOffset),
|
|
kTransferCompleteInt = (1 << kTransferCompleteIntOffset),
|
|
kBlockGapInt = (1 << kBlockGapIntOffset),
|
|
kDMAInt = (1 << kDMAIntOffset),
|
|
kBufferWriteReadyInt = (1 << kBufferWriteReadyIntOffset),
|
|
kBufferReadReadyInt = (1 << kBufferReadReadyIntOffset),
|
|
kCardInsertionInt = (1 << kCardInsertionIntOffset),
|
|
kCardRemovalInt = (1 << kCardRemovalIntOffset),
|
|
kCardInt = (1 << kCardIntOffset),
|
|
|
|
kErrorInt = (1 << kErrorIntOffset),
|
|
};
|
|
|
|
enum ErrorIntFieldOffsets
|
|
{
|
|
kCmdTimeoutErrorIntOffset = 0,
|
|
kCmdCRCErrorIntOffset = 1,
|
|
kCmdEndBitErrorIntOffset = 2,
|
|
kCmdIndexErrorIntOffset = 3,
|
|
kDataTimeoutErrorIntOffset = 4,
|
|
kDataCRCErrorIntOffset = 5,
|
|
kDataEndBitErrorIntOffset = 6,
|
|
kCurrentLimitErrorIntOffset = 7,
|
|
kAutoCmd12ErrorIntOffset = 8,
|
|
kADMAErrorIntOffset = 9
|
|
};
|
|
|
|
enum ErrorIntFields
|
|
{
|
|
kCmdTimeoutErrorInt = (1 << kCmdTimeoutErrorIntOffset),
|
|
kCmdCRCErrorInt = (1 << kCmdCRCErrorIntOffset),
|
|
kCmdEndBitErrorInt = (1 << kCmdEndBitErrorIntOffset),
|
|
kCmdIndexErrorInt = (1 << kCmdIndexErrorIntOffset),
|
|
kDataTimeoutErrorInt = (1 << kDataTimeoutErrorIntOffset),
|
|
kDataCRCErrorInt = (1 << kDataCRCErrorIntOffset),
|
|
kDataEndBitErrorInt = (1 << kDataEndBitErrorIntOffset),
|
|
kCurrentLimitErrorInt = (1 << kCurrentLimitErrorIntOffset),
|
|
kAutoCmd12ErrorInt = (1 << kAutoCmd12ErrorIntOffset),
|
|
kADMAErrorInt = (1 << kADMAErrorIntOffset)
|
|
};
|
|
|
|
|
|
void
|
|
sdhc_enableCommandStatus(SDHCRegisters_t *sdhc, bool enable)
|
|
{
|
|
if(enable) {
|
|
sdhc->normalInterruptStatusEnable |= kCmdCompleteInt;
|
|
sdhc->errorInterruptStatusEnable |= (kCmdIndexErrorInt | kCmdEndBitErrorInt | kCmdCRCErrorInt | kCmdTimeoutErrorInt);
|
|
} else {
|
|
sdhc->normalInterruptStatusEnable &= ~kCmdCompleteInt;
|
|
sdhc->errorInterruptStatusEnable &= ~(kCmdIndexErrorInt | kCmdEndBitErrorInt | kCmdCRCErrorInt | kCmdTimeoutErrorInt);
|
|
}
|
|
}
|
|
|
|
bool
|
|
sdhc_isCommandComplete(const SDHCRegisters_t *sdhc)
|
|
{
|
|
return (sdhc->normalInterruptStatus & kCmdCompleteInt)
|
|
|| (sdhc->errorInterruptStatus
|
|
& (kCmdIndexErrorInt | kCmdEndBitErrorInt | kCmdCRCErrorInt | kCmdTimeoutErrorInt));
|
|
}
|
|
|
|
SDIOReturn_t
|
|
sdhc_getCommandStatus(const SDHCRegisters_t *sdhc)
|
|
{
|
|
const UInt16 state = kCmdTimeoutErrorInt | kCmdCRCErrorInt;
|
|
const UInt16 errorStatus = sdhc->errorInterruptStatus;
|
|
|
|
if((errorStatus & state) == state) {
|
|
return kSDIOReturnCmdLineConflict;
|
|
} else if(errorStatus & kCmdTimeoutErrorInt) {
|
|
return kSDIOReturnCmdTimeout;
|
|
} else if(errorStatus & kCmdCRCErrorInt) {
|
|
return kSDIOReturnCmdCRCError;
|
|
} else if(errorStatus & kCmdEndBitErrorInt) {
|
|
return kSDIOReturnCmdEndBitError;
|
|
} else if(errorStatus & kCmdIndexErrorInt) {
|
|
return kSDIOReturnCmdIndexError;
|
|
} else {
|
|
return kSDIOReturnSuccess;
|
|
}
|
|
}
|
|
|
|
void
|
|
sdhc_clearCommandStatus(SDHCRegisters_t *sdhc)
|
|
{
|
|
sdhc->normalInterruptStatus = kCmdCompleteInt;
|
|
sdhc->errorInterruptStatus = (kCmdIndexErrorInt | kCmdEndBitErrorInt | kCmdCRCErrorInt | kCmdTimeoutErrorInt);
|
|
}
|
|
|
|
|
|
|
|
#pragma mark -
|
|
#pragma mark Transfer State
|
|
|
|
void
|
|
sdhc_enableTransferStatus(SDHCRegisters_t *sdhc, bool enable)
|
|
{
|
|
if(enable) {
|
|
sdhc->normalInterruptStatusEnable |= kTransferCompleteInt;
|
|
sdhc->errorInterruptStatusEnable |= (kDataTimeoutErrorInt | kDataCRCErrorInt | kDataEndBitErrorInt);
|
|
} else {
|
|
sdhc->normalInterruptStatusEnable &= ~kTransferCompleteInt;
|
|
sdhc->errorInterruptStatusEnable &= ~(kDataTimeoutErrorInt | kDataCRCErrorInt | kDataEndBitErrorInt);
|
|
}
|
|
}
|
|
|
|
bool
|
|
sdhc_isTransferComplete(const SDHCRegisters_t *sdhc)
|
|
{
|
|
return (sdhc->normalInterruptStatus & kTransferCompleteInt)
|
|
|| (sdhc->errorInterruptStatus
|
|
& (kDataTimeoutErrorInt | kDataCRCErrorInt | kDataEndBitErrorInt));
|
|
}
|
|
|
|
SDIOReturn_t
|
|
sdhc_getTransferStatus(const SDHCRegisters_t *sdhc)
|
|
{
|
|
const UInt16 errorStatus = sdhc->errorInterruptStatus;
|
|
|
|
if((errorStatus & kDataTimeoutErrorInt)
|
|
&& !(sdhc->normalInterruptStatus & kTransferCompleteInt)) {
|
|
return kSDIOReturnDataTimeout;
|
|
} else if(errorStatus & kDataCRCErrorInt) {
|
|
return kSDIOReturnDataCRCError;
|
|
} else if(errorStatus & kDataEndBitErrorInt) {
|
|
return kSDIOReturnDataEndBitError;
|
|
} else {
|
|
return kSDIOReturnSuccess;
|
|
}
|
|
}
|
|
|
|
void
|
|
sdhc_clearTransferStatus(SDHCRegisters_t *sdhc)
|
|
{
|
|
sdhc->normalInterruptStatus = kTransferCompleteInt;
|
|
sdhc->errorInterruptStatus = (kDataTimeoutErrorInt | kDataCRCErrorInt | kDataEndBitErrorInt);
|
|
}
|
|
|
|
|
|
|
|
#pragma mark -
|
|
#pragma mark PIO
|
|
|
|
bool
|
|
sdhc_isBufferReadReady(const SDHCRegisters_t *sdhc)
|
|
{
|
|
return sdhc->normalInterruptStatus & kBufferReadReadyInt;
|
|
}
|
|
|
|
void
|
|
sdhc_enableBufferReadReady(SDHCRegisters_t *sdhc, bool enable)
|
|
{
|
|
if(enable) {
|
|
sdhc->normalInterruptStatusEnable |= kBufferReadReadyInt;
|
|
} else {
|
|
sdhc->normalInterruptStatusEnable &= ~kBufferReadReadyInt;
|
|
}
|
|
}
|
|
|
|
void
|
|
sdhc_clearBufferReadReady(SDHCRegisters_t *sdhc)
|
|
{
|
|
sdhc->normalInterruptStatus = kBufferReadReadyInt;
|
|
}
|
|
|
|
bool
|
|
sdhc_isBufferWriteReady(const SDHCRegisters_t *sdhc)
|
|
{
|
|
return sdhc->normalInterruptStatus & kBufferWriteReadyInt;
|
|
}
|
|
|
|
void
|
|
sdhc_enableBufferWriteReady(SDHCRegisters_t *sdhc, bool enable)
|
|
{
|
|
if(enable) {
|
|
sdhc->normalInterruptStatusEnable |= kBufferWriteReadyInt;
|
|
} else {
|
|
sdhc->normalInterruptStatusEnable &= ~kBufferWriteReadyInt;
|
|
}
|
|
}
|
|
|
|
void
|
|
sdhc_clearBufferWriteReady(SDHCRegisters_t *sdhc)
|
|
{
|
|
sdhc->normalInterruptStatus = kBufferWriteReadyInt;
|
|
}
|
|
|
|
|
|
|
|
#pragma mark -
|
|
#pragma mark Data Timeout
|
|
|
|
|
|
enum SDHCTimeoutControlOffset
|
|
{
|
|
kSDHCDataTimeoutCounterOffset = 0,
|
|
};
|
|
|
|
enum SDHCTimeoutControl
|
|
{
|
|
kSDHCDataTimeoutCounter = (0xF << kSDHCDataTimeoutCounterOffset),
|
|
};
|
|
|
|
|
|
void
|
|
sdhc_setDataTimeoutCounter(SDHCRegisters_t *sdhc)
|
|
{
|
|
UInt16 divisor = sdhc->clockControl & kSDClkFrequencySelect;
|
|
|
|
// BCOM algorithm
|
|
UInt8 timeout = 7;
|
|
while(timeout && (divisor & 1) == 0) {
|
|
timeout--;
|
|
divisor >>= 1;
|
|
}
|
|
|
|
bool on = sdhc->errorInterruptStatusEnable & kDataTimeoutErrorInt;
|
|
sdhc->errorInterruptStatusEnable &= ~kDataTimeoutErrorInt;
|
|
|
|
sdhc->timeoutControl |= (divisor & kSDHCDataTimeoutCounter);
|
|
|
|
if(on) {
|
|
sdhc->errorInterruptStatusEnable |= kDataTimeoutErrorInt;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#pragma mark -
|
|
#pragma mark Reset
|
|
|
|
|
|
void
|
|
sdhc_reset(SDHCRegisters_t *sdhc, SDHCResetFlags_t flags)
|
|
{
|
|
sdhc->softwareReset |= (flags & (kSDHCResetDataLine | kSDHCResetCmdLine | kSDHCResetAll));
|
|
}
|
|
|
|
SDHCResetFlags_t
|
|
sdhc_isResetting(const SDHCRegisters_t *sdhc)
|
|
{
|
|
return sdhc->softwareReset & (kSDHCResetDataLine | kSDHCResetCmdLine | kSDHCResetAll);
|
|
}
|
|
|
|
|
|
|
|
#pragma mark -
|
|
#pragma mark Interrupts
|
|
|
|
|
|
bool
|
|
sdhc_isSignalingInterrupt(const SDHCRegisters_t *sdhc)
|
|
{
|
|
return (sdhc->normalInterruptStatus & sdhc->normalInterruptSignalEnable)
|
|
|| (sdhc->errorInterruptStatus & sdhc->errorInterruptSignalEnable);
|
|
}
|
|
|
|
|
|
void
|
|
sdhc_clearInterruptSignals(SDHCRegisters_t *sdhc)
|
|
{
|
|
UInt16 clear;
|
|
|
|
sdhc->forceEventAutoCmd12ErrorStatus = 0;
|
|
sdhc->forceEventErrorInterruptStatus = 0;
|
|
|
|
clear = sdhc->errorInterruptSignalEnable & sdhc->errorInterruptStatus;
|
|
|
|
if(clear & kAutoCmd12ErrorInt) {
|
|
UInt16 cmd12Clear = sdhc->autoCmd12ErrorStatus;
|
|
sdhc->autoCmd12ErrorStatus = cmd12Clear;
|
|
}
|
|
|
|
sdhc->errorInterruptStatus = clear;
|
|
|
|
clear = sdhc->normalInterruptSignalEnable & sdhc->normalInterruptStatus;
|
|
sdhc->normalInterruptStatus = clear;
|
|
}
|
|
|
|
|
|
|
|
#pragma mark -
|
|
#pragma mark Card Interrupt
|
|
|
|
|
|
bool
|
|
sdhc_isCardInterrupt(const SDHCRegisters_t *sdhc)
|
|
{
|
|
return sdhc->normalInterruptStatus & kCardInt;
|
|
}
|
|
|
|
void
|
|
sdhc_enableCardInterrupt(SDHCRegisters_t *sdhc, bool enable)
|
|
{
|
|
if(enable) {
|
|
sdhc->normalInterruptStatusEnable |= kCardInt;
|
|
sdhc->normalInterruptSignalEnable |= kCardInt;
|
|
} else {
|
|
sdhc->normalInterruptStatusEnable &= ~kCardInt;
|
|
sdhc->normalInterruptSignalEnable &= ~kCardInt;
|
|
}
|
|
}
|
|
|
|
bool
|
|
sdhc_isCardInterruptEnabled(const SDHCRegisters_t *sdhc)
|
|
{
|
|
return sdhc->normalInterruptStatusEnable & kCardInt
|
|
&& sdhc->normalInterruptSignalEnable & kCardInt;
|
|
}
|
|
|
|
|
|
|
|
#pragma mark -
|
|
#pragma mark Host Control
|
|
|
|
|
|
enum HostControlFieldOffsets
|
|
{
|
|
kDataTransferWidthOffset = 1,
|
|
kHighSpeedEnableOffset = 2,
|
|
};
|
|
|
|
enum HostControlFields
|
|
{
|
|
kDataTransferWidth = (1 << kDataTransferWidthOffset), // 1 == 4 bit, 0 == 1 bit
|
|
kHighSpeedEnable = (1 << kHighSpeedEnableOffset),
|
|
};
|
|
|
|
|
|
void
|
|
sdhc_setHighSpeedMode(SDHCRegisters_t *sdhc, bool enabled)
|
|
{
|
|
if(enabled) {
|
|
sdhc->hostControl |= kHighSpeedEnable;
|
|
} else {
|
|
sdhc->hostControl &= ~kHighSpeedEnable;
|
|
}
|
|
}
|
|
|
|
void
|
|
sdhc_setDataTransferWidth(SDHCRegisters_t *sdhc, UInt8 width)
|
|
{
|
|
bool cardIntOn = sdhc_isCardInterruptEnabled(sdhc);
|
|
|
|
sdhc_enableCardInterrupt(sdhc, false);
|
|
|
|
if(width == 4) {
|
|
sdhc->hostControl |= kDataTransferWidth;
|
|
} else {
|
|
sdhc->hostControl &= ~kDataTransferWidth;
|
|
}
|
|
|
|
sdhc_enableCardInterrupt(sdhc, cardIntOn);
|
|
}
|
|
|
|
|
|
|
|
|